Merge remote-tracking branch 'origin/mc1.18/0.5.1' into mc1.19/0.5.1

This commit is contained in:
simibubi 2023-05-12 22:59:18 +02:00
commit 500019465a
219 changed files with 6797 additions and 1078 deletions

23
.github/workflows/gametest.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: gametest
on: [ pull_request, push, workflow_dispatch ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@v3
- name: setup Java
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
cache: gradle
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: run gametests
run: ./gradlew prepareRunGameTestServer runGameTestServer --no-daemon

1
.gitignore vendored
View file

@ -43,3 +43,4 @@ local.properties
.buildpath
.DS_Store
/libs/

View file

@ -90,6 +90,18 @@ minecraft {
}
}
}
gameTestServer {
workingDirectory project.file('run/gametest')
arg '-mixin.config=create.mixins.json'
property 'forge.logging.console.level', 'info'
mods {
create {
source sourceSets.main
}
}
setForceExit false
}
}
}
@ -135,6 +147,17 @@ repositories {
includeGroup "maven.modrinth"
}
}
flatDir {
dirs 'libs'
}
maven {
// Location of maven for CC: Tweaked
name = "squiddev"
url = "https://squiddev.cc/maven/"
content {
includeGroup "org.squiddev"
}
}
}
dependencies {
@ -162,6 +185,11 @@ dependencies {
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}:api")
runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}")
if (cc_tweaked_enable.toBoolean()) {
compileOnly fg.deobf("org.squiddev:cc-tweaked-${cc_tweaked_minecraft_version}:${cc_tweaked_version}:api")
runtimeOnly fg.deobf("org.squiddev:cc-tweaked-${cc_tweaked_minecraft_version}:${cc_tweaked_version}")
}
// implementation fg.deobf("curse.maven:druidcraft-340991:3101903")
// implementation fg.deobf("com.ferreusveritas.dynamictrees:DynamicTrees-1.16.5:0.10.0-Beta25")
// runtimeOnly fg.deobf("vazkii.arl:AutoRegLib:1.4-35.69")
@ -169,6 +197,7 @@ dependencies {
// runtimeOnly fg.deobf("slimeknights.mantle:Mantle:1.16.5-1.6.115")
// runtimeOnly fg.deobf("slimeknights.tconstruct:TConstruct:1.16.5-3.1.1.252")
// runtimeOnly fg.deobf("maven.modrinth:rubidium:0.5.3")
// implementation fg.deobf("com.railwayteam.railways:railways-1.18.2-1.1.1:all") { transitive = false }
// https://discord.com/channels/313125603924639766/725850371834118214/910619168821354497
// Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings
@ -178,6 +207,12 @@ dependencies {
}
}
sourceSets.main.java {
if (!cc_tweaked_enable.toBoolean()) {
exclude 'com/simibubi/create/compat/computercraft/implementation/**'
}
}
sourceSets.main.resources {
srcDir 'src/generated/resources'
exclude '.cache/'

View file

@ -27,6 +27,10 @@ jei_version = 11.2.0.254
curios_minecraft_version = 1.19.2
curios_version = 5.1.1.0
cc_tweaked_enable = true
cc_tweaked_minecraft_version = 1.18.2
cc_tweaked_version = 1.100.10
# curseforge information
projectId = 328085
curse_type = beta

View file

@ -1192,6 +1192,8 @@
"create.schematicAndQuill.convert": "Save and Upload Immediately",
"create.schematicAndQuill.fallbackName": "My Schematic",
"create.schematicAndQuill.saved": "Saved as %1$s",
"create.schematicAndQuill.failed": "Failed to save schematic, check logs for details",
"create.schematicAndQuill.instant_failed": "Schematic instant-upload failed, check logs for details",
"create.schematic.invalid": "[!] Invalid Item - Use the Schematic Table instead",
"create.schematic.error": "Schematic failed to Load - Check Game Logs",
@ -1811,6 +1813,7 @@
"create.display_source.redstone_power.progress_bar": "Progress Bar",
"create.display_source.boiler.not_enough_space": "Not enough space ",
"create.display_source.boiler.for_boiler_status": "for Boiler Status",
"create.display_source.computer_display_source": "From Computer",
"create.display_target.line": "Line %1$s",
"create.display_target.page": "Page %1$s",
@ -1833,6 +1836,8 @@
"create.super_glue.not_enough": "Not enough glue in inventory",
"create.super_glue.success": "Applying Glue...",
"create.gui.attached_computer.controlled": "This device is being controlled by a computer",
"create.gui.attached_computer.hint": "To use device manually, disconnect all computers and modems",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay",
"create.gui.config.overlay3": "Click or drag with your mouse",
@ -1856,6 +1861,12 @@
"enchantment.create.capacity.desc": "Increases Backtank air capacity.",
"enchantment.create.potato_recovery.desc": "Potato Cannon projectiles have a chance to be reused.",
"create.bogey.style.updated_style": "Updated style",
"create.bogey.style.updated_style_and_size": "Updated style and size",
"create.bogey.style.no_other_sizes": "No other sizes",
"create.bogey.style.invalid": "Unnamed style",
"create.bogey.style.standard": "Standard",
"_": "->------------------------] Subtitles [------------------------<-",
@ -2814,7 +2825,7 @@
"create.ponder.mechanical_roller_fill.text_5": "As opposed to 'clear & pave', neither of these modes will cause the rollers to break existing blocks",
"create.ponder.mechanical_roller_pave.header": "Clearing and Paving with the Roller",
"create.ponder.mechanical_roller_pave.text_1": "Mechanical rollers help to clean up long tracks or paths conveniently",
"create.ponder.mechanical_roller_pave.text_1": "Mechanical rollers help to clean up terrain around tracks or paths",
"create.ponder.mechanical_roller_pave.text_2": "In its default mode, without a material set, it will simply clear blocks like a Drill",
"create.ponder.mechanical_roller_pave.text_3": "While disassembled, a suitable paving material can be specified",
"create.ponder.mechanical_roller_pave.text_4": "Materials can be supplied via chests or barrels attached to the structure",

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"create:track"
]
}

View file

@ -18,10 +18,8 @@
"create:andesite_belt_funnel",
"create:brass_funnel",
"create:brass_belt_funnel",
"create:creative_crate",
"create:redstone_link",
"create:analog_lever",
"create:placard",
"create:pulse_repeater",
"create:pulse_extender",
"create:clipboard",

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"create:track"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"create:copper_diving_boots",
"create:netherite_diving_boots"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"create:copper_backtank",
"create:netherite_backtank"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"create:copper_diving_helmet",
"create:netherite_diving_helmet"
]
}

View file

@ -195,6 +195,7 @@ import com.simibubi.create.content.logistics.block.vault.ItemVaultBlockEntity;
import com.simibubi.create.content.logistics.item.LecternControllerBlockEntity;
import com.simibubi.create.content.logistics.item.LecternControllerRenderer;
import com.simibubi.create.content.logistics.trains.BogeyBlockEntityRenderer;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlockEntity;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayRenderer;
import com.simibubi.create.content.logistics.trains.management.edgePoint.observer.TrackObserverBlockEntity;
@ -831,8 +832,8 @@ public class AllBlockEntityTypes {
public static final BlockEntityEntry<TrackBlockEntity> TRACK = REGISTRATE
.blockEntity("track", TrackBlockEntity::new)
.instance(() -> TrackInstance::new)
.validBlocksDeferred(TrackMaterial::allBlocks)
.renderer(() -> TrackRenderer::new)
.validBlocks(AllBlocks.TRACK)
.register();
public static final BlockEntityEntry<FakeTrackBlockEntity> FAKE_TRACK = REGISTRATE
@ -859,10 +860,10 @@ public class AllBlockEntityTypes {
AllBlocks.BRASS_DOOR, AllBlocks.COPPER_DOOR)
.register();
public static final BlockEntityEntry<CopycatBlockEntity> COPYCAT = REGISTRATE
.blockEntity("copycat", CopycatBlockEntity::new)
.validBlocks(AllBlocks.COPYCAT_PANEL, AllBlocks.COPYCAT_STEP)
.register();
public static final BlockEntityEntry<CopycatBlockEntity> COPYCAT =
REGISTRATE.blockEntity("copycat", CopycatBlockEntity::new)
.validBlocks(AllBlocks.COPYCAT_PANEL, AllBlocks.COPYCAT_STEP)
.register();
public static final BlockEntityEntry<FlapDisplayBlockEntity> FLAP_DISPLAY = REGISTRATE
.blockEntity("flap_display", FlapDisplayBlockEntity::new)

View file

@ -230,6 +230,8 @@ import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultCTBehaviour;
import com.simibubi.create.content.logistics.block.vault.ItemVaultItem;
import com.simibubi.create.content.logistics.item.LecternControllerBlock;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem;
@ -718,7 +720,7 @@ public class AllBlocks {
.onRegister(movementBehaviour(new BlazeBurnerMovementBehaviour()))
.onRegister(interactionBehaviour(new BlazeBurnerInteractionBehaviour()))
.item(BlazeBurnerBlockItem::withBlaze)
.model(AssetLookup.<BlazeBurnerBlockItem>customBlockItemModel("blaze_burner", "block_with_blaze"))
.model(AssetLookup.customBlockItemModel("blaze_burner", "block_with_blaze"))
.build()
.register();
@ -938,7 +940,7 @@ public class AllBlocks {
.onRegister(assignDataBehaviour(new BoilerDisplaySource(), "boiler_status"))
.addLayer(() -> RenderType::cutoutMipped)
.item(FluidTankItem::new)
.model(AssetLookup.<FluidTankItem>customBlockItemModel("_", "block_single_window"))
.model(AssetLookup.customBlockItemModel("_", "block_single_window"))
.build()
.register();
@ -1541,7 +1543,7 @@ public class AllBlocks {
.transform(customItemModel())
.register();
public static final BlockEntry<TrackBlock> TRACK = REGISTRATE.block("track", TrackBlock::new)
public static final BlockEntry<TrackBlock> TRACK = REGISTRATE.block("track", TrackMaterial.ANDESITE::createBlock)
.initialProperties(Material.STONE)
.properties(p -> p.color(MaterialColor.METAL)
.strength(0.8F)
@ -1552,6 +1554,8 @@ public class AllBlocks {
.onRegister(CreateRegistrate.blockModel(() -> TrackModel::new))
.blockstate(new TrackBlockStateGenerator()::generate)
.tag(AllBlockTags.RELOCATION_NOT_SUPPORTED.tag)
.tag(AllBlockTags.TRACKS.tag)
.tag(AllBlockTags.GIRDABLE_TRACKS.tag)
.lang("Train Track")
.item(TrackBlockItem::new)
.model((c, p) -> p.generated(c, Create.asResource("item/" + c.getName())))
@ -1620,13 +1624,13 @@ public class AllBlocks {
.register();
public static final BlockEntry<StandardBogeyBlock> SMALL_BOGEY =
REGISTRATE.block("small_bogey", p -> new StandardBogeyBlock(p, false))
REGISTRATE.block("small_bogey", p -> new StandardBogeyBlock(p, BogeySizes.SMALL))
.properties(p -> p.color(MaterialColor.PODZOL))
.transform(BuilderTransformers.bogey())
.register();
public static final BlockEntry<StandardBogeyBlock> LARGE_BOGEY =
REGISTRATE.block("large_bogey", p -> new StandardBogeyBlock(p, true))
REGISTRATE.block("large_bogey", p -> new StandardBogeyBlock(p, BogeySizes.LARGE))
.properties(p -> p.color(MaterialColor.PODZOL))
.transform(BuilderTransformers.bogey())
.register();
@ -1765,7 +1769,6 @@ public class AllBlocks {
REGISTRATE.block("creative_crate", CreativeCrateBlock::new)
.transform(BuilderTransformers.crate("creative"))
.properties(p -> p.color(MaterialColor.COLOR_PURPLE))
.tag(AllBlockTags.SAFE_NBT.tag)
.register();
public static final BlockEntry<DisplayLinkBlock> DISPLAY_LINK =
@ -1861,7 +1864,6 @@ public class AllBlocks {
public static final BlockEntry<PlacardBlock> PLACARD = REGISTRATE.block("placard", PlacardBlock::new)
.initialProperties(SharedProperties::copperMetal)
.transform(pickaxeOnly())
.tag(AllBlockTags.SAFE_NBT.tag)
.blockstate((c, p) -> p.horizontalFaceBlock(c.get(), AssetLookup.standardModel(c, p)))
.simpleItem()
.register();

View file

@ -0,0 +1,122 @@
package com.simibubi.create;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableMap;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.content.logistics.trains.BogeyRenderer.CommonRenderer;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import com.simibubi.create.content.logistics.trains.StandardBogeyRenderer.CommonStandardBogeyRenderer;
import com.simibubi.create.content.logistics.trains.StandardBogeyRenderer.LargeStandardBogeyRenderer;
import com.simibubi.create.content.logistics.trains.StandardBogeyRenderer.SmallStandardBogeyRenderer;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
import com.tterrag.registrate.util.entry.BlockEntry;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
public class AllBogeyStyles {
public static final Map<ResourceLocation, BogeyStyle> BOGEY_STYLES = new HashMap<>();
public static final Map<ResourceLocation, Map<ResourceLocation, BogeyStyle>> CYCLE_GROUPS = new HashMap<>();
private static final Map<ResourceLocation, BogeyStyle> EMPTY_GROUP = ImmutableMap.of();
public static Map<ResourceLocation, BogeyStyle> getCycleGroup(ResourceLocation cycleGroup) {
return CYCLE_GROUPS.getOrDefault(cycleGroup, EMPTY_GROUP);
}
public static final String STANDARD_CYCLE_GROUP = "standard";
public static final BogeyStyle STANDARD =
create("standard", STANDARD_CYCLE_GROUP).commonRenderer(CommonStandardBogeyRenderer::new)
.displayName(Components.translatable("create.bogey.style.standard"))
.size(BogeySizes.SMALL, SmallStandardBogeyRenderer::new, AllBlocks.SMALL_BOGEY)
.size(BogeySizes.LARGE, LargeStandardBogeyRenderer::new, AllBlocks.LARGE_BOGEY)
.build();
private static BogeyStyleBuilder create(String name, String cycleGroup) {
return create(Create.asResource(name), Create.asResource(cycleGroup));
}
public static BogeyStyleBuilder create(ResourceLocation name, ResourceLocation cycleGroup) {
return new BogeyStyleBuilder(name, cycleGroup);
}
public static void register() {}
public static class BogeyStyleBuilder {
protected final Map<BogeySizes.BogeySize, BogeyStyle.SizeData> sizes = new HashMap<>();
protected final ResourceLocation name;
protected final ResourceLocation cycleGroup;
protected Component displayName = Lang.translateDirect("bogey.style.invalid");
protected ResourceLocation soundType = AllSoundEvents.TRAIN2.getId();
protected CompoundTag defaultData = new CompoundTag();
protected ParticleOptions contactParticle = ParticleTypes.CRIT;
protected ParticleOptions smokeParticle = ParticleTypes.POOF;
protected Optional<Supplier<? extends CommonRenderer>> commonRenderer = Optional.empty();
public BogeyStyleBuilder(ResourceLocation name, ResourceLocation cycleGroup) {
this.name = name;
this.cycleGroup = cycleGroup;
}
public BogeyStyleBuilder displayName(Component displayName) {
this.displayName = displayName;
return this;
}
public BogeyStyleBuilder soundType(ResourceLocation soundType) {
this.soundType = soundType;
return this;
}
public BogeyStyleBuilder defaultData(CompoundTag defaultData) {
this.defaultData = defaultData;
return this;
}
public BogeyStyleBuilder size(BogeySizes.BogeySize size, Supplier<? extends BogeyRenderer> renderer,
BlockEntry<? extends AbstractBogeyBlock<?>> blockEntry) {
this.size(size, renderer, blockEntry.getId());
return this;
}
public BogeyStyleBuilder size(BogeySizes.BogeySize size, Supplier<? extends BogeyRenderer> renderer,
ResourceLocation location) {
this.sizes.put(size, new BogeyStyle.SizeData(location, renderer, renderer.get()));
return this;
}
public BogeyStyleBuilder contactParticle(ParticleOptions contactParticle) {
this.contactParticle = contactParticle;
return this;
}
public BogeyStyleBuilder smokeParticle(ParticleOptions smokeParticle) {
this.smokeParticle = smokeParticle;
return this;
}
public BogeyStyleBuilder commonRenderer(Supplier<? extends CommonRenderer> commonRenderer) {
this.commonRenderer = Optional.of(commonRenderer);
return this;
}
public BogeyStyle build() {
BogeyStyle entry =
new BogeyStyle(name, cycleGroup, displayName, soundType, contactParticle, smokeParticle, defaultData, sizes, commonRenderer);
BOGEY_STYLES.put(name, entry);
CYCLE_GROUPS.computeIfAbsent(cycleGroup, l -> new HashMap<>()).put(name, entry);
return entry;
}
}
}

View file

@ -228,14 +228,16 @@ public class AllItems {
// wrapped by COPPER_BACKTANK for block placement uses.
// must be registered as of 1.18.2
public static final ItemEntry<BacktankBlockItem> COPPER_BACKTANK_PLACEABLE = REGISTRATE
.item("copper_backtank_placeable", p -> new BacktankBlockItem(AllBlocks.COPPER_BACKTANK.get(), AllItems.COPPER_BACKTANK::get, p))
.item("copper_backtank_placeable",
p -> new BacktankBlockItem(AllBlocks.COPPER_BACKTANK.get(), AllItems.COPPER_BACKTANK::get, p))
.model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier")))
.register();
// wrapped by NETHERITE_BACKTANK for block placement uses.
// must be registered as of 1.18.2
public static final ItemEntry<BacktankBlockItem> NETHERITE_BACKTANK_PLACEABLE = REGISTRATE
.item("netherite_backtank_placeable", p -> new BacktankBlockItem(AllBlocks.NETHERITE_BACKTANK.get(), AllItems.NETHERITE_BACKTANK::get, p))
.item("netherite_backtank_placeable",
p -> new BacktankBlockItem(AllBlocks.NETHERITE_BACKTANK.get(), AllItems.NETHERITE_BACKTANK::get, p))
.model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier")))
.register();
@ -248,6 +250,7 @@ public class AllItems {
COPPER_BACKTANK_PLACEABLE))
.model(AssetLookup.customGenericItemModel("_", "item"))
.tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag)
.tag(forgeItemTag("armors/chestplates"))
.register(),
NETHERITE_BACKTANK = REGISTRATE
@ -257,6 +260,7 @@ public class AllItems {
.model(AssetLookup.customGenericItemModel("_", "item"))
.properties(p -> p.fireResistant())
.tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag)
.tag(forgeItemTag("armors/chestplates"))
.register();
public static final ItemEntry<? extends DivingHelmetItem>
@ -265,12 +269,14 @@ public class AllItems {
REGISTRATE
.item("copper_diving_helmet",
p -> new DivingHelmetItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving")))
.tag(forgeItemTag("armors/helmets"))
.register(),
NETHERITE_DIVING_HELMET = REGISTRATE
.item("netherite_diving_helmet",
p -> new DivingHelmetItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving")))
.properties(p -> p.fireResistant())
.tag(forgeItemTag("armors/helmets"))
.register();
public static final ItemEntry<? extends DivingBootsItem>
@ -279,12 +285,14 @@ public class AllItems {
REGISTRATE
.item("copper_diving_boots",
p -> new DivingBootsItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving")))
.tag(forgeItemTag("armors/boots"))
.register(),
NETHERITE_DIVING_BOOTS = REGISTRATE
.item("netherite_diving_boots",
p -> new DivingBootsItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving")))
.properties(p -> p.fireResistant())
.tag(forgeItemTag("armors/boots"))
.register();
public static final ItemEntry<SandPaperItem> SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new)

View file

@ -14,6 +14,7 @@ import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
@ -80,6 +81,8 @@ public class AllTags {
SAFE_NBT,
SEATS,
TOOLBOXES,
TRACKS,
GIRDABLE_TRACKS,
TREE_ATTACHMENTS,
VALVE_HANDLES,
WINDMILL_SAILS,
@ -131,6 +134,10 @@ public class AllTags {
.is(tag);
}
public boolean matches(ItemStack stack) {
return stack != null && stack.getItem() instanceof BlockItem blockItem && matches(blockItem.getBlock());
}
public boolean matches(BlockState state) {
return state.is(tag);
}

View file

@ -2,6 +2,8 @@ package com.simibubi.create;
import java.util.Random;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import org.slf4j.Logger;
import com.google.gson.Gson;
@ -9,6 +11,7 @@ import com.google.gson.GsonBuilder;
import com.mojang.logging.LogUtils;
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.compat.curios.Curios;
import com.simibubi.create.content.contraptions.TorquePropagator;
import com.simibubi.create.content.contraptions.fluids.tank.BoilerHeaters;
@ -125,6 +128,8 @@ public class Create {
AllFeatures.register(modEventBus);
AllPlacementModifiers.register(modEventBus);
BuiltinRegistration.register(modEventBus);
BogeySizes.init();
AllBogeyStyles.register();
AllConfigs.register(modLoadingContext);
@ -134,6 +139,7 @@ public class Create {
ContraptionMovementSetting.registerDefaults();
AllArmInteractionPointTypes.register();
BlockSpoutingBehaviour.registerDefaults();
ComputerCraftProxy.register();
ForgeMod.enableMilkFluid();
CopperRegistries.inject();

View file

@ -0,0 +1,22 @@
package com.simibubi.create.api.event;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import net.minecraftforge.eventbus.api.Event;
public class TrackGraphMergeEvent extends Event{
private TrackGraph mergedInto, mergedFrom;
public TrackGraphMergeEvent(TrackGraph from, TrackGraph into) {
mergedInto = into;
mergedFrom = from;
}
public TrackGraph getGraphMergedInto() {
return mergedInto;
}
public TrackGraph getGraphMergedFrom() {
return mergedFrom;
}
}

View file

@ -5,7 +5,10 @@ import java.util.function.Supplier;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.registries.ForgeRegistries;
/**
* For compatibility with and without another mod present, we have to define load conditions of the specific code
@ -14,6 +17,8 @@ public enum Mods {
DYNAMICTREES,
TCONSTRUCT,
CURIOS,
COMPUTERCRAFT,
STORAGEDRAWERS,
XLPACKETS;
@ -51,4 +56,8 @@ public enum Mods {
toExecute.get().run();
}
}
public Block getBlock(String id) {
return ForgeRegistries.BLOCKS.getValue(new ResourceLocation(asId(), id));
}
}

View file

@ -0,0 +1,57 @@
package com.simibubi.create.compat.computercraft;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.BehaviourType;
import net.minecraft.nbt.CompoundTag;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class AbstractComputerBehaviour extends BlockEntityBehaviour {
public static final BehaviourType<AbstractComputerBehaviour> TYPE = new BehaviourType<>();
boolean hasAttachedComputer;
public AbstractComputerBehaviour(SmartBlockEntity te) {
super(te);
this.hasAttachedComputer = false;
}
@Override
public void read(CompoundTag nbt, boolean clientPacket) {
hasAttachedComputer = nbt.getBoolean("HasAttachedComputer");
super.read(nbt, clientPacket);
}
@Override
public void write(CompoundTag nbt, boolean clientPacket) {
nbt.putBoolean("HasAttachedComputer", hasAttachedComputer);
super.write(nbt, clientPacket);
}
public <T> boolean isPeripheralCap(Capability<T> cap) {
return false;
}
public <T> LazyOptional<T> getPeripheralCapability() {
return LazyOptional.empty();
}
public void removePeripheral() {}
public void setHasAttachedComputer(boolean hasAttachedComputer) {
this.hasAttachedComputer = hasAttachedComputer;
}
public boolean hasAttachedComputer() {
return hasAttachedComputer;
}
@Override
public BehaviourType<?> getType() {
return TYPE;
}
}

View file

@ -0,0 +1,37 @@
package com.simibubi.create.compat.computercraft;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.SyncedBlockEntity;
import com.simibubi.create.foundation.networking.BlockEntityDataPacket;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
public class AttachedComputerPacket extends BlockEntityDataPacket<SyncedBlockEntity> {
private final boolean hasAttachedComputer;
public AttachedComputerPacket(BlockPos tilePos, boolean hasAttachedComputer) {
super(tilePos);
this.hasAttachedComputer = hasAttachedComputer;
}
public AttachedComputerPacket(FriendlyByteBuf buffer) {
super(buffer);
this.hasAttachedComputer = buffer.readBoolean();
}
@Override
protected void writeData(FriendlyByteBuf buffer) {
buffer.writeBoolean(hasAttachedComputer);
}
@Override
protected void handlePacket(SyncedBlockEntity tile) {
if (tile instanceof SmartBlockEntity smartTile) {
smartTile.getBehaviour(AbstractComputerBehaviour.TYPE)
.setHasAttachedComputer(hasAttachedComputer);
}
}
}

View file

@ -0,0 +1,30 @@
package com.simibubi.create.compat.computercraft;
import java.util.function.Function;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.computercraft.implementation.ComputerBehaviour;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
public class ComputerCraftProxy {
public static void register() {
fallbackFactory = FallbackComputerBehaviour::new;
Mods.COMPUTERCRAFT.executeIfInstalled(() -> ComputerCraftProxy::registerWithDependency);
}
private static void registerWithDependency() {
/* Comment if computercraft.implementation is not in the source set */
computerFactory = ComputerBehaviour::new;
}
private static Function<SmartBlockEntity, ? extends AbstractComputerBehaviour> fallbackFactory;
private static Function<SmartBlockEntity, ? extends AbstractComputerBehaviour> computerFactory;
public static AbstractComputerBehaviour behaviour(SmartBlockEntity sbe) {
if (computerFactory == null)
return fallbackFactory.apply(sbe);
return computerFactory.apply(sbe);
}
}

View file

@ -0,0 +1,96 @@
package com.simibubi.create.compat.computercraft;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.element.GuiGameElement;
import com.simibubi.create.foundation.gui.widget.AbstractSimiWidget;
import com.simibubi.create.foundation.gui.widget.ElementWidget;
import com.simibubi.create.foundation.gui.widget.IconButton;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
public class ComputerScreen extends AbstractSimiScreen {
private final AllGuiTextures background = AllGuiTextures.COMPUTER;
private final Supplier<Component> displayTitle;
private final RenderWindowFunction additional;
private final Screen previousScreen;
private final Supplier<Boolean> hasAttachedComputer;
private AbstractSimiWidget computerWidget;
private IconButton confirmButton;
public ComputerScreen(Component title, @Nullable RenderWindowFunction additional, Screen previousScreen, Supplier<Boolean> hasAttachedComputer) {
this(title, () -> title, additional, previousScreen, hasAttachedComputer);
}
public ComputerScreen(Component title, Supplier<Component> displayTitle, @Nullable RenderWindowFunction additional, Screen previousScreen, Supplier<Boolean> hasAttachedComputer) {
super(title);
this.displayTitle = displayTitle;
this.additional = additional;
this.previousScreen = previousScreen;
this.hasAttachedComputer = hasAttachedComputer;
}
@Override
public void tick() {
if (!hasAttachedComputer.get())
minecraft.setScreen(previousScreen);
super.tick();
}
@Override
protected void init() {
setWindowSize(background.width, background.height);
super.init();
int x = guiLeft;
int y = guiTop;
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
computerWidget = new ElementWidget(x + 33, y + 38)
.showingElement(GuiGameElement.of(Mods.COMPUTERCRAFT.getBlock("computer_advanced")));
computerWidget.getToolTip().add(Lang.translate("gui.attached_computer.hint").component());
addRenderableWidget(computerWidget);
});
confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
confirmButton.withCallback(this::onClose);
addRenderableWidget(confirmButton);
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
int x = guiLeft;
int y = guiTop;
background.render(ms, x, y, this);
font.draw(ms, displayTitle.get(), x + background.width / 2.0F - font.width(displayTitle.get()) / 2.0F, y + 4, 0x442000);
font.drawWordWrap(Lang.translate("gui.attached_computer.controlled").component(), x + 55, y + 32, 111, 0x7A7A7A);
if (additional != null)
additional.render(ms, mouseX, mouseY, partialTicks, x, y, background);
}
@FunctionalInterface
public interface RenderWindowFunction {
void render(PoseStack ms, int mouseX, int mouseY, float partialTicks, int guiLeft, int guiTop, AllGuiTextures background);
}
}

View file

@ -0,0 +1,16 @@
package com.simibubi.create.compat.computercraft;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
public class FallbackComputerBehaviour extends AbstractComputerBehaviour {
public FallbackComputerBehaviour(SmartBlockEntity te) {
super(te);
}
@Override
public boolean hasAttachedComputer() {
return false;
}
}

View file

@ -0,0 +1,74 @@
package com.simibubi.create.compat.computercraft.implementation;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.implementation.peripherals.DisplayLinkPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.SequencedGearshiftPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedControllerPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedGaugePeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.StationPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.StressGaugePeripheral;
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerBlockEntity;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencedGearshiftBlockEntity;
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeBlockEntity;
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeBlockEntity;
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlockEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.capabilities.CapabilityToken;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullSupplier;
public class ComputerBehaviour extends AbstractComputerBehaviour {
protected static final Capability<IPeripheral> PERIPHERAL_CAPABILITY =
CapabilityManager.get(new CapabilityToken<>() {
});
LazyOptional<IPeripheral> peripheral;
NonNullSupplier<IPeripheral> peripheralSupplier;
public ComputerBehaviour(SmartBlockEntity te) {
super(te);
this.peripheralSupplier = getPeripheralFor(te);
}
public static NonNullSupplier<IPeripheral> getPeripheralFor(SmartBlockEntity te) {
if (te instanceof SpeedControllerBlockEntity scte)
return () -> new SpeedControllerPeripheral(scte, scte.targetSpeed);
if (te instanceof DisplayLinkBlockEntity dlte)
return () -> new DisplayLinkPeripheral(dlte);
if (te instanceof SequencedGearshiftBlockEntity sgte)
return () -> new SequencedGearshiftPeripheral(sgte);
if (te instanceof SpeedGaugeBlockEntity sgte)
return () -> new SpeedGaugePeripheral(sgte);
if (te instanceof StressGaugeBlockEntity sgte)
return () -> new StressGaugePeripheral(sgte);
if (te instanceof StationBlockEntity ste)
return () -> new StationPeripheral(ste);
throw new IllegalArgumentException("No peripheral available for " + te.getType()
.getRegistryName());
}
@Override
public <T> boolean isPeripheralCap(Capability<T> cap) {
return cap == PERIPHERAL_CAPABILITY;
}
@Override
public <T> LazyOptional<T> getPeripheralCapability() {
if (peripheral == null || !peripheral.isPresent())
peripheral = LazyOptional.of(peripheralSupplier);
return peripheral.cast();
}
@Override
public void removePeripheral() {
if (peripheral != null)
peripheral.invalidate();
}
}

View file

@ -0,0 +1,172 @@
package com.simibubi.create.compat.computercraft.implementation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaTable;
import dan200.computercraft.api.lua.LuaValues;
public class CreateLuaTable implements LuaTable<Object, Object> {
private final Map<Object, Object> map;
public CreateLuaTable() {
this.map = new HashMap<>();
}
public CreateLuaTable(Map<?, ?> map) {
this.map = new HashMap<>(map);
}
public boolean getBoolean(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof Boolean))
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
return (Boolean) value;
}
public String getString(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof String))
throw LuaValues.badField(key, "string", LuaValues.getType(value));
return (String) value;
}
public CreateLuaTable getTable(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof Map<?, ?>))
throw LuaValues.badField(key, "table", LuaValues.getType(value));
return new CreateLuaTable((Map<?, ?>) value);
}
public Optional<Boolean> getOptBoolean(String key) throws LuaException {
Object value = get(key);
if (value == null)
return Optional.empty();
if (!(value instanceof Boolean))
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
return Optional.of((Boolean) value);
}
public Set<String> stringKeySet() throws LuaException {
Set<String> stringSet = new HashSet<>();
for (Object key : keySet()) {
if (!(key instanceof String))
throw new LuaException("key " + key + " is not string (got " + LuaValues.getType(key) + ")");
stringSet.add((String) key);
}
return Collections.unmodifiableSet(stringSet);
}
public Collection<CreateLuaTable> tableValues() throws LuaException {
List<CreateLuaTable> tables = new ArrayList<>();
for (int i = 1; i <= size(); i++) {
Object value = get((double) i);
if (!(value instanceof Map<?, ?>))
throw new LuaException("value " + value + " is not table (got " + LuaValues.getType(value) + ")");
tables.add(new CreateLuaTable((Map<?, ?>) value));
}
return Collections.unmodifiableList(tables);
}
public Map<Object, Object> getMap() {
return map;
}
@Nullable
@Override
public Object put(Object key, Object value) {
return map.put(key, value);
}
public void putBoolean(String key, boolean value) {
map.put(key, value);
}
public void putDouble(String key, double value) {
map.put(key, value);
}
public void putString(String key, String value) {
map.put(key, value);
}
public void putTable(String key, CreateLuaTable value) {
map.put(key, value);
}
public void putTable(int i, CreateLuaTable value) {
map.put(i, value);
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return map.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return map.containsValue(o);
}
@Override
public Object get(Object o) {
return map.get(o);
}
@NotNull
@Override
public Set<Object> keySet() {
return map.keySet();
}
@NotNull
@Override
public Collection<Object> values() {
return map.values();
}
@NotNull
@Override
public Set<Entry<Object, Object>> entrySet() {
return map.entrySet();
}
}

View file

@ -0,0 +1,108 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlockEntity;
import com.simibubi.create.content.logistics.block.display.DisplayLinkContext;
import com.simibubi.create.content.logistics.block.display.target.DisplayTargetStats;
import dan200.computercraft.api.lua.LuaFunction;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
public class DisplayLinkPeripheral extends SyncedPeripheral<DisplayLinkBlockEntity> {
public static final String TAG_KEY = "ComputerSourceList";
private final AtomicInteger cursorX = new AtomicInteger();
private final AtomicInteger cursorY = new AtomicInteger();
public DisplayLinkPeripheral(DisplayLinkBlockEntity tile) {
super(tile);
}
@LuaFunction
public final void setCursorPos(int x, int y) {
cursorX.set(x - 1);
cursorY.set(y - 1);
}
@LuaFunction
public final Object[] getCursorPos() {
return new Object[] {cursorX.get() + 1, cursorY.get() + 1};
}
@LuaFunction(mainThread = true)
public final Object[] getSize() {
DisplayTargetStats stats = tile.activeTarget.provideStats(new DisplayLinkContext(tile.getLevel(), tile));
return new Object[]{stats.maxRows(), stats.maxColumns()};
}
@LuaFunction
public final boolean isColor() {
return false;
}
@LuaFunction
public final boolean isColour() {
return false;
}
@LuaFunction
public final void write(String text) {
ListTag tag = tile.getSourceConfig().getList(TAG_KEY, Tag.TAG_STRING);
int x = cursorX.get();
int y = cursorY.get();
for (int i = tag.size(); i <= y; i++) {
tag.add(StringTag.valueOf(""));
}
StringBuilder builder = new StringBuilder(tag.getString(y));
builder.append(" ".repeat(Math.max(0, x - builder.length())));
builder.replace(x, x + text.length(), text);
tag.set(y, StringTag.valueOf(builder.toString()));
synchronized (tile) {
tile.getSourceConfig().put(TAG_KEY, tag);
}
cursorX.set(x + text.length());
}
@LuaFunction
public final void clearLine() {
ListTag tag = tile.getSourceConfig().getList(TAG_KEY, Tag.TAG_STRING);
if (tag.size() > cursorY.get())
tag.set(cursorY.get(), StringTag.valueOf(""));
synchronized (tile) {
tile.getSourceConfig().put(TAG_KEY, tag);
}
}
@LuaFunction
public final void clear() {
synchronized (tile) {
tile.getSourceConfig().put(TAG_KEY, new ListTag());
}
}
@LuaFunction(mainThread = true)
public final void update() {
tile.tickSource();
}
@NotNull
@Override
public String getType() {
return "Create_DisplayLink";
}
}

View file

@ -0,0 +1,54 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Instruction;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.InstructionSpeedModifiers;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencedGearshiftBlockEntity;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencerInstructions;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
public class SequencedGearshiftPeripheral extends SyncedPeripheral<SequencedGearshiftBlockEntity> {
public SequencedGearshiftPeripheral(SequencedGearshiftBlockEntity tile) {
super(tile);
}
@LuaFunction(mainThread = true)
public final void rotate(IArguments arguments) throws LuaException {
runInstruction(arguments, SequencerInstructions.TURN_ANGLE);
}
@LuaFunction(mainThread = true)
public final void move(IArguments arguments) throws LuaException {
runInstruction(arguments, SequencerInstructions.TURN_DISTANCE);
}
@LuaFunction
public final boolean isRunning() {
return !this.tile.isIdle();
}
private void runInstruction(IArguments arguments, SequencerInstructions instructionType) throws LuaException {
int speedModifier = arguments.count() > 1 ? arguments.getInt(1) : 1;
this.tile.getInstructions().clear();
this.tile.getInstructions().add(new Instruction(
instructionType,
InstructionSpeedModifiers.getByModifier(speedModifier),
Math.abs(arguments.getInt(0))));
this.tile.getInstructions().add(new Instruction(SequencerInstructions.END));
this.tile.run(0);
}
@NotNull
@Override
public String getType() {
return "Create_SequencedGearshift";
}
}

View file

@ -0,0 +1,35 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.ScrollValueBehaviour;
import dan200.computercraft.api.lua.LuaFunction;
public class SpeedControllerPeripheral extends SyncedPeripheral<SpeedControllerBlockEntity> {
private final ScrollValueBehaviour targetSpeed;
public SpeedControllerPeripheral(SpeedControllerBlockEntity tile, ScrollValueBehaviour targetSpeed) {
super(tile);
this.targetSpeed = targetSpeed;
}
@LuaFunction(mainThread = true)
public final void setTargetSpeed(int speed) {
this.targetSpeed.setValue(speed);
}
@LuaFunction
public final float getTargetSpeed() {
return this.targetSpeed.getValue();
}
@NotNull
@Override
public String getType() {
return "Create_RotationSpeedController";
}
}

View file

@ -0,0 +1,26 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeBlockEntity;
import dan200.computercraft.api.lua.LuaFunction;
public class SpeedGaugePeripheral extends SyncedPeripheral<SpeedGaugeBlockEntity> {
public SpeedGaugePeripheral(SpeedGaugeBlockEntity tile) {
super(tile);
}
@LuaFunction
public final float getSpeed() {
return this.tile.getSpeed();
}
@NotNull
@Override
public String getType() {
return "Create_Speedometer";
}
}

View file

@ -0,0 +1,269 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import java.util.Map;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.compat.computercraft.implementation.CreateLuaTable;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.StringHelper;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import net.minecraft.nbt.ByteTag;
import net.minecraft.nbt.CollectionTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraftforge.network.PacketDistributor;
public class StationPeripheral extends SyncedPeripheral<StationBlockEntity> {
public StationPeripheral(StationBlockEntity tile) {
super(tile);
}
@LuaFunction(mainThread = true)
public final void assemble() throws LuaException {
if (!tile.isAssembling())
throw new LuaException("station must be in assembly mode");
tile.assemble(null);
if (tile.getStation() == null || tile.getStation().getPresentTrain() == null)
throw new LuaException("failed to assemble train");
if (!tile.exitAssemblyMode())
throw new LuaException("failed to exit assembly mode");
}
@LuaFunction(mainThread = true)
public final void disassemble() throws LuaException {
if (tile.isAssembling())
throw new LuaException("station must not be in assembly mode");
getTrainOrThrow();
if (!tile.enterAssemblyMode(null))
throw new LuaException("could not disassemble train");
}
@LuaFunction(mainThread = true)
public final void setAssemblyMode(boolean assemblyMode) throws LuaException {
if (assemblyMode) {
if (!tile.enterAssemblyMode(null))
throw new LuaException("failed to enter assembly mode");
} else {
if (!tile.exitAssemblyMode())
throw new LuaException("failed to exit assembly mode");
}
}
@LuaFunction
public final boolean isInAssemblyMode() {
return tile.isAssembling();
}
@LuaFunction
public final String getStationName() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.name;
}
@LuaFunction(mainThread = true)
public final void setStationName(String name) throws LuaException {
if (!tile.updateName(name))
throw new LuaException("could not set station name");
}
@LuaFunction
public final boolean isTrainPresent() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.getPresentTrain() != null;
}
@LuaFunction
public final boolean isTrainImminent() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.getImminentTrain() != null;
}
@LuaFunction
public final boolean isTrainEnroute() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.getNearestTrain() != null;
}
@LuaFunction
public final String getTrainName() throws LuaException {
Train train = getTrainOrThrow();
return train.name.getString();
}
@LuaFunction(mainThread = true)
public final void setTrainName(String name) throws LuaException {
Train train = getTrainOrThrow();
train.name = Components.literal(name);
AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new TrainEditPacket.TrainEditReturnPacket(train.id, name, train.icon.getId()));
}
@LuaFunction
public final boolean hasSchedule() throws LuaException {
Train train = getTrainOrThrow();
return train.runtime.getSchedule() != null;
}
@LuaFunction
public final CreateLuaTable getSchedule() throws LuaException {
Train train = getTrainOrThrow();
Schedule schedule = train.runtime.getSchedule();
if (schedule == null)
throw new LuaException("train doesn't have a schedule");
return fromCompoundTag(schedule.write());
}
@LuaFunction(mainThread = true)
public final void setSchedule(IArguments arguments) throws LuaException {
Train train = getTrainOrThrow();
Schedule schedule = Schedule.fromTag(toCompoundTag(new CreateLuaTable(arguments.getTable(0))));
boolean autoSchedule = train.runtime.getSchedule() == null || train.runtime.isAutoSchedule;
train.runtime.setSchedule(schedule, autoSchedule);
}
private @NotNull Train getTrainOrThrow() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
Train train = station.getPresentTrain();
if (train == null)
throw new LuaException("there is no train present");
return train;
}
private static @NotNull CreateLuaTable fromCompoundTag(CompoundTag tag) throws LuaException {
return (CreateLuaTable) fromNBTTag(null, tag);
}
private static @NotNull Object fromNBTTag(@Nullable String key, Tag tag) throws LuaException {
byte type = tag.getId();
if (type == Tag.TAG_BYTE && key != null && key.equals("Count"))
return ((NumericTag) tag).getAsByte();
else if (type == Tag.TAG_BYTE)
return ((NumericTag) tag).getAsByte() != 0;
else if (type == Tag.TAG_SHORT || type == Tag.TAG_INT || type == Tag.TAG_LONG)
return ((NumericTag) tag).getAsLong();
else if (type == Tag.TAG_FLOAT || type == Tag.TAG_DOUBLE)
return ((NumericTag) tag).getAsDouble();
else if (type == Tag.TAG_STRING)
return tag.getAsString();
else if (type == Tag.TAG_LIST || type == Tag.TAG_BYTE_ARRAY || type == Tag.TAG_INT_ARRAY || type == Tag.TAG_LONG_ARRAY) {
CreateLuaTable list = new CreateLuaTable();
CollectionTag<?> listTag = (CollectionTag<?>) tag;
for (int i = 0; i < listTag.size(); i++) {
list.put(i + 1, fromNBTTag(null, listTag.get(i)));
}
return list;
} else if (type == Tag.TAG_COMPOUND) {
CreateLuaTable table = new CreateLuaTable();
CompoundTag compoundTag = (CompoundTag) tag;
for (String compoundKey : compoundTag.getAllKeys()) {
table.put(
StringHelper.camelCaseToSnakeCase(compoundKey),
fromNBTTag(compoundKey, compoundTag.get(compoundKey))
);
}
return table;
}
throw new LuaException("unknown tag type " + tag.getType().getName());
}
private static @NotNull CompoundTag toCompoundTag(CreateLuaTable table) throws LuaException {
return (CompoundTag) toNBTTag(null, table.getMap());
}
private static @NotNull Tag toNBTTag(@Nullable String key, Object value) throws LuaException {
if (value instanceof Boolean v)
return ByteTag.valueOf(v);
else if (value instanceof Byte || (key != null && key.equals("count")))
return ByteTag.valueOf(((Number) value).byteValue());
else if (value instanceof Number v) {
// If number is numerical integer
if (v.intValue() == v.doubleValue())
return IntTag.valueOf(v.intValue());
else
return DoubleTag.valueOf(v.doubleValue());
} else if (value instanceof String v)
return StringTag.valueOf(v);
else if (value instanceof Map<?, ?> v && v.containsKey(1.0)) { // List
ListTag list = new ListTag();
for (Object o : v.values()) {
list.add(toNBTTag(null, o));
}
return list;
} else if (value instanceof Map<?, ?> v) { // Table/Map
CompoundTag compound = new CompoundTag();
for (Object objectKey : v.keySet()) {
if (!(objectKey instanceof String compoundKey))
throw new LuaException("table key is not of type string");
compound.put(
// Items serialize their resource location as "id" and not as "Id".
// This check is needed to see if the 'i' should be left lowercase or not.
// Items store "count" in the same compound tag, so we can check for its presence to see if this is a serialized item
compoundKey.equals("id") && v.containsKey("count") ? "id" : StringHelper.snakeCaseToCamelCase(compoundKey),
toNBTTag(compoundKey, v.get(compoundKey))
);
}
return compound;
}
throw new LuaException("unknown object type " + value.getClass().getName());
}
@NotNull
@Override
public String getType() {
return "Create_Station";
}
}

View file

@ -0,0 +1,31 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeBlockEntity;
import dan200.computercraft.api.lua.LuaFunction;
public class StressGaugePeripheral extends SyncedPeripheral<StressGaugeBlockEntity> {
public StressGaugePeripheral(StressGaugeBlockEntity tile) {
super(tile);
}
@LuaFunction
public final float getStress() {
return this.tile.getNetworkStress();
}
@LuaFunction
public final float getStressCapacity() {
return this.tile.getNetworkCapacity();
}
@NotNull
@Override
public String getType() {
return "Create_Stressometer";
}
}

View file

@ -0,0 +1,50 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AttachedComputerPacket;
import com.simibubi.create.compat.computercraft.implementation.ComputerBehaviour;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.networking.AllPackets;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraftforge.network.PacketDistributor;
public abstract class SyncedPeripheral<T extends SmartBlockEntity> implements IPeripheral {
protected final T tile;
private final AtomicInteger computers = new AtomicInteger();
public SyncedPeripheral(T tile) {
this.tile = tile;
}
@Override
public void attach(@NotNull IComputerAccess computer) {
computers.incrementAndGet();
updateTile();
}
@Override
public void detach(@NotNull IComputerAccess computer) {
computers.decrementAndGet();
updateTile();
}
private void updateTile() {
boolean hasAttachedComputer = computers.get() > 0;
tile.getBehaviour(ComputerBehaviour.TYPE).setHasAttachedComputer(hasAttachedComputer);
AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new AttachedComputerPacket(tile.getBlockPos(), hasAttachedComputer));
}
@Override
public boolean equals(@Nullable IPeripheral other) {
return this == other;
}
}

View file

@ -4,7 +4,7 @@ import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.virtual.VirtualRenderWorld;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ActorInstance;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices;
@ -62,7 +62,7 @@ public class DrillMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override
public boolean canBreak(Level world, BlockPos breakingPos, BlockState state) {
return super.canBreak(world, breakingPos, state) && !state.getCollisionShape(world, breakingPos)
.isEmpty() && !AllBlocks.TRACK.has(state);
.isEmpty() && !AllTags.AllBlockTags.TRACKS.matches(state);
}
}

View file

@ -3,8 +3,8 @@ package com.simibubi.create.content.contraptions.components.press;
import java.util.List;
import java.util.Optional;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCraftingRecipe;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.Mode;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.PressingBehaviourSpecifics;
@ -68,7 +68,7 @@ public class MechanicalPressBlockEntity extends BasinOperatingBlockEntity implem
public void onItemPressed(ItemStack result) {
award(AllAdvancements.PRESS);
if (AllBlocks.TRACK.isIn(result))
if (AllTags.AllBlockTags.TRACKS.matches(result))
tracksCreated += result.getCount();
if (tracksCreated >= 1000) {
award(AllAdvancements.TRACK_CRAFTING);

View file

@ -29,7 +29,7 @@ import com.simibubi.create.content.contraptions.fluids.tank.FluidTankBlock;
import com.simibubi.create.content.curiosities.deco.SlidingDoorBlock;
import com.simibubi.create.content.logistics.block.redstone.RedstoneLinkBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlock;
import com.simibubi.create.foundation.config.ContraptionMovementSetting;
@ -338,7 +338,7 @@ public class BlockMovementChecks {
return direction == state.getValue(StickerBlock.FACING)
&& !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
}
if (block instanceof IBogeyBlock bogey)
if (block instanceof AbstractBogeyBlock bogey)
return bogey.getStickySurfaces(world, pos, state)
.contains(direction);
if (block instanceof WhistleBlock)

View file

@ -64,7 +64,7 @@ import com.simibubi.create.content.curiosities.deco.SlidingDoorBlock;
import com.simibubi.create.content.logistics.block.inventories.CreativeCrateBlockEntity;
import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultBlockEntity;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.foundation.blockEntity.IMultiBlockEntityContainer;
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.config.AllConfigs;
@ -347,7 +347,7 @@ public abstract class Contraption {
}
// Bogeys tend to have sticky sides
if (state.getBlock()instanceof IBogeyBlock bogey)
if (state.getBlock()instanceof AbstractBogeyBlock<?> bogey)
for (Direction d : bogey.getStickySurfaces(world, pos, state))
if (!visited.contains(pos.relative(d)))
frontier.add(pos.relative(d));

View file

@ -202,6 +202,7 @@ public class GantryContraptionEntity extends AbstractContraptionEntity {
}
@Override
@OnlyIn(Dist.CLIENT)
public void applyLocalTransforms(PoseStack matrixStack, float partialTicks) {}
public void updateClientMotion() {

View file

@ -42,7 +42,6 @@ public class WaterWheelInstance<T extends WaterWheelBlockEntity> extends CutoutR
return getRotatingMaterial().model(key, () -> {
BakedModel model = WaterWheelRenderer.generateModel(key);
BlockState state = key.state();
// TODO waterwheels
Direction dir;
if (key.large()) {
dir = Direction.fromAxisAndDirection(state.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE);

View file

@ -3,9 +3,7 @@ package com.simibubi.create.content.contraptions.components.waterwheel;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.StitchedSprite;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllPartialModels;
@ -32,6 +30,7 @@ import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.ModelData;
import net.minecraftforge.registries.ForgeRegistries;
public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends KineticBlockEntityRenderer<T> {
@ -41,6 +40,8 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
public static final StitchedSprite OAK_LOG_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log"));
public static final StitchedSprite OAK_LOG_TOP_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log_top"));
private static final String[] LOG_SUFFIXES = new String[] { "_log", "_stem" };
protected final boolean large;
public WaterWheelRenderer(Context context, boolean large) {
@ -60,9 +61,8 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
protected SuperByteBuffer getRotatedModel(T be, BlockState state) {
WaterWheelModelKey key = new WaterWheelModelKey(large, state, be.material);
return CreateClient.BUFFER_CACHE.get(WATER_WHEEL, key, () -> {
BakedModel model = WaterWheelRenderer.generateModel(key);
BakedModel model = generateModel(key);
BlockState state1 = key.state();
// TODO waterwheels
Direction dir;
if (key.large()) {
dir = Direction.fromAxisAndDirection(state1.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE);
@ -74,25 +74,24 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
});
}
public static PartialModel getTemplateModel(boolean large, boolean extension) {
if (large) {
public static BakedModel generateModel(WaterWheelModelKey key) {
BakedModel template;
if (key.large()) {
boolean extension = key.state()
.getValue(LargeWaterWheelBlock.EXTENSION);
if (extension) {
return AllPartialModels.LARGE_WATER_WHEEL_EXTENSION;
template = AllPartialModels.LARGE_WATER_WHEEL_EXTENSION.get();
} else {
return AllPartialModels.LARGE_WATER_WHEEL;
template = AllPartialModels.LARGE_WATER_WHEEL.get();
}
} else {
return AllPartialModels.WATER_WHEEL;
template = AllPartialModels.WATER_WHEEL.get();
}
return generateModel(template, key.material());
}
public static BakedModel generateModel(WaterWheelModelKey key) {
boolean extension = key.state()
.getOptionalValue(LargeWaterWheelBlock.EXTENSION)
.orElse(false);
BakedModel template = getTemplateModel(key.large(), extension).get();
BlockState planksBlockState = key.material();
public static BakedModel generateModel(BakedModel template, BlockState planksBlockState) {
Block planksBlock = planksBlockState.getBlock();
ResourceLocation id = RegisteredObjects.getKeyOrThrow(planksBlock);
String path = id.getPath();
@ -104,7 +103,7 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
Map<TextureAtlasSprite, TextureAtlasSprite> map = new Reference2ReferenceOpenHashMap<>();
map.put(OAK_PLANKS_TEMPLATE.get(), getSpriteOnSide(planksBlockState, Direction.UP));
map.put(OAK_LOG_TEMPLATE.get(), getSpriteOnSide(logBlockState, Direction.NORTH));
map.put(OAK_LOG_TEMPLATE.get(), getSpriteOnSide(logBlockState, Direction.SOUTH));
map.put(OAK_LOG_TOP_TEMPLATE.get(), getSpriteOnSide(logBlockState, Direction.UP));
return BakedModelHelper.generateModel(template, map::get);
@ -114,7 +113,7 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
}
private static BlockState getLogBlockState(String namespace, String wood) {
for (String suffix : new String[] { "_log", "_stem" }) {
for (String suffix : LOG_SUFFIXES) {
Optional<BlockState> state =
ForgeRegistries.BLOCKS.getHolder(new ResourceLocation(namespace, wood + suffix))
.map(Holder::value)
@ -125,18 +124,29 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
return Blocks.OAK_LOG.defaultBlockState();
}
private static TextureAtlasSprite getSpriteOnSide(BlockState blockstate, Direction side) {
BakedModel blockModel = Minecraft.getInstance()
private static TextureAtlasSprite getSpriteOnSide(BlockState state, Direction side) {
BakedModel model = Minecraft.getInstance()
.getBlockRenderer()
.getBlockModel(blockstate);
if (blockModel == null)
.getBlockModel(state);
if (model == null)
return null;
@SuppressWarnings("deprecation")
List<BakedQuad> quads = blockModel.getQuads(blockstate, side, RandomSource.create());
if (quads.isEmpty())
return null;
return quads.get(0)
.getSprite();
RandomSource random = RandomSource.create();
random.setSeed(42L);
List<BakedQuad> quads = model.getQuads(state, side, random, ModelData.EMPTY, null);
if (!quads.isEmpty()) {
return quads.get(0)
.getSprite();
}
random.setSeed(42L);
quads = model.getQuads(state, null, random, ModelData.EMPTY, null);
if (!quads.isEmpty()) {
for (BakedQuad quad : quads) {
if (quad.getDirection() == side) {
return quad.getSprite();
}
}
}
return model.getParticleIcon(ModelData.EMPTY);
}
}

View file

@ -25,6 +25,7 @@ import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags;
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.item.ItemStack;
@ -39,6 +40,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.common.ForgeConfig;
import net.minecraftforge.common.Tags;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
@ -54,6 +56,7 @@ public class OpenEndedPipe extends FlowSource {
registerEffectHandler(new MilkEffectHandler());
registerEffectHandler(new WaterEffectHandler());
registerEffectHandler(new LavaEffectHandler());
registerEffectHandler(new TeaEffectHandler());
}
private Level world;
@ -443,4 +446,23 @@ public class OpenEndedPipe extends FlowSource {
}
}
public static class TeaEffectHandler implements IEffectHandler {
@Override
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
return fluid.getFluid().isSame(AllFluids.TEA.get());
}
@Override
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
Level world = pipe.getWorld();
if (world.getGameTime() % 5 != 0)
return;
List<LivingEntity> entities = world
.getEntitiesOfClass(LivingEntity.class, pipe.getAOE(), LivingEntity::isAffectedByPotions);
for (LivingEntity entity : entities) {
entity.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 21, 0, false, false, false));
}
}
}
}

View file

@ -125,4 +125,8 @@ public class HosePulleyFluidHandler implements IFluidHandler {
return internalTank.isFluidValid(tank, stack);
}
public SmartFluidTank getInternalTank() {
return internalTank;
}
}

View file

@ -38,14 +38,24 @@ public class SpoutRenderer extends SafeBlockEntityRenderer<SpoutBlockEntity> {
.getValue(partialTicks);
if (!fluidStack.isEmpty() && level != 0) {
boolean top = fluidStack.getFluid()
.getAttributes()
.isLighterThanAir();
level = Math.max(level, 0.175f);
float min = 2.5f / 16f;
float max = min + (11 / 16f);
float yOffset = (11 / 16f) * level;
ms.pushPose();
ms.translate(0, yOffset, 0);
FluidRenderer.renderFluidBox(fluidStack, min, min - yOffset, min, max, min, max, buffer, ms, light,
false);
if (!top) ms.translate(0, yOffset, 0);
else ms.translate(0, max - min, 0);
FluidRenderer.renderFluidBox(fluidStack,
min, min - yOffset, min,
max, min, max,
buffer, ms, light, false);
ms.popPose();
}

View file

@ -12,6 +12,8 @@ import java.util.function.Supplier;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.EncasedBlock;
@ -36,7 +38,9 @@ import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
@ -46,7 +50,8 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.ticks.TickPriority;
public class EncasedPipeBlock extends Block implements IWrenchable, ISpecialBlockItemRequirement, IBE<FluidPipeBlockEntity>, EncasedBlock {
public class EncasedPipeBlock extends Block
implements IWrenchable, ISpecialBlockItemRequirement, IBE<FluidPipeBlockEntity>, EncasedBlock, ITransformableBlock {
public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION;
private final Supplier<Block> casing;
@ -173,4 +178,20 @@ public class EncasedPipeBlock extends Block implements IWrenchable, ISpecialBloc
EncasedPipeBlock.transferSixWayProperties(state, defaultBlockState()));
FluidTransportBehaviour.loadFlows(level, pos);
}
@Override
public BlockState rotate(BlockState pState, Rotation pRotation) {
return FluidPipeBlockRotation.rotate(pState, pRotation);
}
@Override
public BlockState mirror(BlockState pState, Mirror pMirror) {
return FluidPipeBlockRotation.mirror(pState, pMirror);
}
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
return FluidPipeBlockRotation.transform(state, transform);
}
}

View file

@ -7,6 +7,8 @@ import javax.annotation.Nullable;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour;
@ -36,7 +38,9 @@ import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
@ -50,8 +54,8 @@ import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.ticks.TickPriority;
public class FluidPipeBlock extends PipeBlock
implements SimpleWaterloggedBlock, IWrenchableWithBracket, IBE<FluidPipeBlockEntity>, EncasableBlock {
public class FluidPipeBlock extends PipeBlock implements SimpleWaterloggedBlock, IWrenchableWithBracket,
IBE<FluidPipeBlockEntity>, EncasableBlock, ITransformableBlock {
private static final VoxelShape OCCLUSION_BOX = Block.box(4, 4, 4, 12, 12, 12);
@ -337,4 +341,20 @@ public class FluidPipeBlock extends PipeBlock
public VoxelShape getOcclusionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
return OCCLUSION_BOX;
}
@Override
public BlockState rotate(BlockState pState, Rotation pRotation) {
return FluidPipeBlockRotation.rotate(pState, pRotation);
}
@Override
public BlockState mirror(BlockState pState, Mirror pMirror) {
return FluidPipeBlockRotation.mirror(pState, pMirror);
}
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
return FluidPipeBlockRotation.transform(state, transform);
}
}

View file

@ -0,0 +1,49 @@
package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.Map;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
public class FluidPipeBlockRotation {
public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION;
public static BlockState rotate(BlockState state, Rotation rotation) {
BlockState rotated = state;
for (Direction direction : Iterate.horizontalDirections)
rotated = rotated.setValue(FACING_TO_PROPERTY_MAP.get(rotation.rotate(direction)),
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
return rotated;
}
public static BlockState mirror(BlockState state, Mirror mirror) {
BlockState mirrored = state;
for (Direction direction : Iterate.horizontalDirections)
mirrored = mirrored.setValue(FACING_TO_PROPERTY_MAP.get(mirror.mirror(direction)),
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
return mirrored;
}
public static BlockState transform(BlockState state, StructureTransform transform) {
if (transform.mirror != null)
state = mirror(state, transform.mirror);
if (transform.rotationAxis == Direction.Axis.Y)
return rotate(state, transform.rotation);
BlockState rotated = state;
for (Direction direction : Iterate.directions)
rotated = rotated.setValue(FACING_TO_PROPERTY_MAP.get(transform.rotateFacing(direction)),
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
return rotated;
}
}

View file

@ -92,8 +92,7 @@ public class SmartFluidPipeBlock extends FaceAttachedHorizontalDirectionalBlock
boolean blockTypeChanged = state.getBlock() != newState.getBlock();
if (blockTypeChanged && !world.isClientSide)
FluidPropagator.propagateChangedPipe(world, pos, state);
if (state.hasBlockEntity() && (blockTypeChanged || !newState.hasBlockEntity()))
world.removeBlockEntity(pos);
IBE.onRemove(state, world, pos, newState);
}
@Override

View file

@ -44,7 +44,8 @@ public class SequencedAssemblyRecipe implements Recipe<RecipeWrapper> {
protected List<SequencedRecipe<?>> sequence;
protected int loops;
protected ProcessingOutput transitionalItem;
protected List<ProcessingOutput> resultPool;
public final List<ProcessingOutput> resultPool;
public SequencedAssemblyRecipe(ResourceLocation recipeId, SequencedAssemblyRecipeSerializer serializer) {
this.id = recipeId;

View file

@ -2,6 +2,11 @@ package com.simibubi.create.content.contraptions.relays.advanced;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.RotationPropagator;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.components.motor.KineticScrollValueBehaviour;
@ -20,11 +25,14 @@ import net.minecraft.core.Direction;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class SpeedControllerBlockEntity extends KineticBlockEntity {
public static final int DEFAULT_SPEED = 16;
protected ScrollValueBehaviour targetSpeed;
public ScrollValueBehaviour targetSpeed;
public AbstractComputerBehaviour computerBehaviour;
boolean hasBracket;
@ -50,6 +58,7 @@ public class SpeedControllerBlockEntity extends KineticBlockEntity {
targetSpeed.value = DEFAULT_SPEED;
targetSpeed.withCallback(i -> this.updateTargetRotation());
behaviours.add(targetSpeed);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
registerAwardables(behaviours, AllAdvancements.SPEED_CONTROLLER);
}
@ -125,6 +134,20 @@ public class SpeedControllerBlockEntity extends KineticBlockEntity {
.isHorizontal();
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
private class ControllerValueBoxTransform extends ValueBoxTransform.Sided {
@Override

View file

@ -35,6 +35,9 @@ public class ConfigureSequencedGearshiftPacket extends BlockEntityConfigurationP
@Override
protected void applySettings(SequencedGearshiftBlockEntity be) {
if (be.computerBehaviour.hasAttachedComputer())
return;
be.run(-1);
be.instructions = Instruction.deserializeAll(instructions);
be.sendData();

View file

@ -19,8 +19,12 @@ public class Instruction {
}
public Instruction(SequencerInstructions instruction, int value) {
this(instruction, InstructionSpeedModifiers.FORWARD, value);
}
public Instruction(SequencerInstructions instruction, InstructionSpeedModifiers speedModifier, int value) {
this.instruction = instruction;
speedModifier = InstructionSpeedModifiers.FORWARD;
this.speedModifier = speedModifier;
this.value = value;
}

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.simibubi.create.foundation.utility.Components;
@ -36,4 +37,11 @@ public enum InstructionSpeedModifiers {
return options;
}
public static InstructionSpeedModifiers getByModifier(int modifier) {
return Arrays.stream(InstructionSpeedModifiers.values())
.filter(speedModifier -> speedModifier.value == modifier)
.findAny()
.orElse(InstructionSpeedModifiers.FORWARD);
}
}

View file

@ -1,9 +1,17 @@
package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
import java.util.List;
import java.util.Vector;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.relays.encased.SplitShaftBlockEntity;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos;
@ -12,6 +20,8 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
@ -22,6 +32,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
int timer;
boolean poweredPreviously;
public AbstractComputerBehaviour computerBehaviour;
public record SequenceContext(SequencerInstructions instruction, double relativeValue) {
public static SequenceContext fromGearshift(SequencerInstructions instruction, double kineticSpeed,
@ -61,6 +73,12 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
poweredPreviously = false;
}
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
}
@Override
public void tick() {
super.tick();
@ -103,6 +121,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
}
public void onRedstoneUpdate(boolean isPowered, boolean isRunning) {
if (computerBehaviour.hasAttachedComputer())
return;
if (!poweredPreviously && isPowered)
risingFlank();
poweredPreviously = isPowered;
@ -136,7 +156,7 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
}
}
protected void run(int instructionIndex) {
public void run(int instructionIndex) {
Instruction instruction = getInstruction(instructionIndex);
if (instruction == null || instruction.instruction == SequencerInstructions.END) {
if (getModifier() != 0)
@ -193,6 +213,20 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
super.read(compound, clientPacket);
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
@Override
public float getRotationSpeedModifier(Direction face) {
if (isVirtual())
@ -208,4 +242,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
.getSpeedModifier();
}
public Vector<Instruction> getInstructions() {
return this.instructions;
}
}

View file

@ -4,6 +4,7 @@ import java.util.Vector;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.compat.computercraft.ComputerScreen;
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons;
@ -15,7 +16,6 @@ import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
@ -25,22 +25,26 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
private final ItemStack renderedItem = AllBlocks.SEQUENCED_GEARSHIFT.asStack();
private final AllGuiTextures background = AllGuiTextures.SEQUENCER;
private IconButton confirmButton;
private SequencedGearshiftBlockEntity be;
private ListTag compareTag;
private Vector<Instruction> instructions;
private BlockPos pos;
private Vector<Vector<ScrollInput>> inputs;
public SequencedGearshiftScreen(SequencedGearshiftBlockEntity be) {
super(Lang.translateDirect("gui.sequenced_gearshift.title"));
this.instructions = be.instructions;
this.pos = be.getBlockPos();
this.be = be;
compareTag = Instruction.serializeAll(instructions);
}
@Override
protected void init() {
if (be.computerBehaviour.hasAttachedComputer())
minecraft.setScreen(
new ComputerScreen(title, this::renderAdditional, this, be.computerBehaviour::hasAttachedComputer));
setWindowSize(background.width, background.height);
setWindowOffset(-20, 0);
super.init();
@ -55,8 +59,7 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
for (int row = 0; row < instructions.size(); row++)
initInputsOfRow(row, x, y);
confirmButton =
new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
confirmButton.withCallback(() -> {
onClose();
});
@ -127,6 +130,15 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
modifier.setState(instruction.speedModifier.ordinal());
}
@Override
public void tick() {
super.tick();
if (be.computerBehaviour.hasAttachedComputer())
minecraft.setScreen(
new ComputerScreen(title, this::renderAdditional, this, be.computerBehaviour::hasAttachedComputer));
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
int x = guiLeft;
@ -134,6 +146,13 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
background.render(ms, x, y, this);
for (int row = 0; row < instructions.capacity(); row++) {
AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY;
int yOffset = toDraw.height * row;
toDraw.render(ms, x, y + 14 + yOffset, this);
}
for (int row = 0; row < instructions.capacity(); row++) {
AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY;
int yOffset = toDraw.height * row;
@ -157,9 +176,13 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
}
font.draw(ms, title, x + (background.width - 8) / 2 - font.width(title) / 2, y + 4, 0x592424);
renderAdditional(ms, mouseX, mouseY, partialTicks, x, y, background);
}
GuiGameElement.of(renderedItem)
.<GuiGameElement.GuiRenderBuilder>at(x + background.width + 6, y + background.height - 56, -200)
private void renderAdditional(PoseStack ms, int mouseX, int mouseY, float partialTicks, int guiLeft, int guiTop,
AllGuiTextures background) {
GuiGameElement.of(renderedItem).<GuiGameElement
.GuiRenderBuilder>at(guiLeft + background.width + 6, guiTop + background.height - 56, 100)
.scale(5)
.render(ms);
}
@ -172,7 +195,8 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
ListTag serialized = Instruction.serializeAll(instructions);
if (serialized.equals(compareTag))
return;
AllPackets.getChannel().sendToServer(new ConfigureSequencedGearshiftPacket(pos, serialized));
AllPackets.getChannel()
.sendToServer(new ConfigureSequencedGearshiftPacket(be.getBlockPos(), serialized));
}
@Override

View file

@ -12,7 +12,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
public class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleInformation {
public abstract class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleInformation {
public float dialTarget;
public float dialState;
@ -52,4 +52,5 @@ public class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleI
return true;
}
}

View file

@ -2,24 +2,41 @@ package com.simibubi.create.content.contraptions.relays.gauge;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class SpeedGaugeBlockEntity extends GaugeBlockEntity {
public AbstractComputerBehaviour computerBehaviour;
public SpeedGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
}
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
}
@Override
public void onSpeedChanged(float prevSpeed) {
super.onSpeedChanged(prevSpeed);
@ -62,4 +79,19 @@ public class SpeedGaugeBlockEntity extends GaugeBlockEntity {
.forGoggles(tooltip);
return true;
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
}

View file

@ -2,6 +2,11 @@ package com.simibubi.create.content.contraptions.relays.gauge;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.base.IRotate.StressImpact;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
@ -13,14 +18,19 @@ import com.simibubi.create.foundation.utility.LangBuilder;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class StressGaugeBlockEntity extends GaugeBlockEntity {
public AbstractComputerBehaviour computerBehaviour;
static BlockPos lastSent;
public StressGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
@ -30,6 +40,7 @@ public class StressGaugeBlockEntity extends GaugeBlockEntity {
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
registerAwardables(behaviours, AllAdvancements.STRESSOMETER, AllAdvancements.STRESSOMETER_MAXED);
}
@ -141,4 +152,18 @@ public class StressGaugeBlockEntity extends GaugeBlockEntity {
award(AllAdvancements.STRESSOMETER_MAXED);
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
}

View file

@ -440,7 +440,7 @@ public class ClipboardScreen extends AbstractSimiScreen {
if (currentEntries.get(editingIndex).text.getString()
.isEmpty() && currentEntries.size() > 1) {
currentEntries.remove(editingIndex);
editingIndex -= 1;
editingIndex = Math.max(0, editingIndex - 1);
editContext.setCursorToEnd();
return true;
} else if (hasControlDown()) {

View file

@ -6,6 +6,7 @@ import static net.minecraft.world.level.block.state.properties.BlockStatePropert
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.fluids.pipes.BracketBlock;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour;
@ -223,7 +224,7 @@ public class GirderBlock extends Block implements SimpleWaterloggedBlock, IWrenc
for (Direction d2 : Iterate.directionsInAxis(axis == Axis.X ? Axis.Z : Axis.X)) {
BlockState above = level.getBlockState(pos.above()
.relative(d2));
if (AllBlocks.TRACK.has(above)) {
if (AllTags.AllBlockTags.GIRDABLE_TRACKS.matches(above)) {
TrackShape shape = above.getValue(TrackBlock.SHAPE);
if (shape == (axis == Axis.X ? TrackShape.XO : TrackShape.ZO))
state = state.setValue(updateProperty, true);

View file

@ -9,7 +9,6 @@ import java.util.Optional;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintCraftingInventory;
import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintSection;
@ -106,7 +105,7 @@ public class BlueprintOverlayRenderer {
int tracks = info.requiredTracks;
while (tracks > 0) {
ingredients.add(Pair.of(AllBlocks.TRACK.asStack(Math.min(64, tracks)), info.hasRequiredTracks));
ingredients.add(Pair.of(new ItemStack(info.trackMaterial.getBlock(), Math.min(64, tracks)), info.hasRequiredTracks));
tracks -= 64;
}

View file

@ -26,6 +26,7 @@ import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringB
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.SidedFilteringBehaviour;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.INamedIconOptions;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.ScrollOptionBehaviour;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.Components;
@ -174,7 +175,7 @@ public class BrassTunnelBlockEntity extends BeltTunnelBlockEntity implements IHa
return;
if (selectionMode.get() != SelectionMode.SYNCHRONIZE || syncedOutputActive) {
distributionProgress = 10;
distributionProgress = AllConfigs.server().logistics.brassTunnelTimer.get();
sendData();
}
return;

View file

@ -7,6 +7,7 @@ import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
@ -33,4 +34,8 @@ public class DepotBlockEntity extends SmartBlockEntity {
return depotBehaviour.getItemCapability(cap, side);
return super.getCapability(cap, side);
}
public ItemStack getHeldItem() {
return depotBehaviour.getHeldItemStack();
}
}

View file

@ -9,6 +9,8 @@ import java.util.Map;
import javax.annotation.Nullable;
import com.simibubi.create.Create;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.content.logistics.block.display.source.ComputerDisplaySource;
import com.simibubi.create.content.logistics.block.display.source.DeathCounterDisplaySource;
import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
import com.simibubi.create.content.logistics.block.display.source.EnchantPowerDisplaySource;
@ -241,5 +243,14 @@ public class AllDisplayBehaviours {
assignBlockEntity(register(Create.asResource("scoreboard_display_source"), new ScoreboardDisplaySource()), BlockEntityType.COMMAND_BLOCK);
assignBlockEntity(register(Create.asResource("enchant_power_display_source"), new EnchantPowerDisplaySource()), BlockEntityType.ENCHANTING_TABLE);
assignBlock(register(Create.asResource("redstone_power_display_source"), new RedstonePowerDisplaySource()), Blocks.TARGET);
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
DisplayBehaviour computerDisplaySource = register(Create.asResource("computer_display_source"), new ComputerDisplaySource());
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "wired_modem_full"));
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_normal"));
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_advanced"));
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_command"));
});
}
}

View file

@ -2,6 +2,11 @@ package com.simibubi.create.content.logistics.block.display;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
import com.simibubi.create.content.logistics.block.display.target.DisplayTarget;
import com.simibubi.create.foundation.advancement.AllAdvancements;
@ -18,6 +23,8 @@ import net.minecraft.nbt.NbtUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class DisplayLinkBlockEntity extends SmartBlockEntity {
@ -33,6 +40,7 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
private boolean sendPulse;
public int refreshTicks;
public AbstractComputerBehaviour computerBehaviour;
public DisplayLinkBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
@ -44,6 +52,12 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
glow.chase(0, 0.5f, Chaser.EXP);
}
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
registerAwardables(behaviours, AllAdvancements.DISPLAY_LINK, AllAdvancements.DISPLAY_BOARD);
}
@Override
public void tick() {
super.tick();
@ -61,7 +75,7 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
}
refreshTicks++;
if (refreshTicks < activeSource.getPassiveRefreshTicks())
if (refreshTicks < activeSource.getPassiveRefreshTicks() || !activeSource.shouldPassiveReset())
return;
tickSource();
}
@ -118,11 +132,6 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
award(AllAdvancements.DISPLAY_LINK);
}
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
registerAwardables(behaviours, AllAdvancements.DISPLAY_LINK, AllAdvancements.DISPLAY_BOARD);
}
@Override
public void writeSafe(CompoundTag tag) {
super.writeSafe(tag);
@ -173,6 +182,21 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
sourceConfig = data.copy();
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
public void target(BlockPos targetPosition) {
this.targetOffset = targetPosition.subtract(worldPosition);
}

View file

@ -0,0 +1,33 @@
package com.simibubi.create.content.logistics.block.display.source;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.content.logistics.block.display.DisplayLinkContext;
import com.simibubi.create.content.logistics.block.display.target.DisplayTargetStats;
import com.simibubi.create.foundation.utility.Components;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.MutableComponent;
public class ComputerDisplaySource extends DisplaySource {
@Override
public List<MutableComponent> provideText(DisplayLinkContext context, DisplayTargetStats stats) {
List<MutableComponent> components = new ArrayList<>();
ListTag tag = context.sourceConfig().getList("ComputerSourceList", Tag.TAG_STRING);
for (int i = 0; i < tag.size(); i++) {
components.add(Components.literal(tag.getString(i)));
}
return components;
}
@Override
public boolean shouldPassiveReset() {
return false;
}
}

View file

@ -49,6 +49,10 @@ public abstract class DisplaySource extends DisplayBehaviour {
return 100;
};
public boolean shouldPassiveReset() {
return true;
}
protected String getTranslationKey() {
return id.getPath();
}

View file

@ -123,6 +123,10 @@ public class NixieTubeBlockEntity extends SmartBlockEntity {
customText = Optional.empty();
}
public int getRedstoneStrength() {
return redstoneStrength;
}
//
@Override

View file

@ -127,6 +127,8 @@ public class FilterItem extends Item implements MenuProvider {
for (Tag inbt : attributes) {
CompoundTag compound = (CompoundTag) inbt;
ItemAttribute attribute = ItemAttribute.fromNBT(compound);
if (attribute == null)
continue;
boolean inverted = compound.getBoolean("Inverted");
if (count > 3) {
list.add(Components.literal("- ...")
@ -194,15 +196,19 @@ public class FilterItem extends Item implements MenuProvider {
return test(world, stack, filter, true);
}
private static boolean test(Level world, ItemStack stack, ItemStack filter, boolean matchNBT) {
public static boolean test(Level world, ItemStack stack, ItemStack filter, boolean matchNBT) {
if (filter.isEmpty())
return true;
if (!(filter.getItem() instanceof FilterItem))
return (matchNBT ? ItemHandlerHelper.canItemStacksStack(filter, stack) : ItemStack.isSame(filter, stack));
return testDirect(filter, stack, matchNBT);
boolean defaults = !filter.hasTag();
if (defaults) {
return testDirect(filter, stack, matchNBT);
}
if (AllItems.FILTER.get() == filter.getItem()) {
ItemStackHandler filterItems = getFilterItems(filter);
boolean respectNBT = defaults ? false
@ -211,24 +217,32 @@ public class FilterItem extends Item implements MenuProvider {
boolean blacklist = defaults ? false
: filter.getTag()
.getBoolean("Blacklist");
boolean isEmpty = true;
for (int slot = 0; slot < filterItems.getSlots(); slot++) {
ItemStack stackInSlot = filterItems.getStackInSlot(slot);
if (stackInSlot.isEmpty())
continue;
isEmpty = false;
boolean matches = test(world, stack, stackInSlot, respectNBT);
if (matches)
return !blacklist;
}
if (isEmpty) {
return testDirect(filter, stack, matchNBT);
}
return blacklist;
}
if (AllItems.ATTRIBUTE_FILTER.get() == filter.getItem()) {
WhitelistMode whitelistMode = WhitelistMode.values()[defaults ? 0
: filter.getTag()
.getInt("WhitelistMode")];
ListTag attributes = defaults ? new ListTag()
: filter.getTag()
.getList("MatchedAttributes", Tag.TAG_COMPOUND);
if (attributes.isEmpty()) {
return testDirect(filter, stack, matchNBT);
}
WhitelistMode whitelistMode = WhitelistMode.values()[defaults ? 0
: filter.getTag()
.getInt("WhitelistMode")];
for (Tag inbt : attributes) {
CompoundTag compound = (CompoundTag) inbt;
ItemAttribute attribute = ItemAttribute.fromNBT(compound);
@ -270,7 +284,7 @@ public class FilterItem extends Item implements MenuProvider {
return false;
}
private static boolean test(Level world, FluidStack stack, ItemStack filter, boolean matchNBT) {
public static boolean test(Level world, FluidStack stack, ItemStack filter, boolean matchNBT) {
if (filter.isEmpty())
return true;
if (stack.isEmpty())
@ -313,4 +327,12 @@ public class FilterItem extends Item implements MenuProvider {
return false;
}
private static boolean testDirect(ItemStack filter, ItemStack stack, boolean matchNBT) {
if (matchNBT) {
return ItemHandlerHelper.canItemStacksStack(filter, stack);
} else {
return ItemStack.isSame(filter, stack);
}
}
}

View file

@ -10,6 +10,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.processing.InWorldProcessing;
@ -78,6 +79,7 @@ public interface ItemAttribute {
return attributeType;
}
@Nullable
static ItemAttribute fromNBT(CompoundTag nbt) {
for (ItemAttribute itemAttribute : types)
if (itemAttribute.canRead(nbt))

View file

@ -0,0 +1,361 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Vector3f;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.CarriageBogey;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.foundation.block.IBE;
import com.simibubi.create.foundation.block.ProperWaterloggedBlock;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> extends Block implements IBE<T>, ProperWaterloggedBlock, ISpecialBlockItemRequirement, IWrenchable {
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS;
static final List<ResourceLocation> BOGEYS = new ArrayList<>();
public BogeySizes.BogeySize size;
public AbstractBogeyBlock(Properties pProperties, BogeySizes.BogeySize size) {
super(pProperties);
registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false));
this.size = size;
}
public boolean isOnIncompatibleTrack(Carriage carriage, boolean leading) {
TravellingPoint point = leading ? carriage.getLeadingPoint() : carriage.getTrailingPoint();
CarriageBogey bogey = leading ? carriage.leadingBogey() : carriage.trailingBogey();
return point.edge.getTrackMaterial().trackType != getTrackType(bogey.getStyle());
}
public Set<TrackMaterial.TrackType> getValidPathfindingTypes(BogeyStyle style) {
return ImmutableSet.of(getTrackType(style));
}
public abstract TrackMaterial.TrackType getTrackType(BogeyStyle style);
/**
* Only for internal Create use. If you have your own style set, do not call this method
*/
@Deprecated
public static void registerStandardBogey(ResourceLocation block) {
BOGEYS.add(block);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(AXIS, WATERLOGGED);
super.createBlockStateDefinition(builder);
}
@Override
public BlockState updateShape(BlockState pState, Direction pDirection, BlockState pNeighborState,
LevelAccessor pLevel, BlockPos pCurrentPos, BlockPos pNeighborPos) {
updateWater(pLevel, pState, pCurrentPos);
return pState;
}
@Override
public FluidState getFluidState(BlockState pState) {
return fluidState(pState);
}
static final EnumSet<Direction> STICKY_X = EnumSet.of(Direction.EAST, Direction.WEST);
static final EnumSet<Direction> STICKY_Z = EnumSet.of(Direction.SOUTH, Direction.NORTH);
public EnumSet<Direction> getStickySurfaces(BlockGetter world, BlockPos pos, BlockState state) {
return state.getValue(BlockStateProperties.HORIZONTAL_AXIS) == Direction.Axis.X ? STICKY_X : STICKY_Z;
}
public abstract double getWheelPointSpacing();
public abstract double getWheelRadius();
public Vec3 getConnectorAnchorOffset(boolean upsideDown) {
return getConnectorAnchorOffset();
}
/**
* This should be implemented, but not called directly
*/
protected abstract Vec3 getConnectorAnchorOffset();
public boolean allowsSingleBogeyCarriage() {
return true;
}
public abstract BogeyStyle getDefaultStyle();
/**
* Legacy system doesn't capture bogey tile entities when constructing a train
*/
public boolean captureTileEntityForTrain() {
return false;
}
@OnlyIn(Dist.CLIENT)
public void render(@Nullable BlockState state, float wheelAngle, PoseStack ms, float partialTicks,
MultiBufferSource buffers, int light, int overlay, BogeyStyle style, CompoundTag bogeyData) {
if (style == null)
style = getDefaultStyle();
final Optional<BogeyRenderer.CommonRenderer> commonRenderer
= style.getInWorldCommonRenderInstance();
final BogeyRenderer renderer = style.getInWorldRenderInstance(this.getSize());
if (state != null) {
ms.translate(.5f, .5f, .5f);
if (state.getValue(AXIS) == Direction.Axis.X)
ms.mulPose(Vector3f.YP.rotationDegrees(90));
}
ms.translate(0, -1.5 - 1 / 128f, 0);
VertexConsumer vb = buffers.getBuffer(RenderType.cutoutMipped());
if (bogeyData == null)
bogeyData = new CompoundTag();
renderer.render(bogeyData, wheelAngle, ms, light, vb, state == null);
CompoundTag finalBogeyData = bogeyData;
commonRenderer.ifPresent(common ->
common.render(finalBogeyData, wheelAngle, ms, light, vb, state == null));
}
public BogeySizes.BogeySize getSize() {
return this.size;
}
public Direction getBogeyUpDirection() {
return Direction.UP;
}
public boolean isTrackAxisAlongFirstCoordinate(BlockState state) {
return state.getValue(AXIS) == Direction.Axis.X;
}
@Nullable
public BlockState getMatchingBogey(Direction upDirection, boolean axisAlongFirst) {
if (upDirection != Direction.UP)
return null;
return defaultBlockState().setValue(AXIS, axisAlongFirst ? Direction.Axis.X : Direction.Axis.Z);
}
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand,
BlockHitResult hit) {
if (level.isClientSide)
return InteractionResult.PASS;
ItemStack stack = player.getItemInHand(hand);
if (!player.isShiftKeyDown() && stack.is(AllItems.WRENCH.get()) && !player.getCooldowns().isOnCooldown(stack.getItem())
&& AllBogeyStyles.BOGEY_STYLES.size() > 1) {
BlockEntity be = level.getBlockEntity(pos);
if (!(be instanceof AbstractBogeyBlockEntity sbte))
return InteractionResult.FAIL;
player.getCooldowns().addCooldown(stack.getItem(), 20);
BogeyStyle currentStyle = sbte.getStyle();
BogeySizes.BogeySize size = getSize();
BogeyStyle style = this.getNextStyle(currentStyle);
if (style == currentStyle)
return InteractionResult.PASS;
Set<BogeySizes.BogeySize> validSizes = style.validSizes();
for (int i = 0; i < BogeySizes.count(); i++) {
if (validSizes.contains(size)) break;
size = size.increment();
}
sbte.setBogeyStyle(style);
CompoundTag defaultData = style.defaultData;
sbte.setBogeyData(sbte.getBogeyData().merge(defaultData));
if (size == getSize()) {
player.displayClientMessage(Lang.translateDirect("bogey.style.updated_style")
.append(": ").append(style.displayName), true);
} else {
CompoundTag oldData = sbte.getBogeyData();
level.setBlock(pos, this.getStateOfSize(sbte, size), 3);
BlockEntity newBlockEntity = level.getBlockEntity(pos);
if (!(newBlockEntity instanceof AbstractBogeyBlockEntity newTileEntity))
return InteractionResult.FAIL;
newTileEntity.setBogeyData(oldData);
player.displayClientMessage(Lang.translateDirect("bogey.style.updated_style_and_size")
.append(": ").append(style.displayName), true);
}
return InteractionResult.CONSUME;
}
return InteractionResult.PASS;
}
/**
* If, instead of using the style-based cycling system you prefer to use separate blocks, return them from this method
*/
protected List<ResourceLocation> getBogeyBlockCycle() {
return BOGEYS;
}
@Override
public BlockState getRotatedBlockState(BlockState state, Direction targetedFace) {
Block block = state.getBlock();
List<ResourceLocation> bogeyCycle = getBogeyBlockCycle();
int indexOf = bogeyCycle.indexOf(RegisteredObjects.getKeyOrThrow(block));
if (indexOf == -1)
return state;
int index = (indexOf + 1) % bogeyCycle.size();
Direction bogeyUpDirection = getBogeyUpDirection();
boolean trackAxisAlongFirstCoordinate = isTrackAxisAlongFirstCoordinate(state);
while (index != indexOf) {
ResourceLocation id = bogeyCycle.get(index);
Block newBlock = ForgeRegistries.BLOCKS.getValue(id);
if (newBlock instanceof AbstractBogeyBlock<?> bogey) {
BlockState matchingBogey = bogey.getMatchingBogey(bogeyUpDirection, trackAxisAlongFirstCoordinate);
if (matchingBogey != null)
return copyProperties(state, matchingBogey);
}
index = (index + 1) % bogeyCycle.size();
}
return state;
}
public BlockState getNextSize(Level level, BlockPos pos) {
BlockEntity te = level.getBlockEntity(pos);
if (te instanceof AbstractBogeyBlockEntity sbte)
return this.getNextSize(sbte);
return level.getBlockState(pos);
}
/**
* List of BlockState Properties to copy between sizes
*/
public List<Property<?>> propertiesToCopy() {
return ImmutableList.of(WATERLOGGED, AXIS);
}
// generic method needed to satisfy Property and BlockState's generic requirements
private <V extends Comparable<V>> BlockState copyProperty(BlockState source, BlockState target, Property<V> property) {
if (source.hasProperty(property) && target.hasProperty(property)) {
return target.setValue(property, source.getValue(property));
}
return target;
}
private BlockState copyProperties(BlockState source, BlockState target) {
for (Property<?> property : propertiesToCopy())
target = copyProperty(source, target, property);
return target;
}
public BlockState getNextSize(AbstractBogeyBlockEntity sbte) {
BogeySizes.BogeySize size = this.getSize();
BogeyStyle style = sbte.getStyle();
BlockState nextBlock = style.getNextBlock(size).defaultBlockState();
nextBlock = copyProperties(sbte.getBlockState(), nextBlock);
return nextBlock;
}
public BlockState getStateOfSize(AbstractBogeyBlockEntity sbte, BogeySizes.BogeySize size) {
BogeyStyle style = sbte.getStyle();
BlockState state = style.getBlockOfSize(size).defaultBlockState();
return copyProperties(sbte.getBlockState(), state);
}
public BogeyStyle getNextStyle(Level level, BlockPos pos) {
BlockEntity te = level.getBlockEntity(pos);
if (te instanceof AbstractBogeyBlockEntity sbte)
return this.getNextStyle(sbte.getStyle());
return getDefaultStyle();
}
public BogeyStyle getNextStyle(BogeyStyle style) {
Collection<BogeyStyle> allStyles = style.getCycleGroup().values();
if (allStyles.size() <= 1)
return style;
List<BogeyStyle> list = new ArrayList<>(allStyles);
return Iterate.cycleValue(list, style);
}
@Override
public @NotNull BlockState rotate(@NotNull BlockState pState, Rotation pRotation) {
return switch (pRotation) {
case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> pState.cycle(AXIS);
default -> pState;
};
}
@Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity te) {
return new ItemRequirement(ItemRequirement.ItemUseType.CONSUME, AllBlocks.RAILWAY_CASING.asStack());
}
public boolean canBeUpsideDown() {
return false;
}
public boolean isUpsideDown(BlockState state) {
return false;
}
public BlockState getVersion(BlockState base, boolean upsideDown) {
return base;
}
}

View file

@ -26,6 +26,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
@ -42,6 +43,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
public Couple<Integer> smoothing;
public boolean primary;
public boolean hasGirder;
protected TrackMaterial trackMaterial;
// runtime
@ -58,19 +60,20 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
private AABB bounds;
public BezierConnection(Couple<BlockPos> positions, Couple<Vec3> starts, Couple<Vec3> axes, Couple<Vec3> normals,
boolean primary, boolean girder) {
boolean primary, boolean girder, TrackMaterial material) {
tePositions = positions;
this.starts = starts;
this.axes = axes;
this.normals = normals;
this.primary = primary;
this.hasGirder = girder;
this.trackMaterial = material;
resolved = false;
}
public BezierConnection secondary() {
BezierConnection bezierConnection =
new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), !primary, hasGirder);
BezierConnection bezierConnection = new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(),
normals.swap(), !primary, hasGirder, trackMaterial);
if (smoothing != null)
bezierConnection.smoothing = smoothing.swap();
return bezierConnection;
@ -80,6 +83,26 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
return secondary().secondary();
}
private static boolean coupleEquals(Couple<?> a, Couple<?> b) {
return (a.getFirst()
.equals(b.getFirst())
&& a.getSecond()
.equals(b.getSecond()))
|| (a.getFirst() instanceof Vec3 aFirst && a.getSecond() instanceof Vec3 aSecond
&& b.getFirst() instanceof Vec3 bFirst && b.getSecond() instanceof Vec3 bSecond
&& aFirst.closerThan(bFirst, 1e-6) && aSecond.closerThan(bSecond, 1e-6));
}
public boolean equalsSansMaterial(BezierConnection other) {
return equalsSansMaterialInner(other) || equalsSansMaterialInner(other.secondary());
}
private boolean equalsSansMaterialInner(BezierConnection other) {
return this == other || (other != null && coupleEquals(this.tePositions, other.tePositions)
&& coupleEquals(this.starts, other.starts) && coupleEquals(this.axes, other.axes)
&& coupleEquals(this.normals, other.normals) && this.hasGirder == other.hasGirder);
}
public BezierConnection(CompoundTag compound, BlockPos localTo) {
this(Couple.deserializeEach(compound.getList("Positions", Tag.TAG_COMPOUND), NbtUtils::readBlockPos)
.map(b -> b.offset(localTo)),
@ -87,7 +110,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
.map(v -> v.add(Vec3.atLowerCornerOf(localTo))),
Couple.deserializeEach(compound.getList("Axes", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
Couple.deserializeEach(compound.getList("Normals", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
compound.getBoolean("Primary"), compound.getBoolean("Girder"));
compound.getBoolean("Primary"), compound.getBoolean("Girder"), TrackMaterial.deserialize(compound.getString("Material")));
if (compound.contains("Smoothing"))
smoothing =
@ -105,6 +128,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
compound.put("Starts", starts.serializeEach(VecHelper::writeNBTCompound));
compound.put("Axes", axes.serializeEach(VecHelper::writeNBTCompound));
compound.put("Normals", normals.serializeEach(VecHelper::writeNBTCompound));
compound.putString("Material", getMaterial().id.toString());
if (smoothing != null)
compound.put("Smoothing", smoothing.serializeEach(NBTHelper::intToCompound));
@ -115,7 +139,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
public BezierConnection(FriendlyByteBuf buffer) {
this(Couple.create(buffer::readBlockPos), Couple.create(() -> VecHelper.read(buffer)),
Couple.create(() -> VecHelper.read(buffer)), Couple.create(() -> VecHelper.read(buffer)),
buffer.readBoolean(), buffer.readBoolean());
buffer.readBoolean(), buffer.readBoolean(), TrackMaterial.deserialize(buffer.readUtf()));
if (buffer.readBoolean())
smoothing = Couple.create(buffer::readVarInt);
}
@ -127,6 +151,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
normals.forEach(v -> VecHelper.write(v, buffer));
buffer.writeBoolean(primary);
buffer.writeBoolean(hasGirder);
buffer.writeUtf(getMaterial().id.toString());
buffer.writeBoolean(smoothing != null);
if (smoothing != null)
smoothing.forEach(buffer::writeVarInt);
@ -333,7 +358,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
Inventory inv = player.getInventory();
int tracks = getTrackItemCost();
while (tracks > 0) {
inv.placeItemBackInInventory(AllBlocks.TRACK.asStack(Math.min(64, tracks)));
inv.placeItemBackInInventory(new ItemStack(getMaterial().getBlock(), Math.min(64, tracks)));
tracks -= 64;
}
int girders = getGirderItemCost();
@ -361,7 +386,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
continue;
Vec3 v = VecHelper.offsetRandomly(segment.position, level.random, .125f)
.add(origin);
ItemEntity entity = new ItemEntity(level, v.x, v.y, v.z, AllBlocks.TRACK.asStack());
ItemEntity entity = new ItemEntity(level, v.x, v.y, v.z, getMaterial().asStack());
entity.setDefaultPickUpDelay();
level.addFreshEntity(entity);
if (!hasGirder)
@ -375,7 +400,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
}
public void spawnDestroyParticles(Level level) {
BlockParticleOption data = new BlockParticleOption(ParticleTypes.BLOCK, AllBlocks.TRACK.getDefaultState());
BlockParticleOption data = new BlockParticleOption(ParticleTypes.BLOCK, getMaterial().getBlock().defaultBlockState());
BlockParticleOption girderData =
new BlockParticleOption(ParticleTypes.BLOCK, AllBlocks.METAL_GIRDER.getDefaultState());
if (!(level instanceof ServerLevel slevel))
@ -393,6 +418,14 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
}
}
public TrackMaterial getMaterial() {
return trackMaterial;
}
public void setMaterial(TrackMaterial material) {
trackMaterial = material;
}
public static class Segment {
public int index;

View file

@ -1,7 +1,7 @@
package com.simibubi.create.content.logistics.trains;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyBlockEntity;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
@ -17,11 +17,11 @@ public class BogeyBlockEntityRenderer<T extends BlockEntity> extends SafeBlockEn
protected void renderSafe(T be, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light,
int overlay) {
BlockState blockState = be.getBlockState();
float angle = 0;
if (be instanceof StandardBogeyBlockEntity sbte)
angle = sbte.getVirtualAngle(partialTicks);
if (blockState.getBlock()instanceof IBogeyBlock bogey)
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay);
if (be instanceof AbstractBogeyBlockEntity sbte) {
float angle = sbte.getVirtualAngle(partialTicks);
if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey)
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay, sbte.getStyle(), sbte.getBogeyData());
}
}
}

View file

@ -0,0 +1,309 @@
package com.simibubi.create.content.logistics.trains;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public abstract class BogeyRenderer {
Map<String, ModelData[]> contraptionModelData = new HashMap<>();
/**
* A common interface for getting transform data for both in-world and in-contraption model data safely from a
* partial model
*
* @param model The key for the model data to instantiate or retrieve
* @param ms The posestack used for contraption model data
* @param inInstancedContraption The type of model needed
* @param size The amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?>[] getTransformsFromPartial(PartialModel model, PoseStack ms, boolean inInstancedContraption, int size) {
return (inInstancedContraption) ? transformContraptionModelData(keyFromModel(model), ms) : createModelData(model, size);
}
/**
* A common interface for getting transform data for both in-world and in-contraption model data safely from a
* blockstate
*
* @param state The key for the model data to instantiate or retrieve
* @param ms The posestack used for contraption model data
* @param inContraption The type of model needed
* @param size The amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?>[] getTransformsFromBlockState(BlockState state, PoseStack ms, boolean inContraption, int size) {
return inContraption ? transformContraptionModelData(keyFromModel(state), ms) : createModelData(state, size);
}
/**
* Used for calling both in-world and in-contraption rendering
*
* @param bogeyData Custom data stored on the bogey able to be used for rendering
* @param wheelAngle The angle of the wheel
* @param ms The posestack to render to
* @param light (Optional) Light used for in-world rendering
* @param vb (Optional) Vertex Consumer used for in-world rendering
*/
@OnlyIn(Dist.CLIENT)
public abstract void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption);
/**
* Used for calling in-contraption rendering ensuring that falsey data is handled correctly
*
* @param bogeyData Custom data stored on the bogey able to be used for rendering
* @param wheelAngle The angle of the wheel
* @param ms The posestack to render to
*/
@OnlyIn(Dist.CLIENT)
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms) {
this.render(bogeyData, wheelAngle, ms, 0, null, true);
}
public abstract BogeySizes.BogeySize getSize();
/**
* Used to collect Contraption Model Data for in-contraption rendering, should not be utilised directly when
* rendering to prevent render type mismatch
*
* @param key The key used to access the model
* @param ms Posestack of the contraption to bind the model data to
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] transformContraptionModelData(String key, PoseStack ms) {
ModelData[] modelData = contraptionModelData.get(key);
Arrays.stream(modelData).forEach(modelDataElement -> modelDataElement.setTransform(ms));
return modelData;
}
/**
* Used for in world rendering, creates a set count of model data to be rendered, allowing for a generic response
* when rendering multiple models both in-world and in-contraption for example, with wheels
*
* @param model The partial model of the model data ot be made
* @param size The Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] createModelData(PartialModel model, int size) {
BlockState air = Blocks.AIR.defaultBlockState();
SuperByteBuffer[] data = { CachedBufferer.partial(model, air) };
return expandArrayToLength(data, size);
}
/**
* Used for in world rendering, creates a set count of model data to be rendered, allowing for a generic response
* when rendering multiple models both in-world and in-contraption for example, with wheels
*
* @param state The state of the model data to be made
* @param size Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] createModelData(BlockState state, int size) {
SuperByteBuffer[] data = { CachedBufferer.block(state) };
return expandArrayToLength(data, size);
}
/**
* Utility function to clone in-world models to a set size to allow for common handling of rendering with multiple
* instances of the same model for example with wheels
*
* @param data An in-world model to be replicated
* @param size Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] expandArrayToLength(SuperByteBuffer[] data, int size) {
return Arrays.stream(Collections.nCopies(size, data).toArray())
.flatMap(inner -> Arrays.stream((SuperByteBuffer[]) inner))
.toArray(SuperByteBuffer[]::new);
}
/**
* Helper function to collect or create a single model from a partial model used for both in-world and
* in-contraption rendering
*
* @param model The key of the model to be collected or instantiated
* @param ms Posestack to bind the model to if it is within a contraption
* @param inInstancedContraption Type of rendering required
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?> getTransformFromPartial(PartialModel model, PoseStack ms, boolean inInstancedContraption) {
BlockState air = Blocks.AIR.defaultBlockState();
return inInstancedContraption ? contraptionModelData.get(keyFromModel(model))[0].setTransform(ms)
: CachedBufferer.partial(model, air);
}
/**
* A common interface for getting transform data for blockstates, for a single model
*
* @param state The state of the model to be collected or instantiated
* @param ms Posestack to bind the model to if it is within a contraption
* @param inContraption Type of model required
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?> getTransformFromBlockState(BlockState state, PoseStack ms, boolean inContraption) {
return (inContraption) ? contraptionModelData.get(keyFromModel(state))[0].setTransform(ms)
: CachedBufferer.block(state);
}
/**
* Provides render implementations a point in setup to instantiate all model data to be needed
*
* @param materialManager The material manager
*/
@OnlyIn(Dist.CLIENT)
public abstract void initialiseContraptionModelData(MaterialManager materialManager);
/**
* Creates instances of models for in-world rendering to a set length from a provided partial model
*
* @param materialManager The material manager
* @param model Partial model to be instanced
* @param count Amount of models neeeded
*/
public void createModelInstances(MaterialManager materialManager, PartialModel model, int count) {
ModelData[] modelData = new ModelData[count];
materialManager.defaultSolid().material(Materials.TRANSFORMED)
.getModel(model).createInstances(modelData);
contraptionModelData.put(keyFromModel(model), modelData);
}
/**
* Creates instances of models for in-contraption rendering to a set length from a provided blockstate
*
* @param materialManager The material manager
* @param state Blockstate of the model to be created
* @param count Amount of models needed
*/
public void createModelInstances(MaterialManager materialManager, BlockState state, int count) {
ModelData[] modelData = new ModelData[count];
materialManager.defaultSolid().material(Materials.TRANSFORMED)
.getModel(state).createInstances(modelData);
contraptionModelData.put(keyFromModel(state), modelData);
}
/**
* Creates a single instance of models for in-contraption rendering from a provided blockstate
*
* @param materialManager The material manager
* @param state Blockstate of the model to be created
*/
public void createModelInstance(MaterialManager materialManager, BlockState state) {
this.createModelInstances(materialManager, state, 1);
}
/**
* Helper function to create a single model instance for in-contraption rendering
*
* @param materialManager The material manager
* @param models The type of model to create instances of
*/
public void createModelInstances(MaterialManager materialManager, PartialModel... models) {
for (PartialModel model : models)
createModelInstances(materialManager, model, 1);
}
/**
* Handles scale for all model data and renders non contraption model data
*
* @param b The model data itself
* @param ms Pose stack to render to
* @param light light level of the scene
* @param vb Vertex Consumber to render to
* @param <B> Generic alias for both contraption and in-world model data
*/
public static <B extends Transform<?>> void finalize(B b, PoseStack ms, int light, @Nullable VertexConsumer vb) {
b.scale(1 - 1/512f);
if (b instanceof SuperByteBuffer byteBuf && vb != null)
byteBuf.light(light).renderInto(ms, vb);
}
/**
* Automatic handling for setting empty transforms for all model data
*
*/
public void emptyTransforms() {
for (ModelData[] data : contraptionModelData.values())
for (ModelData model : data)
model.setEmptyTransform();
}
/**
* Automatic handling for updating all model data's light
*
* @param blockLight the blocklight to be applied
* @param skyLight the skylight to be applied
*/
public void updateLight(int blockLight, int skyLight) {
for (ModelData[] data : contraptionModelData.values())
for (ModelData model : data)
model.setBlockLight(blockLight).setSkyLight(skyLight);
}
/**
* Automatic handling for clearing all model data of a contraption
*
*/
public void remove() {
for (ModelData[] data : contraptionModelData.values())
for (ModelData model : data)
model.delete();
contraptionModelData.clear();
}
/**
* Create a model key from a partial model, so it can be easily accessed
*
* @param partialModel the model we want a unique key for
* @return Key of the model
*/
private String keyFromModel(PartialModel partialModel) {
return partialModel.getLocation().toString();
}
/**
* Create a model key from a blockstate, so it can be easily accessed
*
* @param state Blockstate of the model
* @return Key of the model
*/
private String keyFromModel(BlockState state) {
return state.toString();
}
public static abstract class CommonRenderer extends BogeyRenderer {
@Override
public BogeySizes.BogeySize getSize() {
return null;
}
}
}

View file

@ -0,0 +1,70 @@
package com.simibubi.create.content.logistics.trains;
import com.simibubi.create.Create;
import net.minecraft.resources.ResourceLocation;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
public class BogeySizes {
private static final Collection<BogeySize> BOGEY_SIZES = new HashSet<>();
public static final BogeySize SMALL = new BogeySize(Create.ID, "small", 6.5f / 16f);
public static final BogeySize LARGE = new BogeySize(Create.ID, "large", 12.5f / 16f);
static {
BOGEY_SIZES.add(SMALL);
BOGEY_SIZES.add(LARGE);
}
public static BogeySize addSize(String modId, String name, float size) {
ResourceLocation location = new ResourceLocation(modId, name);
return addSize(location, size);
}
public static BogeySize addSize(ResourceLocation location, float size) {
BogeySize customSize = new BogeySize(location, size);
BOGEY_SIZES.add(customSize);
return customSize;
}
public static List<BogeySize> getAllSizesSmallToLarge() {
return BOGEY_SIZES.stream()
.sorted(Comparator.comparing(BogeySize::wheelRadius))
.collect(Collectors.toList());
}
public static List<BogeySize> getAllSizesLargeToSmall() {
List<BogeySize> sizes = getAllSizesSmallToLarge();
Collections.reverse(sizes);
return sizes;
}
public static int count() {
return BOGEY_SIZES.size();
}
public record BogeySize(ResourceLocation location, float wheelRadius) {
public BogeySize(String modId, String name, float wheelRadius) {
this(new ResourceLocation(modId, name), wheelRadius);
}
public BogeySize increment() {
List<BogeySize> values = getAllSizesSmallToLarge();
int ordinal = values.indexOf(this);
return values.get((ordinal + 1) % values.size());
}
public boolean is(BogeySize size) {
return size.location == this.location;
}
}
public static void init() {
}
}

View file

@ -265,13 +265,17 @@ public class GlobalRailwayManager {
public void clientTick() {
if (isTrackGraphDebugActive())
for (TrackGraph trackGraph : trackNetworks.values())
TrackGraphVisualizer.debugViewGraph(trackGraph);
TrackGraphVisualizer.debugViewGraph(trackGraph, isTrackGraphDebugExtended());
}
private static boolean isTrackGraphDebugActive() {
return KineticDebugger.isF3DebugModeActive() && AllConfigs.client().showTrackGraphOnF3.get();
}
private static boolean isTrackGraphDebugExtended() {
return AllConfigs.client().showExtendedTrackGraphOnF3.get();
}
public GlobalRailwayManager sided(LevelAccessor level) {
if (level != null && !level.isClientSide())
return this;

View file

@ -1,91 +0,0 @@
package com.simibubi.create.content.logistics.trains;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.logistics.trains.entity.BogeyInstance;
import com.simibubi.create.content.logistics.trains.entity.CarriageBogey;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
public interface IBogeyBlock extends IWrenchable {
static final List<ResourceLocation> BOGEYS = new ArrayList<>();
public static void register(ResourceLocation block) {
BOGEYS.add(block);
}
public EnumSet<Direction> getStickySurfaces(BlockGetter world, BlockPos pos, BlockState state);
public double getWheelPointSpacing();
public double getWheelRadius();
public boolean allowsSingleBogeyCarriage();
public Vec3 getConnectorAnchorOffset();
@OnlyIn(Dist.CLIENT)
public void render(@Nullable BlockState state, float wheelAngle, PoseStack ms, float partialTicks,
MultiBufferSource buffers, int light, int overlay);
@OnlyIn(Dist.CLIENT)
public BogeyInstance createInstance(MaterialManager materialManager, CarriageBogey bogey);
public default Direction getBogeyUpDirection() {
return Direction.UP;
}
public boolean isTrackAxisAlongFirstCoordinate(BlockState state);
@Nullable
public BlockState getMatchingBogey(Direction upDirection, boolean axisAlongFirst);
@Override
default BlockState getRotatedBlockState(BlockState state, Direction targetedFace) {
Block block = state.getBlock();
int indexOf = BOGEYS.indexOf(RegisteredObjects.getKeyOrThrow(block));
if (indexOf == -1)
return state;
int index = (indexOf + 1) % BOGEYS.size();
Direction bogeyUpDirection = getBogeyUpDirection();
boolean trackAxisAlongFirstCoordinate = isTrackAxisAlongFirstCoordinate(state);
while (index != indexOf) {
ResourceLocation id = BOGEYS.get(index);
Block newBlock = ForgeRegistries.BLOCKS.getValue(id);
if (newBlock instanceof IBogeyBlock bogey) {
BlockState matchingBogey = bogey.getMatchingBogey(bogeyUpDirection, trackAxisAlongFirstCoordinate);
if (matchingBogey != null)
return matchingBogey.hasProperty(WATERLOGGED)
? matchingBogey.setValue(WATERLOGGED, state.getValue(WATERLOGGED))
: matchingBogey;
}
index = (index + 1) % BOGEYS.size();
}
return state;
}
}

View file

@ -25,6 +25,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
@ -85,20 +86,38 @@ public interface ITrackBlock {
Function<Vec3, Integer> yOffsetFactory = v -> getYOffsetAt(world, pos, state, v);
addToListIfConnected(connectedTo, list, offsetFactory, b -> shape.getNormal(), dimensionFactory,
yOffsetFactory, axis, null);
yOffsetFactory, axis, null, (b, v) -> getMaterialSimple(world, v));
});
return list;
}
public static TrackMaterial getMaterialSimple(BlockGetter world, Vec3 pos) {
return getMaterialSimple(world, pos, TrackMaterial.ANDESITE);
}
public static TrackMaterial getMaterialSimple(BlockGetter world, Vec3 pos, TrackMaterial defaultMaterial) {
if (defaultMaterial == null)
defaultMaterial = TrackMaterial.ANDESITE;
if (world != null) {
Block block = world.getBlockState(new BlockPos(pos)).getBlock();
if (block instanceof ITrackBlock track) {
return track.getMaterial();
}
}
return defaultMaterial;
}
public static void addToListIfConnected(@Nullable TrackNodeLocation fromEnd, Collection<DiscoveredLocation> list,
BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory,
Function<Boolean, ResourceKey<Level>> dimensionFactory, Function<Vec3, Integer> yOffsetFactory, Vec3 axis,
BezierConnection viaTurn) {
BezierConnection viaTurn, BiFunction<Boolean, Vec3, TrackMaterial> materialFactory) {
Vec3 firstOffset = offsetFactory.apply(0.5d, true);
DiscoveredLocation firstLocation =
new DiscoveredLocation(dimensionFactory.apply(true), firstOffset).viaTurn(viaTurn)
.materialA(materialFactory.apply(true, offsetFactory.apply(0.0d, true)))
.materialB(materialFactory.apply(true, offsetFactory.apply(1.0d, true)))
.withNormal(normalFactory.apply(true))
.withDirection(axis)
.withYOffset(yOffsetFactory.apply(firstOffset));
@ -106,6 +125,8 @@ public interface ITrackBlock {
Vec3 secondOffset = offsetFactory.apply(0.5d, false);
DiscoveredLocation secondLocation =
new DiscoveredLocation(dimensionFactory.apply(false), secondOffset).viaTurn(viaTurn)
.materialA(materialFactory.apply(false, offsetFactory.apply(0.0d, false)))
.materialB(materialFactory.apply(false, offsetFactory.apply(1.0d, false)))
.withNormal(normalFactory.apply(false))
.withDirection(axis)
.withYOffset(yOffsetFactory.apply(secondOffset));
@ -169,4 +190,6 @@ public interface ITrackBlock {
.normalize()) < 0 ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE);
}
TrackMaterial getMaterial();
}

View file

@ -0,0 +1,134 @@
package com.simibubi.create.content.logistics.trains;
import static com.simibubi.create.AllPartialModels.BOGEY_DRIVE;
import static com.simibubi.create.AllPartialModels.BOGEY_FRAME;
import static com.simibubi.create.AllPartialModels.BOGEY_PIN;
import static com.simibubi.create.AllPartialModels.BOGEY_PISTON;
import static com.simibubi.create.AllPartialModels.LARGE_BOGEY_WHEELS;
import static com.simibubi.create.AllPartialModels.SMALL_BOGEY_WHEELS;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
public class StandardBogeyRenderer {
public static class CommonStandardBogeyRenderer extends BogeyRenderer.CommonRenderer {
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
createModelInstances(materialManager, AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z), 2);
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
Transform<?>[] shafts = getTransformsFromBlockState(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z), ms, inInstancedContraption, 2);
for (int i : Iterate.zeroAndOne) {
shafts[i].translate(-.5f, .25f, i * -1)
.centre()
.rotateZ(wheelAngle)
.unCentre();
finalize(shafts[i], ms, light, vb);
}
}
}
public static class SmallStandardBogeyRenderer extends BogeyRenderer {
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
createModelInstances(materialManager, SMALL_BOGEY_WHEELS, 2);
createModelInstances(materialManager, BOGEY_FRAME);
}
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.SMALL;
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
Transform<?> transform = getTransformFromPartial(BOGEY_FRAME, ms, inInstancedContraption);
finalize(transform, ms, light, vb);
Transform<?>[] wheels = getTransformsFromPartial(SMALL_BOGEY_WHEELS, ms, inInstancedContraption, 2);
for (int side : Iterate.positiveAndNegative) {
if (!inInstancedContraption)
ms.pushPose();
Transform<?> wheel = wheels[(side + 1)/2];
wheel.translate(0, 12 / 16f, side)
.rotateX(wheelAngle);
finalize(wheel, ms, light, vb);
if (!inInstancedContraption)
ms.popPose();
}
}
}
public static class LargeStandardBogeyRenderer extends BogeyRenderer {
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
createModelInstances(materialManager, LARGE_BOGEY_WHEELS, BOGEY_DRIVE, BOGEY_PISTON, BOGEY_PIN);
createModelInstances(materialManager, AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X), 2);
}
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.LARGE;
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
Transform<?>[] secondaryShafts = getTransformsFromBlockState(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X), ms, inInstancedContraption, 2);
for (int i : Iterate.zeroAndOne) {
Transform<?> secondShaft = secondaryShafts[i];
secondShaft.translate(-.5f, .25f, .5f + i * -2)
.centre()
.rotateX(wheelAngle)
.unCentre();
finalize(secondShaft, ms, light, vb);
}
Transform<?> bogeyDrive = getTransformFromPartial(BOGEY_DRIVE, ms, inInstancedContraption);
finalize(bogeyDrive, ms, light, vb);
Transform<?> bogeyPiston = getTransformFromPartial(BOGEY_PISTON, ms, inInstancedContraption)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)));
finalize(bogeyPiston, ms, light, vb);
if (!inInstancedContraption)
ms.pushPose();
Transform<?> bogeyWheels = getTransformFromPartial(LARGE_BOGEY_WHEELS, ms, inInstancedContraption)
.translate(0, 1, 0)
.rotateX(wheelAngle);
finalize(bogeyWheels, ms, light, vb);
Transform<?> bogeyPin = getTransformFromPartial(BOGEY_PIN, ms, inInstancedContraption)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateX(-wheelAngle);
finalize(bogeyPin, ms, light, vb);
if (!inInstancedContraption)
ms.popPose();
}
}
}

View file

@ -24,13 +24,19 @@ public class TrackEdge {
BezierConnection turn;
EdgeData edgeData;
boolean interDimensional;
TrackMaterial trackMaterial;
public TrackEdge(TrackNode node1, TrackNode node2, BezierConnection turn) {
public TrackEdge(TrackNode node1, TrackNode node2, BezierConnection turn, TrackMaterial trackMaterial) {
this.interDimensional = !node1.location.dimension.equals(node2.location.dimension);
this.edgeData = new EdgeData(this);
this.node1 = node1;
this.node2 = node2;
this.turn = turn;
this.trackMaterial = trackMaterial;
}
public TrackMaterial getTrackMaterial() {
return trackMaterial;
}
public boolean isTurn() {
@ -230,13 +236,15 @@ public class TrackEdge {
public CompoundTag write(DimensionPalette dimensions) {
CompoundTag baseCompound = isTurn() ? turn.write(BlockPos.ZERO) : new CompoundTag();
baseCompound.put("Signals", edgeData.write(dimensions));
baseCompound.putString("Material", getTrackMaterial().id.toString());
return baseCompound;
}
public static TrackEdge read(TrackNode node1, TrackNode node2, CompoundTag tag, TrackGraph graph,
DimensionPalette dimensions) {
TrackEdge trackEdge =
new TrackEdge(node1, node2, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null);
new TrackEdge(node1, node2, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null,
TrackMaterial.deserialize(tag.getString("Material")));
trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), trackEdge, graph, dimensions);
return trackEdge;
}

View file

@ -393,14 +393,15 @@ public class TrackGraph {
return connectionsFrom.get(nodes.getSecond());
}
public void connectNodes(LevelAccessor reader, TrackNodeLocation location, TrackNodeLocation location2,
public void connectNodes(LevelAccessor reader, DiscoveredLocation location, DiscoveredLocation location2,
@Nullable BezierConnection turn) {
TrackNode node1 = nodes.get(location);
TrackNode node2 = nodes.get(location2);
boolean bezier = turn != null;
TrackEdge edge = new TrackEdge(node1, node2, turn);
TrackEdge edge2 = new TrackEdge(node2, node1, bezier ? turn.secondary() : null);
TrackMaterial material = bezier ? turn.getMaterial() : location2.materialA;
TrackEdge edge = new TrackEdge(node1, node2, turn, material);
TrackEdge edge2 = new TrackEdge(node2, node1, bezier ? turn.secondary() : null, material);
for (TrackGraph graph : Create.RAILWAYS.trackNetworks.values()) {
for (TrackNode otherNode1 : graph.nodes.values()) {

View file

@ -60,7 +60,7 @@ public class TrackGraphSync {
public void edgeAdded(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge) {
flushGraphPacket(graph);
currentGraphSyncPacket.addedEdges
.add(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge.getTurn()));
.add(Pair.of(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge.getTrackMaterial()), edge.getTurn()));
currentPayload++;
}
@ -82,7 +82,7 @@ public class TrackGraphSync {
if (currentGraphSyncPacket.addedNodes.remove(nodeId) == null)
currentGraphSyncPacket.removedNodes.add(nodeId);
currentGraphSyncPacket.addedEdges.removeIf(pair -> {
Couple<Integer> ids = pair.getFirst();
Couple<Integer> ids = pair.getFirst().getFirst();
return ids.getFirst()
.intValue() == nodeId
|| ids.getSecond()
@ -156,7 +156,7 @@ public class TrackGraphSync {
graph.connectionsByNode.get(node)
.forEach((node2, edge) -> {
Couple<Integer> key = Couple.create(node.getNetId(), node2.getNetId());
currentPacket.addedEdges.add(Pair.of(key, edge.getTurn()));
currentPacket.addedEdges.add(Pair.of(Pair.of(key, edge.getTrackMaterial()), edge.getTurn()));
currentPacket.syncEdgeData(node, node2, edge);
});

View file

@ -22,7 +22,7 @@ import net.minecraft.world.phys.Vec3;
public class TrackGraphSyncPacket extends TrackGraphPacket {
Map<Integer, Pair<TrackNodeLocation, Vec3>> addedNodes;
List<Pair<Couple<Integer>, BezierConnection>> addedEdges;
List<Pair<Pair<Couple<Integer>, TrackMaterial>, BezierConnection>> addedEdges;
List<Integer> removedNodes;
List<TrackEdgePoint> addedEdgePoints;
List<UUID> removedEdgePoints;
@ -79,7 +79,7 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedEdges.add(
Pair.of(Couple.create(buffer::readVarInt), buffer.readBoolean() ? new BezierConnection(buffer) : null));
Pair.of(Pair.of(Couple.create(buffer::readVarInt), TrackMaterial.deserialize(buffer.readUtf())), buffer.readBoolean() ? new BezierConnection(buffer) : null));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
@ -134,8 +134,9 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
buffer.writeVarInt(addedEdges.size());
addedEdges.forEach(pair -> {
pair.getFirst()
pair.getFirst().getFirst()
.forEach(buffer::writeVarInt);
buffer.writeUtf(pair.getFirst().getSecond().id.toString());
BezierConnection turn = pair.getSecond();
buffer.writeBoolean(turn != null);
if (turn != null)
@ -192,13 +193,13 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
graph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond());
}
for (Pair<Couple<Integer>, BezierConnection> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst()
for (Pair<Pair<Couple<Integer>, TrackMaterial>, BezierConnection> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst().getFirst()
.map(graph::getNode);
TrackNode node1 = nodes.getFirst();
TrackNode node2 = nodes.getSecond();
if (node1 != null && node2 != null)
graph.putConnection(node1, node2, new TrackEdge(node1, node2, pair.getSecond()));
graph.putConnection(node1, node2, new TrackEdge(node1, node2, pair.getSecond(), pair.getFirst().getSecond()));
}
for (TrackEdgePoint edgePoint : addedEdgePoints)

View file

@ -37,7 +37,6 @@ public class TrackGraphVisualizer {
Vec3 camera = cameraEntity.getEyePosition();
Outliner outliner = CreateClient.OUTLINER;
boolean ctrl = false; // AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.sided(null).signalEdgeGroups;
float width = 1 / 8f;
@ -213,7 +212,7 @@ public class TrackGraphVisualizer {
}
}
public static void debugViewGraph(TrackGraph graph) {
public static void debugViewGraph(TrackGraph graph, boolean extended) {
Minecraft mc = Minecraft.getInstance();
Entity cameraEntity = mc.cameraEntity;
if (cameraEntity == null)
@ -266,6 +265,17 @@ public class TrackGraphVisualizer {
yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
if (!edge.isTurn()) {
if (extended) {
Vec3 materialPos = edge.getPosition(graph, 0.5)
.add(0, 1, 0);
CreateClient.OUTLINER.showItem(Pair.of(edge, edge.edgeData), materialPos,
edge.getTrackMaterial()
.asStack());
CreateClient.OUTLINER.showAABB(edge.edgeData, AABB.ofSize(materialPos, .25, 0, .25)
.move(0, -0.5, 0))
.lineWidth(1 / 16f)
.colored(graph.color);
}
CreateClient.OUTLINER.showLine(edge, edge.getPosition(graph, 0)
.add(yOffset),
edge.getPosition(graph, 1)
@ -277,6 +287,16 @@ public class TrackGraphVisualizer {
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
if (extended) {
Vec3 materialPos = edge.getPosition(graph, 0.5)
.add(0, 1, 0);
CreateClient.OUTLINER.showItem(Pair.of(edge, edge.edgeData), materialPos, edge.getTrackMaterial()
.asStack());
CreateClient.OUTLINER.showAABB(edge.edgeData, AABB.ofSize(materialPos, .25, 0, .25)
.move(0, -0.5, 0))
.lineWidth(1 / 16f)
.colored(graph.color);
}
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(graph, i * 1f / turn.getSegmentCount());
if (previous != null)

View file

@ -0,0 +1,171 @@
package com.simibubi.create.content.logistics.trains;
import static com.simibubi.create.content.logistics.trains.TrackMaterialFactory.make;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.PartialModel;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public class TrackMaterial {
public static final Map<ResourceLocation, TrackMaterial> ALL = new HashMap<>();
public static final TrackMaterial ANDESITE = make(Create.asResource("andesite"))
.lang("Andesite")
.block(NonNullSupplier.lazy(() -> AllBlocks.TRACK))
.particle(Create.asResource("block/palettes/stone_types/polished/andesite_cut_polished"))
.defaultModels()
.build();
public final ResourceLocation id;
public final String langName;
public final NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock;
public final Ingredient sleeperIngredient;
public final Ingredient railsIngredient;
public final ResourceLocation particle;
public final TrackType trackType;
@Nullable
private final TrackMaterial.TrackType.TrackBlockFactory customFactory;
@OnlyIn(Dist.CLIENT)
protected TrackModelHolder modelHolder;
@OnlyIn(Dist.CLIENT)
public TrackModelHolder getModelHolder() {
return modelHolder;
}
public TrackMaterial(ResourceLocation id, String langName, NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock,
ResourceLocation particle, Ingredient sleeperIngredient, Ingredient railsIngredient,
TrackType trackType, Supplier<Supplier<TrackModelHolder>> modelHolder) {
this(id, langName, trackBlock, particle, sleeperIngredient, railsIngredient, trackType, modelHolder, null);
}
public TrackMaterial(ResourceLocation id, String langName, NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock,
ResourceLocation particle, Ingredient sleeperIngredient, Ingredient railsIngredient,
TrackType trackType, Supplier<Supplier<TrackModelHolder>> modelHolder,
@Nullable TrackType.TrackBlockFactory customFactory) {
this.id = id;
this.langName = langName;
this.trackBlock = trackBlock;
this.sleeperIngredient = sleeperIngredient;
this.railsIngredient = railsIngredient;
this.particle = particle;
this.trackType = trackType;
this.customFactory = customFactory;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> this.modelHolder = modelHolder.get().get());
ALL.put(this.id, this);
}
public NonNullSupplier<? extends TrackBlock> getBlockSupplier() {
return this.trackBlock.get();
}
public TrackBlock getBlock() {
return getBlockSupplier().get();
}
public ItemStack asStack() {
return asStack(1);
}
public ItemStack asStack(int count) {
return new ItemStack(getBlock(), count);
}
public TrackBlock createBlock(BlockBehaviour.Properties properties) {
return (this.customFactory != null ? this.customFactory : this.trackType.factory)
.create(properties, this);
}
public boolean isFromMod(String modId) {
return this.id.getNamespace().equals(modId);
}
public static List<TrackMaterial> allFromMod(String modid) {
return ALL.values().stream().filter(tm -> tm.isFromMod(modid)).toList();
}
public static List<NonNullSupplier<? extends Block>> allBlocksFromMod(String modid) {
List<NonNullSupplier<? extends Block>> list = new ArrayList<>();
for (TrackMaterial material : allFromMod(modid)) {
list.add(material.getBlockSupplier());
}
return list;
}
public static List<NonNullSupplier<? extends Block>> allBlocks() {
List<NonNullSupplier<? extends Block>> list = new ArrayList<>();
for (TrackMaterial material : ALL.values()) {
list.add(material.getBlockSupplier());
}
return list;
}
public String resourceName() {
return this.id.getPath();
}
public static TrackMaterial deserialize(String serializedName) {
if (serializedName.isBlank()) // Data migrating from 0.5
return ANDESITE;
ResourceLocation id = ResourceLocation.tryParse(serializedName);
if (ALL.containsKey(id))
return ALL.get(id);
Create.LOGGER.error("Failed to locate serialized track material: " + serializedName);
return ANDESITE;
}
public static class TrackType {
@FunctionalInterface
public interface TrackBlockFactory {
TrackBlock create(BlockBehaviour.Properties properties, TrackMaterial material);
}
public static final TrackType STANDARD = new TrackType(Create.asResource("standard"), TrackBlock::new);
public final ResourceLocation id;
protected final TrackBlockFactory factory;
public TrackType(ResourceLocation id, TrackBlockFactory factory) {
this.id = id;
this.factory = factory;
}
}
public static TrackMaterial fromItem(Item item) {
if (item instanceof BlockItem blockItem && blockItem.getBlock() instanceof ITrackBlock trackBlock)
return trackBlock.getMaterial();
return TrackMaterial.ANDESITE;
}
@OnlyIn(Dist.CLIENT)
public record TrackModelHolder(PartialModel tie, PartialModel segment_left, PartialModel segment_right) {
static final TrackModelHolder DEFAULT = new TrackModelHolder(AllPartialModels.TRACK_TIE,
AllPartialModels.TRACK_SEGMENT_LEFT, AllPartialModels.TRACK_SEGMENT_RIGHT);
}
}

View file

@ -0,0 +1,142 @@
package com.simibubi.create.content.logistics.trains;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.PartialModel;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.ItemLike;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public class TrackMaterialFactory {
private final ResourceLocation id;
private String langName;
private NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock;
private Ingredient sleeperIngredient = Ingredient.EMPTY;
private Ingredient railsIngredient = Ingredient.fromValues(Stream.of(new Ingredient.TagValue(AllTags.forgeItemTag("nuggets/iron")), new Ingredient.TagValue(AllTags.forgeItemTag("nuggets/zinc"))));
private ResourceLocation particle;
private TrackMaterial.TrackType trackType = TrackMaterial.TrackType.STANDARD;
@Nullable
private TrackMaterial.TrackType.TrackBlockFactory customFactory = null;
@OnlyIn(Dist.CLIENT)
private TrackMaterial.TrackModelHolder modelHolder;
@OnlyIn(Dist.CLIENT)
private PartialModel tieModel;
@OnlyIn(Dist.CLIENT)
private PartialModel leftSegmentModel;
@OnlyIn(Dist.CLIENT)
private PartialModel rightSegmentModel;
public TrackMaterialFactory(ResourceLocation id) {
this.id = id;
}
public static TrackMaterialFactory make(ResourceLocation id) { // Convenience function for static import
return new TrackMaterialFactory(id);
}
public TrackMaterialFactory lang(String langName) {
this.langName = langName;
return this;
}
public TrackMaterialFactory block(NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock) {
this.trackBlock = trackBlock;
return this;
}
public TrackMaterialFactory defaultModels() { // was setBuiltin
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> this.modelHolder = TrackMaterial.TrackModelHolder.DEFAULT);
return this;
}
public TrackMaterialFactory sleeper(Ingredient sleeperIngredient) {
this.sleeperIngredient = sleeperIngredient;
return this;
}
public TrackMaterialFactory sleeper(ItemLike... items) {
this.sleeperIngredient = Ingredient.of(items);
return this;
}
public TrackMaterialFactory rails(Ingredient railsIngredient) {
this.railsIngredient = railsIngredient;
return this;
}
public TrackMaterialFactory rails(ItemLike... items) {
this.railsIngredient = Ingredient.of(items);
return this;
}
public TrackMaterialFactory noRecipeGen() {
this.railsIngredient = Ingredient.EMPTY;
this.sleeperIngredient = Ingredient.EMPTY;
return this;
}
public TrackMaterialFactory particle(ResourceLocation particle) {
this.particle = particle;
return this;
}
public TrackMaterialFactory trackType(TrackMaterial.TrackType trackType) {
this.trackType = trackType;
return this;
}
public TrackMaterialFactory standardModels() { // was defaultModels
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
String namespace = id.getNamespace();
String prefix = "block/track/" + id.getPath() + "/";
tieModel = new PartialModel(new ResourceLocation(namespace, prefix + "tie"));
leftSegmentModel = new PartialModel(new ResourceLocation(namespace, prefix + "segment_left"));
rightSegmentModel = new PartialModel(new ResourceLocation(namespace, prefix + "segment_right"));
});
return this;
}
public TrackMaterialFactory customModels(Supplier<Supplier<PartialModel>> tieModel, Supplier<Supplier<PartialModel>> leftSegmentModel, Supplier<Supplier<PartialModel>> rightSegmentModel) {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.tieModel = tieModel.get().get();
this.leftSegmentModel = leftSegmentModel.get().get();
this.rightSegmentModel = rightSegmentModel.get().get();
});
return this;
}
public TrackMaterialFactory customBlockFactory(TrackMaterial.TrackType.TrackBlockFactory factory) {
this.customFactory = factory;
return this;
}
public TrackMaterial build() {
assert trackBlock != null;
assert langName != null;
assert particle != null;
assert trackType != null;
assert sleeperIngredient != null;
assert railsIngredient != null;
assert id != null;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
assert modelHolder != null;
if (tieModel != null || leftSegmentModel != null || rightSegmentModel != null) {
assert tieModel != null && leftSegmentModel != null && rightSegmentModel != null;
modelHolder = new TrackMaterial.TrackModelHolder(tieModel, leftSegmentModel, rightSegmentModel);
}
});
return new TrackMaterial(id, langName, trackBlock, particle, sleeperIngredient, railsIngredient, trackType, () -> () -> modelHolder, customFactory);
}
}

View file

@ -25,8 +25,8 @@ public class TrackNodeLocation extends Vec3i {
this(vec.x, vec.y, vec.z);
}
public TrackNodeLocation(double p_121865_, double p_121866_, double p_121867_) {
super(Math.round(p_121865_ * 2), Math.floor(p_121866_) * 2, Math.round(p_121867_ * 2));
public TrackNodeLocation(double x, double y, double z) {
super(Math.round(x * 2), Math.floor(y) * 2, Math.round(z * 2));
}
public TrackNodeLocation in(Level level) {
@ -122,9 +122,11 @@ public class TrackNodeLocation extends Vec3i {
boolean forceNode = false;
Vec3 direction;
Vec3 normal;
TrackMaterial materialA;
TrackMaterial materialB;
public DiscoveredLocation(Level level, double p_121865_, double p_121866_, double p_121867_) {
super(p_121865_, p_121866_, p_121867_);
public DiscoveredLocation(Level level, double x, double y, double z) {
super(x, y, z);
in(level);
}
@ -137,6 +139,22 @@ public class TrackNodeLocation extends Vec3i {
this(level.dimension(), vec);
}
public DiscoveredLocation materialA(TrackMaterial material) {
this.materialA = material;
return this;
}
public DiscoveredLocation materialB(TrackMaterial material) {
this.materialB = material;
return this;
}
public DiscoveredLocation materials(TrackMaterial materialA, TrackMaterial materialB) {
this.materialA = materialA;
this.materialB = materialB;
return this;
}
public DiscoveredLocation viaTurn(BezierConnection turn) {
this.turn = turn;
if (turn != null)
@ -176,6 +194,10 @@ public class TrackNodeLocation extends Vec3i {
return forceNode;
}
public boolean differentMaterials() {
return materialA != materialB;
}
public boolean notInLineWith(Vec3 direction) {
return this.direction != null
&& Math.max(direction.dot(this.direction), direction.dot(this.direction.scale(-1))) < 7 / 8f;

View file

@ -8,6 +8,7 @@ import java.util.List;
import java.util.Set;
import com.simibubi.create.Create;
import com.simibubi.create.api.event.TrackGraphMergeEvent;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalPropagator;
@ -16,6 +17,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.MinecraftForge;
public class TrackPropagator {
@ -135,6 +137,7 @@ public class TrackPropagator {
if (graph == null)
graph = other;
else {
MinecraftForge.EVENT_BUS.post(new TrackGraphMergeEvent(other, graph));
other.transferAll(graph);
manager.removeGraphAndGroup(other);
sync.graphRemoved(other);
@ -234,6 +237,8 @@ public class TrackPropagator {
return true;
if (location.shouldForceNode())
return true;
if (location.differentMaterials())
return true;
if (next.stream()
.anyMatch(DiscoveredLocation::shouldForceNode))
return true;

View file

@ -0,0 +1,22 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.MaterialManager;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import net.minecraft.nbt.CompoundTag;
public class BackupBogeyRenderer extends BogeyRenderer.CommonRenderer {
public static BackupBogeyRenderer INSTANCE = new BackupBogeyRenderer();
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
}
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
}
}

View file

@ -1,235 +1,69 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.Material;
import java.util.Optional;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.Vec3;
public sealed class BogeyInstance {
public final class BogeyInstance {
private final BogeySizes.BogeySize size;
private final BogeyStyle style;
public final CarriageBogey bogey;
private final ModelData[] shafts;
public final BogeyRenderer renderer;
public final Optional<BogeyRenderer.CommonRenderer> commonRenderer;
protected BogeyInstance(CarriageBogey bogey, MaterialManager materialManager) {
public BogeyInstance(CarriageBogey bogey, BogeyStyle style, BogeySizes.BogeySize size,
MaterialManager materialManager) {
this.bogey = bogey;
this.size = size;
this.style = style;
shafts = new ModelData[2];
materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z))
.createInstances(shafts);
this.renderer = this.style.createRendererInstance(this.size);
this.commonRenderer = this.style.getNewCommonRenderInstance();
commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.initialiseContraptionModelData(materialManager));
renderer.initialiseContraptionModelData(materialManager);
}
public void remove() {
for (ModelData shaft : shafts)
shaft.delete();
}
public void hiddenFrame() {
void hiddenFrame() {
beginFrame(0, null);
}
public void beginFrame(float wheelAngle, PoseStack ms) {
if (ms == null) {
for (int i : Iterate.zeroAndOne)
shafts[i].setEmptyTransform();
renderer.emptyTransforms();
return;
}
for (int i : Iterate.zeroAndOne)
shafts[i].setTransform(ms)
.translate(-.5f, .25f, i * -1)
.centre()
.rotateZ(wheelAngle)
.unCentre();
commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.render(bogey.bogeyData, wheelAngle, ms));
renderer.render(bogey.bogeyData, wheelAngle, ms);
}
public void updateLight(BlockAndTintGetter world, CarriageContraptionEntity entity) {
var lightPos = new BlockPos(getLightPos(entity));
updateLight(world.getBrightness(LightLayer.BLOCK, lightPos), world.getBrightness(LightLayer.SKY, lightPos));
commonRenderer
.ifPresent(bogeyRenderer -> bogeyRenderer.updateLight(world.getBrightness(LightLayer.BLOCK, lightPos),
world.getBrightness(LightLayer.SKY, lightPos)));
renderer.updateLight(world.getBrightness(LightLayer.BLOCK, lightPos),
world.getBrightness(LightLayer.SKY, lightPos));
}
private Vec3 getLightPos(CarriageContraptionEntity entity) {
if (bogey.getAnchorPosition() != null) {
return bogey.getAnchorPosition();
} else {
return entity.getLightProbePosition(AnimationTickHolder.getPartialTicks());
}
return bogey.getAnchorPosition() != null ? bogey.getAnchorPosition()
: entity.getLightProbePosition(AnimationTickHolder.getPartialTicks());
}
public void updateLight(int blockLight, int skyLight) {
for (ModelData shaft : shafts) {
shaft.setBlockLight(blockLight)
.setSkyLight(skyLight);
}
}
public static final class Frame extends BogeyInstance {
private final ModelData frame;
private final ModelData[] wheels;
public Frame(CarriageBogey bogey, MaterialManager materialManager) {
super(bogey, materialManager);
frame = materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllPartialModels.BOGEY_FRAME)
.createInstance();
wheels = new ModelData[2];
materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllPartialModels.SMALL_BOGEY_WHEELS)
.createInstances(wheels);
}
@Override
public void beginFrame(float wheelAngle, PoseStack ms) {
super.beginFrame(wheelAngle, ms);
if (ms == null) {
frame.setEmptyTransform();
for (int side : Iterate.positiveAndNegative)
wheels[(side + 1) / 2].setEmptyTransform();
return;
}
frame.setTransform(ms);
for (int side : Iterate.positiveAndNegative) {
wheels[(side + 1) / 2].setTransform(ms)
.translate(0, 12 / 16f, side)
.rotateX(wheelAngle);
}
}
@Override
public void updateLight(int blockLight, int skyLight) {
super.updateLight(blockLight, skyLight);
frame.setBlockLight(blockLight)
.setSkyLight(skyLight);
for (ModelData wheel : wheels)
wheel.setBlockLight(blockLight)
.setSkyLight(skyLight);
}
@Override
public void remove() {
super.remove();
frame.delete();
for (ModelData wheel : wheels)
wheel.delete();
}
}
public static final class Drive extends BogeyInstance {
private final ModelData[] secondShaft;
private final ModelData drive;
private final ModelData piston;
private final ModelData wheels;
private final ModelData pin;
public Drive(CarriageBogey bogey, MaterialManager materialManager) {
super(bogey, materialManager);
Material<ModelData> mat = materialManager.defaultSolid()
.material(Materials.TRANSFORMED);
secondShaft = new ModelData[2];
mat.getModel(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X))
.createInstances(secondShaft);
drive = mat.getModel(AllPartialModels.BOGEY_DRIVE)
.createInstance();
piston = mat.getModel(AllPartialModels.BOGEY_PISTON)
.createInstance();
wheels = mat.getModel(AllPartialModels.LARGE_BOGEY_WHEELS)
.createInstance();
pin = mat.getModel(AllPartialModels.BOGEY_PIN)
.createInstance();
}
@Override
public void beginFrame(float wheelAngle, PoseStack ms) {
super.beginFrame(wheelAngle, ms);
if (ms == null) {
for (int i : Iterate.zeroAndOne)
secondShaft[i].setEmptyTransform();
drive.setEmptyTransform();
piston.setEmptyTransform();
wheels.setEmptyTransform();
pin.setEmptyTransform();
return;
}
for (int i : Iterate.zeroAndOne)
secondShaft[i].setTransform(ms)
.translate(-.5f, .25f, .5f + i * -2)
.centre()
.rotateX(wheelAngle)
.unCentre();
drive.setTransform(ms);
piston.setTransform(ms)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)));
wheels.setTransform(ms)
.translate(0, 1, 0)
.rotateX(wheelAngle);
pin.setTransform(ms)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateX(-wheelAngle);
}
@Override
public void updateLight(int blockLight, int skyLight) {
super.updateLight(blockLight, skyLight);
for (ModelData shaft : secondShaft)
shaft.setBlockLight(blockLight)
.setSkyLight(skyLight);
drive.setBlockLight(blockLight)
.setSkyLight(skyLight);
piston.setBlockLight(blockLight)
.setSkyLight(skyLight);
wheels.setBlockLight(blockLight)
.setSkyLight(skyLight);
pin.setBlockLight(blockLight)
.setSkyLight(skyLight);
}
@Override
public void remove() {
super.remove();
for (ModelData shaft : secondShaft)
shaft.delete();
drive.delete();
piston.delete();
wheels.delete();
pin.delete();
}
@FunctionalInterface
interface BogeyInstanceFactory {
BogeyInstance create(CarriageBogey bogey, BogeySizes.BogeySize size, MaterialManager materialManager);
}
}

View file

@ -0,0 +1,114 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.MaterialManager;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.content.logistics.trains.BogeyRenderer.CommonRenderer;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class BogeyStyle {
private final Optional<Supplier<? extends CommonRenderer>> commonRendererFactory;
public final ResourceLocation name;
public final ResourceLocation cycleGroup;
private final Optional<CommonRenderer> commonRenderer;
private final Map<BogeySizes.BogeySize, SizeData> sizes;
public final Component displayName;
public final ResourceLocation soundType;
public final ParticleOptions contactParticle;
public final ParticleOptions smokeParticle;
public final CompoundTag defaultData;
public BogeyStyle(ResourceLocation name, ResourceLocation cycleGroup, Component displayName, ResourceLocation soundType, ParticleOptions contactParticle, ParticleOptions smokeParticle,
CompoundTag defaultData, Map<BogeySizes.BogeySize, SizeData> sizes, Optional<Supplier<? extends CommonRenderer>> commonRenderer) {
this.name = name;
this.cycleGroup = cycleGroup;
this.displayName = displayName;
this.soundType = soundType;
this.contactParticle = contactParticle;
this.smokeParticle = smokeParticle;
this.defaultData = defaultData;
this.sizes = sizes;
this.commonRendererFactory = commonRenderer;
this.commonRenderer = commonRenderer.map(Supplier::get);
}
public Map<ResourceLocation, BogeyStyle> getCycleGroup() {
return AllBogeyStyles.getCycleGroup(cycleGroup);
}
public Block getNextBlock(BogeySizes.BogeySize currentSize) {
return Stream.iterate(currentSize.increment(), BogeySizes.BogeySize::increment)
.filter(sizes::containsKey)
.findFirst()
.map(size -> ForgeRegistries.BLOCKS.getValue(sizes.get(size).block()))
.orElse(ForgeRegistries.BLOCKS.getValue(sizes.get(currentSize).block()));
}
public Block getBlockOfSize(BogeySizes.BogeySize size) {
return ForgeRegistries.BLOCKS.getValue(sizes.get(size).block());
}
public Set<BogeySizes.BogeySize> validSizes() {
return sizes.keySet();
}
@NotNull
public SoundEvent getSoundType() {
AllSoundEvents.SoundEntry entry = AllSoundEvents.ALL.get(this.soundType);
if (entry == null || entry.getMainEvent() == null) entry = AllSoundEvents.TRAIN2;
return entry.getMainEvent();
}
public BogeyRenderer createRendererInstance(BogeySizes.BogeySize size) {
return this.sizes.get(size).createRenderInstance();
}
public BogeyRenderer getInWorldRenderInstance(BogeySizes.BogeySize size) {
SizeData sizeData = this.sizes.get(size);
return sizeData != null ? sizeData.getInWorldInstance() : BackupBogeyRenderer.INSTANCE;
}
public Optional<CommonRenderer> getInWorldCommonRenderInstance() {
return this.commonRenderer;
}
public Optional<CommonRenderer> getNewCommonRenderInstance() {
return this.commonRendererFactory.map(Supplier::get);
}
public BogeyInstance createInstance(CarriageBogey bogey, BogeySizes.BogeySize size, MaterialManager materialManager) {
return new BogeyInstance(bogey, this, size, materialManager);
}
public record SizeData(ResourceLocation block, Supplier<? extends BogeyRenderer> rendererFactory, BogeyRenderer instance) {
public BogeyRenderer createRenderInstance() {
return rendererFactory.get();
}
public BogeyRenderer getInWorldInstance() {
return instance;
}
}
}

View file

@ -85,6 +85,11 @@ public class Carriage {
bogey2.carriage = this;
}
public boolean isOnIncompatibleTrack() {
return leadingBogey().type.isOnIncompatibleTrack(this, true)
|| trailingBogey().type.isOnIncompatibleTrack(this, false);
}
public void setTrain(Train train) {
this.train = train;
}
@ -103,7 +108,7 @@ public class Carriage {
DimensionalCarriageEntity dimensional = getDimensional(level);
dimensional.alignEntity(entity);
dimensional.removeAndSaveEntity(entity, false);
dimensional.removeAndSaveEntity(entity, true);
}
public DimensionalCarriageEntity getDimensional(Level level) {
@ -306,6 +311,9 @@ public class Carriage {
double leadingWheelSpacing = leadingBogey.type.getWheelPointSpacing();
double trailingWheelSpacing = trailingBogey.type.getWheelPointSpacing();
boolean leadingUpsideDown = leadingBogey.isUpsideDown();
boolean trailingUpsideDown = trailingBogey.isUpsideDown();
for (boolean leading : Iterate.trueAndFalse) {
TravellingPoint point = leading ? getLeadingPoint() : getTrailingPoint();
TravellingPoint otherPoint = !leading ? getLeadingPoint() : getTrailingPoint();
@ -321,26 +329,31 @@ public class Carriage {
dce.positionAnchor = dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2);
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2,
leadingUpsideDown, trailingUpsideDown);
boolean backAnchorFlip = trailingBogey.isUpsideDown() ^ leadingBogey.isUpsideDown();
if (isOnTwoBogeys()) {
dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2));
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition()
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2,
leadingUpsideDown, trailingUpsideDown));
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition(backAnchorFlip)
: pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2));
leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2,
leadingUpsideDown, trailingUpsideDown));
} else {
if (dimension.equals(otherDimension)) {
dce.rotationAnchors = leadingBogey.points.map(tp -> tp.getPosition(train.graph));
} else {
dce.rotationAnchors
.setFirst(leadingBogey.points.getFirst() == point ? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing));
dce.rotationAnchors
.setSecond(leadingBogey.points.getSecond() == point ? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing));
dce.rotationAnchors.setFirst(leadingBogey.points.getFirst() == point
? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing, leadingUpsideDown, trailingUpsideDown));
dce.rotationAnchors.setSecond(leadingBogey.points.getSecond() == point
? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing, leadingUpsideDown, trailingUpsideDown));
}
}
@ -358,15 +371,16 @@ public class Carriage {
}
private Vec3 pivoted(DimensionalCarriageEntity dce, ResourceKey<Level> dimension, TravellingPoint start,
double offset) {
double offset, boolean leadingUpsideDown, boolean trailingUpsideDown) {
if (train.graph == null)
return dce.pivot == null ? null : dce.pivot.getLocation();
TrackNodeLocation pivot = dce.findPivot(dimension, start == getLeadingPoint());
if (pivot == null)
return null;
Vec3 startVec = start.getPosition(train.graph);
boolean flipped = start != getLeadingPoint() && (leadingUpsideDown != trailingUpsideDown);
Vec3 startVec = start.getPosition(train.graph, flipped);
Vec3 portalVec = pivot.getLocation()
.add(0, 1, 0);
.add(0, leadingUpsideDown ? -1.0 : 1.0, 0);
return VecHelper.lerp((float) (offset / startVec.distanceTo(portalVec)), startVec, portalVec);
}
@ -725,8 +739,8 @@ public class Carriage {
}
private void dismountPlayer(ServerLevel sLevel, ServerPlayer sp, Integer seat, boolean portal) {
if (!portal) {
private void dismountPlayer(ServerLevel sLevel, ServerPlayer sp, Integer seat, boolean capture) {
if (!capture) {
sp.stopRiding();
return;
}

View file

@ -1,15 +1,21 @@
package com.simibubi.create.content.logistics.trains.entity;
import static com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity.BOGEY_DATA_KEY;
import static com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity.BOGEY_STYLE_KEY;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.DimensionPalette;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
@ -27,11 +33,15 @@ import net.minecraftforge.registries.ForgeRegistries;
public class CarriageBogey {
public Carriage carriage;
public static final String UPSIDE_DOWN_KEY = "UpsideDown";
public Carriage carriage;
boolean isLeading;
IBogeyBlock type;
public CompoundTag bogeyData;
AbstractBogeyBlock<?> type;
boolean upsideDown;
Couple<TravellingPoint> points;
LerpedFloat wheelAngle;
@ -42,8 +52,15 @@ public class CarriageBogey {
int derailAngle;
public CarriageBogey(IBogeyBlock type, TravellingPoint point, TravellingPoint point2) {
public CarriageBogey(AbstractBogeyBlock<?> type, boolean upsideDown, CompoundTag bogeyData, TravellingPoint point, TravellingPoint point2) {
this.type = type;
this.upsideDown = type.canBeUpsideDown() && upsideDown;
point.upsideDown = this.upsideDown;
point2.upsideDown = this.upsideDown;
if (bogeyData == null || bogeyData.isEmpty())
bogeyData = this.createBogeyData(); // Prevent Crash When Updating
bogeyData.putBoolean(UPSIDE_DOWN_KEY, upsideDown);
this.bogeyData = bogeyData;
points = Couple.create(point, point2);
wheelAngle = LerpedFloat.angular();
yaw = LerpedFloat.angular();
@ -100,11 +117,15 @@ public class CarriageBogey {
}
public TravellingPoint leading() {
return points.getFirst();
TravellingPoint point = points.getFirst();
point.upsideDown = isUpsideDown();
return point;
}
public TravellingPoint trailing() {
return points.getSecond();
TravellingPoint point = points.getSecond();
point.upsideDown = isUpsideDown();
return point;
}
public double getStress() {
@ -118,18 +139,25 @@ public class CarriageBogey {
@Nullable
public Vec3 getAnchorPosition() {
return getAnchorPosition(false);
}
@Nullable
public Vec3 getAnchorPosition(boolean flipUpsideDown) {
if (leading().edge == null)
return null;
return points.getFirst()
.getPosition(carriage.train.graph)
.getPosition(carriage.train.graph, flipUpsideDown)
.add(points.getSecond()
.getPosition(carriage.train.graph))
.getPosition(carriage.train.graph, flipUpsideDown))
.scale(.5);
}
public void updateCouplingAnchor(Vec3 entityPos, float entityXRot, float entityYRot, int bogeySpacing,
float partialTicks, boolean leading) {
Vec3 thisOffset = type.getConnectorAnchorOffset();
boolean selfUpsideDown = isUpsideDown();
boolean leadingUpsideDown = carriage.leadingBogey().isUpsideDown();
Vec3 thisOffset = type.getConnectorAnchorOffset(selfUpsideDown);
thisOffset = thisOffset.multiply(1, 1, leading ? -1 : 1);
thisOffset = VecHelper.rotate(thisOffset, pitch.getValue(partialTicks), Axis.X);
@ -141,6 +169,8 @@ public class CarriageBogey {
thisOffset = VecHelper.rotate(thisOffset, 180, Axis.Y);
thisOffset = VecHelper.rotate(thisOffset, -entityXRot, Axis.X);
thisOffset = VecHelper.rotate(thisOffset, entityYRot + 90, Axis.Y);
if (selfUpsideDown != leadingUpsideDown)
thisOffset = thisOffset.add(0, selfUpsideDown ? -2 : 2, 0);
couplingAnchors.set(leading, entityPos.add(thisOffset));
}
@ -150,23 +180,46 @@ public class CarriageBogey {
tag.putString("Type", RegisteredObjects.getKeyOrThrow((Block) type)
.toString());
tag.put("Points", points.serializeEach(tp -> tp.write(dimensions)));
tag.putBoolean("UpsideDown", upsideDown);
bogeyData.putBoolean(UPSIDE_DOWN_KEY, upsideDown);
NBTHelper.writeResourceLocation(bogeyData, BOGEY_STYLE_KEY, getStyle().name);
tag.put(BOGEY_DATA_KEY, bogeyData);
return tag;
}
public static CarriageBogey read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) {
ResourceLocation location = new ResourceLocation(tag.getString("Type"));
IBogeyBlock type = (IBogeyBlock) ForgeRegistries.BLOCKS.getValue(location);
AbstractBogeyBlock<?> type = (AbstractBogeyBlock<?>) ForgeRegistries.BLOCKS.getValue(location);
boolean upsideDown = tag.getBoolean("UpsideDown");
Couple<TravellingPoint> points = Couple.deserializeEach(tag.getList("Points", Tag.TAG_COMPOUND),
c -> TravellingPoint.read(c, graph, dimensions));
CarriageBogey carriageBogey = new CarriageBogey(type, points.getFirst(), points.getSecond());
return carriageBogey;
CompoundTag data = tag.getCompound(AbstractBogeyBlockEntity.BOGEY_DATA_KEY);
return new CarriageBogey(type, upsideDown, data, points.getFirst(), points.getSecond());
}
public BogeyInstance createInstance(MaterialManager materialManager) {
return type.createInstance(materialManager, this);
return this.getStyle().createInstance(this, type.getSize(), materialManager);
}
public BogeyStyle getStyle() {
ResourceLocation location = NBTHelper.readResourceLocation(this.bogeyData, BOGEY_STYLE_KEY);
BogeyStyle style = AllBogeyStyles.BOGEY_STYLES.get(location);
return style != null ? style : AllBogeyStyles.STANDARD; // just for safety
}
private CompoundTag createBogeyData() {
BogeyStyle style = type != null ? type.getDefaultStyle() : AllBogeyStyles.STANDARD;
CompoundTag nbt = style.defaultData != null ? style.defaultData : new CompoundTag();
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, style.name);
nbt.putBoolean(UPSIDE_DOWN_KEY, isUpsideDown());
return nbt;
}
void setLeading() {
isLeading = true;
}
public boolean isUpsideDown() {
return type.canBeUpsideDown() && upsideDown;
}
}

View file

@ -22,7 +22,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.ren
import com.simibubi.create.content.contraptions.components.structureMovement.train.TrainCargoManager;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
@ -162,11 +162,13 @@ public class CarriageContraption extends Contraption {
.getStep(), toLocalPos(pos));
}
if (blockState.getBlock() instanceof IBogeyBlock) {
if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey) {
boolean captureTE = bogey.captureTileEntityForTrain();
bogeys++;
if (bogeys == 2)
secondBogeyPos = pos;
return Pair.of(new StructureBlockInfo(pos, blockState, null), null);
return Pair.of(new StructureBlockInfo(pos, blockState, captureTE ? getBlockEntityNBT(world, pos) : null),
captureTE ? world.getBlockEntity(pos) : null);
}
if (AllBlocks.BLAZE_BURNER.has(blockState)

View file

@ -13,6 +13,7 @@ import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer<CarriageContraptionEntity> {
@ -65,8 +66,9 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
translateBogey(ms, bogey, bogeySpacing, viewYRot, viewXRot, partialTicks);
int light = getBogeyLightCoords(entity, bogey, partialTicks);
bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light,
overlay);
overlay, bogey.getStyle(), bogey.bogeyData);
ms.popPose();
}
@ -80,6 +82,8 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
public static void translateBogey(PoseStack ms, CarriageBogey bogey, int bogeySpacing, float viewYRot,
float viewXRot, float partialTicks) {
boolean selfUpsideDown = bogey.isUpsideDown();
boolean leadingUpsideDown = bogey.carriage.leadingBogey().isUpsideDown();
TransformStack.cast(ms)
.rotateY(viewYRot + 90)
.rotateX(-viewXRot)
@ -90,7 +94,9 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
.rotateY(-viewYRot - 90)
.rotateY(bogey.yaw.getValue(partialTicks))
.rotateX(bogey.pitch.getValue(partialTicks))
.translate(0, .5f, 0);
.translate(0, .5f, 0)
.rotateZ(selfUpsideDown ? 180 : 0)
.translateY(selfUpsideDown != leadingUpsideDown ? 2 : 0);
}
public static int getBogeyLightCoords(CarriageContraptionEntity entity, CarriageBogey bogey, float partialTicks) {

View file

@ -7,6 +7,7 @@ import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
@ -31,7 +32,8 @@ public class CarriageContraptionInstance extends EntityInstance<CarriageContrapt
if (carriage == null)
return;
bogeys = carriage.bogeys.mapNotNullWithParam(CarriageBogey::createInstance, materialManager);
bogeys = carriage.bogeys.mapNotNullWithParam((bogey, manager) ->
bogey.getStyle().createInstance(bogey, bogey.type.getSize(), manager), materialManager);
updateLight();
}
@ -98,8 +100,10 @@ public class CarriageContraptionInstance extends EntityInstance<CarriageContrapt
return;
bogeys.forEach(instance -> {
if (instance != null)
instance.remove();
if (instance != null) {
instance.commonRenderer.ifPresent(BogeyRenderer::remove);
instance.renderer.remove();
}
});
}

View file

@ -79,7 +79,7 @@ public class CarriageCouplingRenderer {
float margin = 3 / 16f;
double couplingDistance = train.carriageSpacing.get(i) - 2 * margin
- bogey1.type.getConnectorAnchorOffset().z - bogey2.type.getConnectorAnchorOffset().z;
- bogey1.type.getConnectorAnchorOffset(bogey1.isUpsideDown()).z - bogey2.type.getConnectorAnchorOffset(bogey2.isUpsideDown()).z;
int couplingSegments = (int) Math.round(couplingDistance * 4);
double stretch = ((anchor2.distanceTo(anchor) - 2 * margin) * 4) / couplingSegments;
for (int j = 0; j < couplingSegments; j++) {

View file

@ -8,7 +8,6 @@ import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
@ -109,7 +108,7 @@ public class CarriageParticles {
m = m.add(contraptionMotion.scale(.75f));
level.addParticle(spark ? ParticleTypes.CRIT : ParticleTypes.POOF, v.x, v.y, v.z, m.x, m.y, m.z);
level.addParticle(spark ? bogey.getStyle().contactParticle : bogey.getStyle().smokeParticle, v.x, v.y, v.z, m.x, m.y, m.z);
}
}
}

View file

@ -3,6 +3,7 @@ package com.simibubi.create.content.logistics.trains.entity;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.AllSoundEvents.SoundEntry;
import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
@ -30,6 +31,9 @@ public class CarriageSounds {
LoopingSound sharedWheelSoundSeated;
LoopingSound sharedHonkSound;
Couple<SoundEvent> bogeySounds;
SoundEvent closestBogeySound;
boolean arrived;
int tick;
@ -37,6 +41,10 @@ public class CarriageSounds {
public CarriageSounds(CarriageContraptionEntity entity) {
this.entity = entity;
bogeySounds = entity.getCarriage().bogeys.map(bogey ->
bogey != null && bogey.getStyle() != null ? bogey.getStyle().getSoundType()
: AllSoundEvents.TRAIN2.getMainEvent());
closestBogeySound = bogeySounds.getFirst();
distanceFactor = LerpedFloat.linear();
speedFactor = LerpedFloat.linear();
approachFactor = LerpedFloat.linear();
@ -80,6 +88,15 @@ public class CarriageSounds {
double distance1 = toBogey1.length();
double distance2 = toBogey2.length();
Couple<CarriageBogey> bogeys = entity.getCarriage().bogeys;
CarriageBogey relevantBogey = bogeys.get(distance1 > distance2);
if (relevantBogey == null) {
relevantBogey = bogeys.getFirst();
}
if (relevantBogey != null) {
closestBogeySound = relevantBogey.getStyle().getSoundType();
}
Vec3 toCarriage = distance1 > distance2 ? toBogey2 : toBogey1;
double distance = Math.min(distance1, distance2);
Vec3 soundLocation = cam.add(toCarriage);
@ -98,7 +115,7 @@ public class CarriageSounds {
seatCrossfade.tickChaser();
minecartEsqueSound = playIfMissing(mc, minecartEsqueSound, AllSoundEvents.TRAIN.getMainEvent());
sharedWheelSound = playIfMissing(mc, sharedWheelSound, AllSoundEvents.TRAIN2.getMainEvent());
sharedWheelSound = playIfMissing(mc, sharedWheelSound, closestBogeySound);
sharedWheelSoundSeated = playIfMissing(mc, sharedWheelSoundSeated, AllSoundEvents.TRAIN3.getMainEvent());
float volume = Math.min(Math.min(speedFactor.getValue(), distanceFactor.getValue() / 100),
@ -206,7 +223,7 @@ public class CarriageSounds {
public void submitSharedSoundVolume(Vec3 location, float volume) {
Minecraft mc = Minecraft.getInstance();
minecartEsqueSound = playIfMissing(mc, minecartEsqueSound, AllSoundEvents.TRAIN.getMainEvent());
sharedWheelSound = playIfMissing(mc, sharedWheelSound, AllSoundEvents.TRAIN2.getMainEvent());
sharedWheelSound = playIfMissing(mc, sharedWheelSound, closestBogeySound);
sharedWheelSoundSeated = playIfMissing(mc, sharedWheelSoundSeated, AllSoundEvents.TRAIN3.getMainEvent());
boolean approach = true;

View file

@ -14,6 +14,8 @@ import java.util.UUID;
import javax.annotation.Nullable;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableObject;
@ -555,6 +557,21 @@ public class Navigation {
if (graph == null)
return;
// Cache the list of track types that the train can travel on
Set<TrackMaterial.TrackType> validTypes = new HashSet<>();
for (int i = 0; i < train.carriages.size(); i++) {
Carriage carriage = train.carriages.get(i);
if (i == 0) {
validTypes.addAll(carriage.leadingBogey().type.getValidPathfindingTypes(carriage.leadingBogey().getStyle()));
} else {
validTypes.retainAll(carriage.leadingBogey().type.getValidPathfindingTypes(carriage.leadingBogey().getStyle()));
}
if (carriage.isOnTwoBogeys())
validTypes.retainAll(carriage.trailingBogey().type.getValidPathfindingTypes(carriage.trailingBogey().getStyle()));
}
if (validTypes.isEmpty()) // if there are no valid track types, a route can't be found
return;
Map<TrackEdge, Integer> penalties = new IdentityHashMap<>();
boolean costRelevant = maxCost >= 0;
if (costRelevant) {
@ -674,6 +691,8 @@ public class Navigation {
continue;
for (Entry<TrackNode, TrackEdge> target : validTargets) {
if (!validTypes.contains(target.getValue().getTrackMaterial().trackType))
continue;
TrackNode newNode = target.getKey();
TrackEdge newEdge = target.getValue();
double newDistance = newEdge.getLength() + distance;

View file

@ -42,6 +42,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.networking.AllPackets;
@ -66,6 +67,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Explosion.BlockInteraction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.fluids.FluidStack;
@ -86,6 +88,7 @@ public class Train {
public boolean honk = false;
public UUID id;
@Nullable
public UUID owner;
public TrackGraph graph;
public Navigation navigation;
@ -313,7 +316,13 @@ public class Train {
if (leadingAnchor == null || trailingAnchor == null)
continue;
total += leadingAnchor.distanceTo(trailingAnchor);
double distanceTo = leadingAnchor.distanceToSqr(trailingAnchor);
if (carriage.leadingBogey().isUpsideDown() != previousCarriage.trailingBogey().isUpsideDown()) {
distanceTo = Math.sqrt(distanceTo - 4);
} else {
distanceTo = Math.sqrt(distanceTo);
}
total += distanceTo;
entries++;
}
}
@ -375,7 +384,7 @@ public class Train {
int carriageType = first ? last ? Carriage.BOTH : Carriage.FIRST : last ? Carriage.LAST : Carriage.MIDDLE;
double actualDistance =
carriage.travel(level, graph, distance + totalStress, toFollowForward, toFollowBackward, carriageType);
blocked |= carriage.blocked;
blocked |= carriage.blocked || carriage.isOnIncompatibleTrack();
boolean onTwoBogeys = carriage.isOnTwoBogeys();
maxStress = Math.max(maxStress, onTwoBogeys ? carriage.bogeySpacing - carriage.getAnchorDiff() : 0);
@ -722,9 +731,20 @@ public class Train {
if (entity.getContraption()instanceof CarriageContraption cc)
cc.returnStorageForDisassembly(carriage.storage);
entity.setPos(Vec3
.atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset)));
.atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset).below(carriage.leadingBogey().isUpsideDown() ? 2 : 0)));
entity.disassemble();
for (CarriageBogey bogey : carriage.bogeys) {
if (bogey == null)
continue;
Vec3 bogeyPosition = bogey.getAnchorPosition();
if (bogeyPosition == null) continue;
BlockEntity be = level.getBlockEntity(new BlockPos(bogeyPosition));
if (!(be instanceof AbstractBogeyBlockEntity sbte))
continue;
sbte.setBogeyData(bogey.bogeyData);
}
offset += carriage.bogeySpacing;
if (i < carriageSpacing.size())
@ -944,7 +964,7 @@ public class Train {
occupiedObservers.clear();
cachedObserverFiltering.clear();
TravellingPoint signalScout = new TravellingPoint(node1, node2, edge, position);
TravellingPoint signalScout = new TravellingPoint(node1, node2, edge, position, false);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.signalEdgeGroups;
MutableObject<UUID> prevGroup = new MutableObject<>(null);
@ -1087,7 +1107,8 @@ public class Train {
public CompoundTag write(DimensionPalette dimensions) {
CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id);
tag.putUUID("Owner", owner);
if (owner != null)
tag.putUUID("Owner", owner);
if (graph != null)
tag.putUUID("Graph", graph.id);
tag.put("Carriages", NBTHelper.writeCompoundList(carriages, c -> c.write(dimensions)));
@ -1133,7 +1154,7 @@ public class Train {
public static Train read(CompoundTag tag, Map<UUID, TrackGraph> trackNetworks, DimensionPalette dimensions) {
UUID id = tag.getUUID("Id");
UUID owner = tag.getUUID("Owner");
UUID owner = tag.contains("Owner") ? tag.getUUID("Owner") : null;
UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null;
TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId);
List<Carriage> carriages = new ArrayList<>();

Some files were not shown because too many files have changed in this diff Show more