Compare commits
17 Commits
v1.20.5-1.
...
v1.20.6-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
de078e3037 | ||
![]() |
209b1ddbf9 | ||
![]() |
0c9f9a8652 | ||
![]() |
862d92785e | ||
![]() |
d48b85d50c | ||
![]() |
4d619de357 | ||
![]() |
57c289f173 | ||
![]() |
f63f85921f | ||
![]() |
c7e49d1929 | ||
![]() |
eb584aa94d | ||
![]() |
ad70e2ad90 | ||
![]() |
2c0d8263d3 | ||
![]() |
1e214f329e | ||
![]() |
de930c8d09 | ||
![]() |
94c864759d | ||
![]() |
735e7ce09b | ||
![]() |
6e9799316a |
3
.github/workflows/make-doc.yml
vendored
@@ -3,8 +3,7 @@ name: Build documentation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- mc-1.19.x
|
||||
- mc-1.20.x
|
||||
- mc-*
|
||||
|
||||
jobs:
|
||||
make_doc:
|
||||
|
@@ -30,11 +30,6 @@ subsystems {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
implementation("net.neoforged:neoforge:${libs.findVersion("neoForge").get()}")
|
||||
}
|
||||
|
||||
MinecraftConfigurations.setup(project)
|
||||
|
||||
extensions.configure(CCTweakedExtension::class.java) {
|
||||
|
@@ -56,7 +56,6 @@ repositories {
|
||||
includeGroup("mezz.jei")
|
||||
includeGroup("org.teavm")
|
||||
includeModule("com.terraformersmc", "modmenu")
|
||||
includeModule("me.lucko", "fabric-permissions-api")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,9 +94,8 @@ sourceSets.all {
|
||||
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||
check("ReferenceEquality", CheckSeverity.OFF)
|
||||
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
|
||||
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
|
||||
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||
|
||||
|
@@ -225,12 +225,12 @@ abstract class CCTweakedExtension(
|
||||
* where possible.
|
||||
*/
|
||||
fun downloadFile(label: String, url: String): File {
|
||||
val url = URI(url)
|
||||
val path = File(url.path)
|
||||
val uri = URI(url)
|
||||
val path = File(uri.path)
|
||||
|
||||
project.repositories.ivy {
|
||||
name = label
|
||||
setUrl(URI(url.scheme, url.userInfo, url.host, url.port, path.parent, null, null))
|
||||
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
|
||||
patternLayout {
|
||||
artifact("[artifact].[ext]")
|
||||
}
|
||||
|
@@ -98,7 +98,6 @@ abstract class MergeTrees : DefaultTask() {
|
||||
}
|
||||
|
||||
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
|
||||
println(sharedFiles)
|
||||
|
||||
// Copy shared files to the common directory
|
||||
fsOperations.sync {
|
||||
|
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=true
|
||||
modVersion=1.110.3
|
||||
modVersion=1.111.0
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.20.5
|
||||
mcVersion=1.20.6
|
||||
|
@@ -7,14 +7,14 @@
|
||||
# Minecraft
|
||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||
# Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml
|
||||
fabric-api = "0.97.6+1.20.5"
|
||||
fabric-api = "0.98.0+1.20.6"
|
||||
fabric-loader = "0.15.10"
|
||||
neoForge = "20.5.20-beta"
|
||||
neoForge = "20.6.48-beta"
|
||||
neoForgeSpi = "8.0.1"
|
||||
mixin = "0.8.5"
|
||||
parchment = "2024.04.14"
|
||||
parchmentMc = "1.20.4"
|
||||
yarn = "1.20.5+build.1"
|
||||
parchment = "2024.05.01"
|
||||
parchmentMc = "1.20.6"
|
||||
yarn = "1.20.6+build.1"
|
||||
|
||||
# Core dependencies (these versions are tied to the version Minecraft uses)
|
||||
fastutil = "8.5.12"
|
||||
@@ -36,14 +36,14 @@ kotlin-coroutines = "1.7.3"
|
||||
nightConfig = "3.6.7"
|
||||
|
||||
# Minecraft mods
|
||||
emi = "1.0.30+1.20.4"
|
||||
fabricPermissions = "0.3.20230723"
|
||||
emi = "1.1.5+1.20.6"
|
||||
fabricPermissions = "0.3.1"
|
||||
iris = "1.6.14+1.20.4"
|
||||
jei = "17.3.0.48"
|
||||
modmenu = "9.0.0"
|
||||
moreRed = "4.0.0.4"
|
||||
oculus = "1.2.5"
|
||||
rei = "14.0.688"
|
||||
rei = "15.0.728"
|
||||
rubidium = "0.6.1"
|
||||
sodium = "mc1.20-0.4.10"
|
||||
mixinExtra = "0.3.5"
|
||||
@@ -58,7 +58,7 @@ jmh = "1.37"
|
||||
cctJavadoc = "1.8.2"
|
||||
checkstyle = "10.14.1"
|
||||
curseForgeGradle = "1.1.18"
|
||||
errorProne-core = "2.23.0"
|
||||
errorProne-core = "2.27.0"
|
||||
errorProne-plugin = "3.1.0"
|
||||
fabric-loom = "1.6.7"
|
||||
githubRelease = "2.5.2"
|
||||
|
@@ -21,4 +21,16 @@ tasks.javadoc {
|
||||
|
||||
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
|
||||
source(project(":core-api").sourceSets.main.map { it.allJava })
|
||||
|
||||
options {
|
||||
this as StandardJavadocDocletOptions
|
||||
addBooleanOption("-allow-script-in-comments", true)
|
||||
bottom(
|
||||
"""
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -4,12 +4,14 @@
|
||||
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -21,11 +23,11 @@ import javax.annotation.Nullable;
|
||||
* {@link UpgradeType} instance, which are then registered in a registry.
|
||||
* <p>
|
||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||
* the upgrade automatically registered. It is recommended this is done via {@linkplain PocketUpgradeDataProvider data
|
||||
* generators}.
|
||||
* the upgrade automatically registered. It is recommended this is done via
|
||||
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <pre>{@code
|
||||
* {@snippet lang="java" :
|
||||
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
|
||||
* static final DeferredRegister<UpgradeType<? extends IPocketUpgrade>> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod");
|
||||
*
|
||||
@@ -35,19 +37,19 @@ import javax.annotation.Nullable;
|
||||
*
|
||||
* // Then in your constructor
|
||||
* POCKET_UPGRADES.register(bus);
|
||||
* }</pre>
|
||||
* }
|
||||
* <p>
|
||||
* We can then define a new upgrade using JSON by placing the following in
|
||||
* {@code data/<my_mod>/computercraft/pocket_upgrades/<my_upgrade_id>.json}.
|
||||
* <pre>{@code
|
||||
* {@code data/<my_mod>/computercraft/pocket_upgrade/<my_upgrade_id>.json}.
|
||||
* {@snippet lang="json" :
|
||||
* {
|
||||
* "type": my_mod:my_upgrade",
|
||||
* "type": "my_mod:my_upgrade"
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* {@link PocketUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
|
||||
*/
|
||||
public interface IPocketUpgrade extends UpgradeBase {
|
||||
ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
|
||||
|
||||
/**
|
||||
* The registry key for pocket upgrade types.
|
||||
*
|
||||
|
@@ -1,30 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A data provider to generate pocket computer upgrades.
|
||||
* <p>
|
||||
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
|
||||
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
||||
* generate them.
|
||||
*
|
||||
* @see IPocketUpgrade
|
||||
* @see UpgradeType
|
||||
*/
|
||||
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade> {
|
||||
public PocketUpgradeDataProvider(PackOutput output) {
|
||||
super(output, "Pocket Computer Upgrades", RegistryHelper.POCKET_UPGRADE, ComputerCraftAPIService.get().pocketUpgradeCodec());
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
@@ -12,6 +13,7 @@ import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -23,11 +25,11 @@ import javax.annotation.Nullable;
|
||||
* {@link UpgradeType} instance, which are then registered in a registry.
|
||||
* <p>
|
||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||
* the upgrade automatically registered. It is recommended this is done via {@linkplain TurtleUpgradeDataProvider data
|
||||
* generators}.
|
||||
* the upgrade automatically registered. It is recommended this is done via
|
||||
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <pre>{@code
|
||||
* {@snippet lang="java" :
|
||||
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
|
||||
* static final DeferredRegister<UpgradeType<? extends ITurtleUpgrade>> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod");
|
||||
*
|
||||
@@ -37,28 +39,38 @@ import javax.annotation.Nullable;
|
||||
*
|
||||
* // Then in your constructor
|
||||
* TURTLE_UPGRADES.register(bus);
|
||||
* }</pre>
|
||||
* }
|
||||
* <p>
|
||||
* We can then define a new upgrade using JSON by placing the following in
|
||||
* {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}.
|
||||
*
|
||||
* <pre>{@code
|
||||
* {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
|
||||
* <p>
|
||||
* {@snippet lang="json" :
|
||||
* {
|
||||
* "type": "my_mod:my_upgrade"
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
|
||||
* }
|
||||
* <p>
|
||||
* Finally, we need to register a model for our upgrade, see
|
||||
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
|
||||
*
|
||||
* <pre>{@code
|
||||
* // Register our model inside FMLClientSetupEvent
|
||||
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
|
||||
* }</pre>
|
||||
*/
|
||||
public interface ITurtleUpgrade extends UpgradeBase {
|
||||
/**
|
||||
* The registry in which turtle upgrades are stored.
|
||||
*/
|
||||
ResourceKey<Registry<ITurtleUpgrade>> REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade"));
|
||||
|
||||
/**
|
||||
* Create a {@link ResourceKey} for a turtle upgrade given a {@link ResourceLocation}.
|
||||
* <p>
|
||||
* This should only be called from within data generation code. Do not hard code references to your upgrades!
|
||||
*
|
||||
* @param id The id of the turtle upgrade.
|
||||
* @return The upgrade registry key.
|
||||
*/
|
||||
static ResourceKey<ITurtleUpgrade> createKey(ResourceLocation id) {
|
||||
return ResourceKey.create(REGISTRY, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The registry key for turtle upgrade types.
|
||||
*
|
||||
|
@@ -0,0 +1,157 @@
|
||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import dan200.computercraft.impl.upgrades.TurtleToolSpec;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A builder for custom turtle tool upgrades.
|
||||
* <p>
|
||||
* This can be used from your <a href="../upgrades/UpgradeType.html#datagen">data generator</a> code in order to
|
||||
* register turtle tools for your mod's tools.
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* {@snippet lang = "java":
|
||||
* import net.minecraft.data.worldgen.BootstrapContext;
|
||||
* import net.minecraft.resources.ResourceLocation;
|
||||
* import net.minecraft.world.item.Items;
|
||||
*
|
||||
* public void registerTool(BootstrapContext<ITurtleUpgrade> upgrades) {
|
||||
* TurtleToolBuilder.tool(new ResourceLocation("my_mod", "wooden_pickaxe"), Items.WOODEN_PICKAXE).register(upgrades);
|
||||
* }
|
||||
*}
|
||||
*/
|
||||
public final class TurtleToolBuilder {
|
||||
private final ResourceKey<ITurtleUpgrade> id;
|
||||
private final Item item;
|
||||
private Component adjective;
|
||||
private float damageMultiplier = TurtleToolSpec.DEFAULT_DAMAGE_MULTIPLIER;
|
||||
private @Nullable TagKey<Block> breakable;
|
||||
private boolean allowEnchantments = false;
|
||||
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
|
||||
|
||||
private TurtleToolBuilder(ResourceKey<ITurtleUpgrade> id, Item item) {
|
||||
this.id = id;
|
||||
adjective = Component.translatable(UpgradeBase.getDefaultAdjective(id.location()));
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public static TurtleToolBuilder tool(ResourceLocation id, Item item) {
|
||||
return new TurtleToolBuilder(ITurtleUpgrade.createKey(id), item);
|
||||
}
|
||||
|
||||
public static TurtleToolBuilder tool(ResourceKey<ITurtleUpgrade> id, Item item) {
|
||||
return new TurtleToolBuilder(id, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id for this turtle tool.
|
||||
*
|
||||
* @return The upgrade id.
|
||||
*/
|
||||
public ResourceKey<ITurtleUpgrade> id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a custom adjective for this tool. By default this takes its adjective from the upgrade id.
|
||||
*
|
||||
* @param adjective The new adjective to use.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public TurtleToolBuilder adjective(Component adjective) {
|
||||
this.adjective = adjective;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of damage a swing of this tool will do. This is multiplied by {@link Attributes#ATTACK_DAMAGE} to
|
||||
* get the final damage.
|
||||
*
|
||||
* @param damageMultiplier The damage multiplier.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public TurtleToolBuilder damageMultiplier(float damageMultiplier) {
|
||||
this.damageMultiplier = damageMultiplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
|
||||
* {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
|
||||
*
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public TurtleToolBuilder allowEnchantments() {
|
||||
allowEnchantments = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when the tool will consume durability.
|
||||
*
|
||||
* @param durability The durability predicate.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public TurtleToolBuilder consumeDurability(TurtleToolDurability durability) {
|
||||
consumeDurability = durability;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
|
||||
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
|
||||
* be broken.
|
||||
*
|
||||
* @param breakable The tag containing all blocks breakable by this item.
|
||||
* @return The tool builder, for further use.
|
||||
* @see ComputerCraftTags.Blocks
|
||||
*/
|
||||
public TurtleToolBuilder breakable(TagKey<Block> breakable) {
|
||||
this.breakable = breakable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the turtle tool upgrade.
|
||||
*
|
||||
* @return The constructed upgrade.
|
||||
*/
|
||||
public ITurtleUpgrade build() {
|
||||
return ComputerCraftAPIService.get().createTurtleTool(new TurtleToolSpec(
|
||||
adjective,
|
||||
item,
|
||||
damageMultiplier,
|
||||
allowEnchantments,
|
||||
consumeDurability,
|
||||
Optional.ofNullable(breakable)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build this upgrade and register it for datagen.
|
||||
*
|
||||
* @param upgrades The registry this upgrade should be added to.
|
||||
*/
|
||||
public void register(BootstrapContext<ITurtleUpgrade> upgrades) {
|
||||
upgrades.register(id(), build());
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@ import net.minecraft.world.item.ItemStack;
|
||||
/**
|
||||
* Indicates if an equipped turtle item will consume durability.
|
||||
*
|
||||
* @see TurtleUpgradeDataProvider.ToolBuilder#consumeDurability(TurtleToolDurability)
|
||||
* @see TurtleToolBuilder#consumeDurability(TurtleToolDurability)
|
||||
*/
|
||||
public enum TurtleToolDurability implements StringRepresentable {
|
||||
/**
|
||||
|
@@ -1,164 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.impl.upgrades.TurtleToolSpec;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A data provider to generate turtle upgrades.
|
||||
* <p>
|
||||
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
|
||||
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
||||
* generate them.
|
||||
*
|
||||
* @see ITurtleUpgrade
|
||||
*/
|
||||
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade> {
|
||||
public TurtleUpgradeDataProvider(PackOutput output) {
|
||||
super(output, "Turtle Upgrades", RegistryHelper.TURTLE_UPGRADE, ComputerCraftAPIService.get().turtleUpgradeCodec());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new turtle tool upgrade, such as a pickaxe or shovel.
|
||||
*
|
||||
* @param id The ID of this tool.
|
||||
* @param item The item used for tool actions. Note, this doesn't inherit all properties of the tool, you may need
|
||||
* to specify {@link ToolBuilder#damageMultiplier(float)} and {@link ToolBuilder#breakable(TagKey)}.
|
||||
* @return A tool builder,
|
||||
*/
|
||||
public final ToolBuilder tool(ResourceLocation id, Item item) {
|
||||
return new ToolBuilder(id, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for custom turtle tool upgrades.
|
||||
*
|
||||
* @see #tool(ResourceLocation, Item)
|
||||
*/
|
||||
public final class ToolBuilder {
|
||||
private final ResourceLocation id;
|
||||
private final Item toolItem;
|
||||
private Component adjective;
|
||||
private @Nullable Item craftingItem;
|
||||
private float damageMultiplier = TurtleToolSpec.DEFAULT_DAMAGE_MULTIPLIER;
|
||||
private @Nullable TagKey<Block> breakable;
|
||||
private boolean allowEnchantments = false;
|
||||
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
|
||||
|
||||
ToolBuilder(ResourceLocation id, Item toolItem) {
|
||||
this.id = id;
|
||||
adjective = Component.translatable(UpgradeBase.getDefaultAdjective(id));
|
||||
this.toolItem = toolItem;
|
||||
craftingItem = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a custom adjective for this tool. By default this takes its adjective from the tool item.
|
||||
*
|
||||
* @param adjective The new adjective to use.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder adjective(Component adjective) {
|
||||
this.adjective = adjective;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a custom item which is used to craft this upgrade. By default this is the same as the provided tool
|
||||
* item, but you may wish to override it.
|
||||
*
|
||||
* @param craftingItem The item used to craft this upgrade.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder craftingItem(Item craftingItem) {
|
||||
this.craftingItem = craftingItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of damage a swing of this tool will do. This is multiplied by {@link Attributes#ATTACK_DAMAGE} to
|
||||
* get the final damage.
|
||||
*
|
||||
* @param damageMultiplier The damage multiplier.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder damageMultiplier(float damageMultiplier) {
|
||||
this.damageMultiplier = damageMultiplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
|
||||
* {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
|
||||
*
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder allowEnchantments() {
|
||||
allowEnchantments = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when the tool will consume durability.
|
||||
*
|
||||
* @param durability The durability predicate.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder consumeDurability(TurtleToolDurability durability) {
|
||||
consumeDurability = durability;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
|
||||
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
|
||||
* be broken.
|
||||
*
|
||||
* @param breakable The tag containing all blocks breakable by this item.
|
||||
* @return The tool builder, for further use.
|
||||
* @see ComputerCraftTags.Blocks
|
||||
*/
|
||||
public ToolBuilder breakable(TagKey<Block> breakable) {
|
||||
this.breakable = breakable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this as an upgrade.
|
||||
*
|
||||
* @param add The callback given to {@link #addUpgrades(Consumer)}.
|
||||
*/
|
||||
public void add(Consumer<Upgrade<ITurtleUpgrade>> add) {
|
||||
upgrade(id, ComputerCraftAPIService.get().createTurtleTool(new TurtleToolSpec(
|
||||
adjective,
|
||||
Optional.ofNullable(craftingItem),
|
||||
toolItem,
|
||||
damageMultiplier,
|
||||
allowEnchantments,
|
||||
consumeDurability,
|
||||
Optional.ofNullable(breakable)
|
||||
))).add(add);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,154 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.upgrades;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A data generator/provider for turtle and pocket computer upgrades. This should not be extended directly, instead see
|
||||
* the other subclasses.
|
||||
*
|
||||
* @param <T> The base class of upgrades.
|
||||
*/
|
||||
public abstract class UpgradeDataProvider<T extends UpgradeBase> implements DataProvider {
|
||||
private final PackOutput output;
|
||||
private final String name;
|
||||
private final ResourceKey<Registry<T>> registryName;
|
||||
private final Codec<T> codec;
|
||||
|
||||
private @Nullable Map<ResourceKey<T>, T> upgrades;
|
||||
|
||||
@ApiStatus.Internal
|
||||
protected UpgradeDataProvider(PackOutput output, String name, ResourceKey<Registry<T>> registryName, Codec<T> codec) {
|
||||
this.output = output;
|
||||
this.name = name;
|
||||
this.registryName = registryName;
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new upgrade.
|
||||
*
|
||||
* @param id The ID of the upgrade to create.
|
||||
* @param upgrade The upgrade to add.
|
||||
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
|
||||
*/
|
||||
protected final Upgrade<T> upgrade(ResourceLocation id, T upgrade) {
|
||||
return new Upgrade<>(id, upgrade, j -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all turtle or pocket computer upgrades.
|
||||
* <p>
|
||||
* <strong>Example usage:</strong>
|
||||
* <pre>{@code
|
||||
* protected void addUpgrades(Consumer<Upgrade<ITurtleUpgrade>> addUpgrade) {
|
||||
* upgrade(new ResourceLocation("mymod", "speaker"), new TurtleSpeaker(new ItemStack(Items.NOTE_BLOCK))).add(addUpgrade);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param addUpgrade A callback used to register an upgrade.
|
||||
*/
|
||||
protected abstract void addUpgrades(Consumer<Upgrade<T>> addUpgrade);
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> run(CachedOutput cache) {
|
||||
var base = output.createPathProvider(PackOutput.Target.DATA_PACK, registryName.location().getNamespace() + "/" + registryName.location().getPath());
|
||||
|
||||
Map<ResourceKey<T>, T> upgrades = new LinkedHashMap<>();
|
||||
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
addUpgrades(upgrade -> {
|
||||
var id = ResourceKey.create(registryName, upgrade.id);
|
||||
if (upgrades.containsKey(id)) throw new IllegalStateException("Duplicate upgrade " + upgrade.id);
|
||||
|
||||
var json = (JsonObject) codec.encodeStart(JsonOps.INSTANCE, upgrade.upgrade).getOrThrow();
|
||||
upgrade.serialise.accept(json);
|
||||
|
||||
futures.add(DataProvider.saveStable(cache, json, base.json(upgrade.id)));
|
||||
|
||||
upgrades.put(id, upgrade.upgrade);
|
||||
});
|
||||
|
||||
this.upgrades = Collections.unmodifiableMap(upgrades);
|
||||
|
||||
return Util.sequenceFailFast(futures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered upgrades.
|
||||
*
|
||||
* @return The map of registered upgrades.
|
||||
*/
|
||||
public Map<ResourceKey<T>, T> getGeneratedUpgrades() {
|
||||
var upgrades = this.upgrades;
|
||||
if (upgrades == null) throw new IllegalStateException("Upgrades are not available yet");
|
||||
return upgrades;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructed upgrade instance, produced {@link #addUpgrades(Consumer)}.
|
||||
*
|
||||
* @param <T> The type of upgrade.
|
||||
*/
|
||||
public static final class Upgrade<T extends UpgradeBase> {
|
||||
private final ResourceLocation id;
|
||||
private final T upgrade;
|
||||
private final Consumer<JsonObject> serialise;
|
||||
|
||||
private Upgrade(ResourceLocation id, T upgrade, Consumer<JsonObject> serialise) {
|
||||
this.id = id;
|
||||
this.upgrade = upgrade;
|
||||
this.serialise = serialise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for registering an upgrade.
|
||||
*
|
||||
* @param add The callback given to {@link #addUpgrades(Consumer)}
|
||||
*/
|
||||
public void add(Consumer<Upgrade<T>> add) {
|
||||
add.accept(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link Upgrade} which requires the given mod to be present.
|
||||
* <p>
|
||||
* This uses mod-loader-specific hooks (Forge's crafting conditions and Fabric's resource conditions). If using
|
||||
* this in a multi-loader setup, you must generate resources separately for the two loaders.
|
||||
*
|
||||
* @param modId The id of the mod.
|
||||
* @return A new upgrade instance.
|
||||
*/
|
||||
public Upgrade<T> requireMod(String modId) {
|
||||
return new Upgrade<>(id, upgrade, json -> {
|
||||
PlatformHelper.get().addRequiredModCondition(json, modId);
|
||||
serialise.accept(json);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,11 +6,10 @@ package dan200.computercraft.api.upgrades;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
import dan200.computercraft.impl.upgrades.UpgradeTypeImpl;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.data.registries.RegistryPatchGenerator;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
|
||||
@@ -33,8 +32,35 @@ import java.util.function.Function;
|
||||
* {@link IPocketUpgrade#typeRegistry()}).
|
||||
* <p>
|
||||
* In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It
|
||||
* is recommended to do this via the data generators (see {@link TurtleUpgradeDataProvider} and
|
||||
* {@link PocketUpgradeDataProvider}).
|
||||
* is recommended to do this via the data generators.
|
||||
*
|
||||
* <h2 id="datagen">Data Generation</h2>
|
||||
* As turtle and pocket upgrades are just loaded using vanilla's dynamic loaders, one may use the same data generation
|
||||
* tools as you would for any other dynamic registry.
|
||||
* <p>
|
||||
* This can typically be done by extending vanilla's built-in registries using {@link RegistryPatchGenerator}, and then
|
||||
* writing out the new registries using {@code net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider}
|
||||
* on Fabric or {@code net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider} on Forge.
|
||||
* <p>
|
||||
* {@snippet lang="java" :
|
||||
* import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
* import net.minecraft.Util;
|
||||
* import net.minecraft.core.HolderLookup;
|
||||
* import net.minecraft.core.RegistrySetBuilder;
|
||||
* import net.minecraft.data.DataGenerator;
|
||||
* import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
|
||||
*
|
||||
* import java.util.concurrent.CompletableFuture;
|
||||
*
|
||||
* public void generate(DataGenerator.PackGenerator output, CompletableFuture<HolderLookup.Provider> registries) {
|
||||
* var newRegistries = RegistryPatchGenerator.createLookup(registries, Util.make(new RegistrySetBuilder(), builder -> {
|
||||
* builder.add(ITurtleUpgrade.REGISTRY, upgrades -> {
|
||||
* upgrades.register(ITurtleUpgrade.createKey(new ResourceLocation("my_mod", "my_upgrade")), new MyUpgrade());
|
||||
* });
|
||||
* }));
|
||||
* output.addProvider(o -> new DatapackBuiltinEntriesProvider(o, newRegistries, Set.of("my_mod")));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param <T> The upgrade subclass that this upgrade type represents.
|
||||
* @see ITurtleUpgrade
|
||||
|
@@ -1,54 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstraction layer for Forge and Fabric. See implementations for more details.
|
||||
* <p>
|
||||
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public interface PlatformHelper {
|
||||
/**
|
||||
* Get the current {@link PlatformHelper} instance.
|
||||
*
|
||||
* @return The current instance.
|
||||
*/
|
||||
static PlatformHelper get() {
|
||||
var instance = Instance.INSTANCE;
|
||||
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
|
||||
* {@link UpgradeDataProvider}.
|
||||
*
|
||||
* @param object The JSON object we're generating.
|
||||
* @param modId The mod ID that we require.
|
||||
*/
|
||||
void addRequiredModCondition(JsonObject object, String modId);
|
||||
|
||||
final class Instance {
|
||||
static final @Nullable PlatformHelper INSTANCE;
|
||||
static final @Nullable Throwable ERROR;
|
||||
|
||||
static {
|
||||
// We don't want class initialisation to fail here (as that results in confusing errors). Instead, capture
|
||||
// the error and rethrow it when accessing. This should be JITted away in the common case.
|
||||
var helper = Services.tryLoad(PlatformHelper.class);
|
||||
INSTANCE = helper.instance();
|
||||
ERROR = helper.error();
|
||||
}
|
||||
|
||||
private Instance() {
|
||||
}
|
||||
}
|
||||
}
|
@@ -22,8 +22,7 @@ import java.util.Optional;
|
||||
* The template for a turtle tool.
|
||||
*
|
||||
* @param adjective The adjective for this tool.
|
||||
* @param craftItem The item used to craft this tool.
|
||||
* @param toolItem The actual tool used.
|
||||
* @param item The tool used.
|
||||
* @param damageMultiplier The damage multiplier for this tool.
|
||||
* @param allowEnchantments Whether to allow enchantments.
|
||||
* @param consumeDurability When to consume durability.
|
||||
@@ -31,8 +30,7 @@ import java.util.Optional;
|
||||
*/
|
||||
public record TurtleToolSpec(
|
||||
Component adjective,
|
||||
Optional<Item> craftItem,
|
||||
Item toolItem,
|
||||
Item item,
|
||||
float damageMultiplier,
|
||||
boolean allowEnchantments,
|
||||
TurtleToolDurability consumeDurability,
|
||||
@@ -42,8 +40,7 @@ public record TurtleToolSpec(
|
||||
|
||||
public static final MapCodec<TurtleToolSpec> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
ComponentSerialization.CODEC.fieldOf("adjective").forGetter(TurtleToolSpec::adjective),
|
||||
BuiltInRegistries.ITEM.byNameCodec().optionalFieldOf("craftingItem").forGetter(TurtleToolSpec::craftItem),
|
||||
BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::toolItem),
|
||||
BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::item),
|
||||
Codec.FLOAT.optionalFieldOf("damageMultiplier", DEFAULT_DAMAGE_MULTIPLIER).forGetter(TurtleToolSpec::damageMultiplier),
|
||||
Codec.BOOL.optionalFieldOf("allowEnchantments", false).forGetter(TurtleToolSpec::allowEnchantments),
|
||||
TurtleToolDurability.CODEC.optionalFieldOf("consumeDurability", TurtleToolDurability.NEVER).forGetter(TurtleToolSpec::consumeDurability),
|
||||
|
@@ -129,3 +129,5 @@ val runData by tasks.registering(MergeTrees::class) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
|
||||
|
@@ -6,12 +6,15 @@ package dan200.computercraft.client.integration.emi;
|
||||
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.integration.RecipeModHelpers;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
import dev.emi.emi.api.EmiEntrypoint;
|
||||
import dev.emi.emi.api.EmiPlugin;
|
||||
import dev.emi.emi.api.EmiRegistry;
|
||||
import dev.emi.emi.api.stack.Comparison;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.function.BiPredicate;
|
||||
@@ -25,6 +28,10 @@ public class EMIComputerCraft implements EmiPlugin {
|
||||
|
||||
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison);
|
||||
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
|
||||
|
||||
for (var stack : RecipeModHelpers.getExtraStacks(Minecraft.getInstance().level.registryAccess())) {
|
||||
registry.addEmiStack(EmiStack.of(stack));
|
||||
}
|
||||
}
|
||||
|
||||
private static final Comparison turtleComparison = compareStacks((left, right)
|
||||
|
@@ -27,7 +27,6 @@ import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
@@ -48,8 +47,6 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
||||
*/
|
||||
private static final float MARGIN = (float) (MonitorBlockEntity.RENDER_MARGIN * 1.1);
|
||||
|
||||
private static final Matrix3f IDENTITY_NORMAL = new Matrix3f().identity();
|
||||
|
||||
private static @Nullable ByteBuffer backingBuffer;
|
||||
|
||||
private static long lastFrame = -1;
|
||||
|
@@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -23,8 +24,7 @@ import java.util.stream.Stream;
|
||||
public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
|
||||
@Override
|
||||
public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
|
||||
var component = data.get(ModRegistry.DataComponents.ON.get());
|
||||
var active = component != null && component.isPresent() && component.get();
|
||||
var active = DataComponentUtil.isPresent(data, ModRegistry.DataComponents.ON.get(), x -> x);
|
||||
|
||||
var models = upgrade.advanced() ? ModemModels.ADVANCED : ModemModels.NORMAL;
|
||||
return side == TurtleSide.LEFT
|
||||
|
@@ -11,14 +11,12 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.impl.UpgradeManager;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -32,14 +30,6 @@ public final class TurtleUpgradeModellers {
|
||||
private static final Map<UpgradeType<? extends ITurtleUpgrade>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
|
||||
private static volatile boolean fetchedModels;
|
||||
|
||||
/**
|
||||
* In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
|
||||
* {@link TurtleUpgradeModeller}, we maintain a cache here.
|
||||
* <p>
|
||||
* Turtle upgrades may be removed as part of datapack reloads, so we use a weak map to avoid the memory leak.
|
||||
*/
|
||||
private static final WeakHashMap<ITurtleUpgrade, TurtleUpgradeModeller<?>> modelCache = new WeakHashMap<>();
|
||||
|
||||
private TurtleUpgradeModellers() {
|
||||
}
|
||||
|
||||
@@ -57,20 +47,17 @@ public final class TurtleUpgradeModellers {
|
||||
}
|
||||
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||
return modeller.getModel(upgrade, access, side, access.getUpgradeData(side));
|
||||
return getModeller(upgrade).getModel(upgrade, access, side, access.getUpgradeData(side));
|
||||
}
|
||||
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, DataComponentPatch data, TurtleSide side) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||
return modeller.getModel(upgrade, null, side, data);
|
||||
return getModeller(upgrade).getModel(upgrade, null, side, data);
|
||||
}
|
||||
|
||||
private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgrade) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> getModeller(T upgrade) {
|
||||
var modeller = turtleModels.get(upgrade.getType());
|
||||
return modeller == null ? NULL_TURTLE_MODELLER : modeller;
|
||||
return (TurtleUpgradeModeller<T>) (modeller == null ? NULL_TURTLE_MODELLER : modeller);
|
||||
}
|
||||
|
||||
public static Stream<ResourceLocation> getDependencies() {
|
||||
|
@@ -206,8 +206,10 @@
|
||||
"item.computercraft.treasure_disk": "Floppy Disk",
|
||||
"itemGroup.computercraft": "ComputerCraft",
|
||||
"tag.item.computercraft.computer": "Computers",
|
||||
"tag.item.computercraft.dyeable": "Dyable items",
|
||||
"tag.item.computercraft.monitor": "Monitors",
|
||||
"tag.item.computercraft.turtle": "Turtles",
|
||||
"tag.item.computercraft.turtle_can_place": "Turtle-placeable items",
|
||||
"tag.item.computercraft.wired_modem": "Wired modems",
|
||||
"tracking_field.computercraft.avg": "%s (avg)",
|
||||
"tracking_field.computercraft.computer_tasks.name": "Tasks",
|
||||
|
@@ -1 +1,12 @@
|
||||
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_advanced"}}
|
||||
{
|
||||
"parent": "computercraft:block/turtle_base",
|
||||
"textures": {
|
||||
"back": "computercraft:block/turtle_advanced_back",
|
||||
"backpack": "computercraft:block/turtle_advanced_backpack",
|
||||
"bottom": "computercraft:block/turtle_advanced_bottom",
|
||||
"front": "computercraft:block/turtle_advanced_front",
|
||||
"left": "computercraft:block/turtle_advanced_left",
|
||||
"right": "computercraft:block/turtle_advanced_right",
|
||||
"top": "computercraft:block/turtle_advanced_top"
|
||||
}
|
||||
}
|
||||
|
@@ -1 +1,12 @@
|
||||
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_normal"}}
|
||||
{
|
||||
"parent": "computercraft:block/turtle_base",
|
||||
"textures": {
|
||||
"back": "computercraft:block/turtle_normal_back",
|
||||
"backpack": "computercraft:block/turtle_normal_backpack",
|
||||
"bottom": "computercraft:block/turtle_normal_bottom",
|
||||
"front": "computercraft:block/turtle_normal_front",
|
||||
"left": "computercraft:block/turtle_normal_left",
|
||||
"right": "computercraft:block/turtle_normal_right",
|
||||
"top": "computercraft:block/turtle_normal_top"
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"key": {"#": {"item": "minecraft:stone"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"pattern": [" # ", "#R#", " # "],
|
||||
"result": {"count": 6, "id": "computercraft:cable"}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "G": {"tag": "c:glass_panes"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"key": {"#": {"item": "minecraft:stone"}, "G": {"tag": "c:glass_panes"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"pattern": ["###", "#R#", "#G#"],
|
||||
"result": {"count": 1, "id": "computercraft:computer_normal"}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"key": {"#": {"item": "minecraft:stone"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"pattern": ["###", "#R#", "#R#"],
|
||||
"result": {"count": 1, "id": "computercraft:disk_drive"}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "G": {"tag": "c:glass_panes"}},
|
||||
"key": {"#": {"item": "minecraft:stone"}, "G": {"tag": "c:glass_panes"}},
|
||||
"pattern": ["###", "#G#", "###"],
|
||||
"result": {"count": 1, "id": "computercraft:monitor_normal"}
|
||||
}
|
||||
|
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "A": {"item": "minecraft:golden_apple"}, "G": {"tag": "c:glass_panes"}},
|
||||
"key": {
|
||||
"#": {"item": "minecraft:stone"},
|
||||
"A": {"item": "minecraft:golden_apple"},
|
||||
"G": {"tag": "c:glass_panes"}
|
||||
},
|
||||
"pattern": ["###", "#A#", "#G#"],
|
||||
"result": {"count": 1, "id": "computercraft:pocket_computer_normal"}
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"type": "computercraft:impostor_shapeless",
|
||||
"type": "computercraft:printout",
|
||||
"category": "redstone",
|
||||
"ingredients": [
|
||||
"ingredients": [{"tag": "c:strings"}],
|
||||
"min_printouts": 2,
|
||||
"printout": [
|
||||
{"item": "computercraft:printed_page"},
|
||||
{"item": "computercraft:printed_page"},
|
||||
{"tag": "c:strings"}
|
||||
{"item": "computercraft:printed_pages"},
|
||||
{"item": "minecraft:paper"}
|
||||
],
|
||||
"result": {"count": 1, "id": "computercraft:printed_pages"}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "D": {"tag": "c:dyes"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"key": {"#": {"item": "minecraft:stone"}, "D": {"tag": "c:dyes"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"pattern": ["###", "#R#", "#D#"],
|
||||
"result": {"count": 1, "id": "computercraft:printer"}
|
||||
}
|
||||
|
@@ -1 +0,0 @@
|
||||
{"type": "computercraft:printout", "category": "misc"}
|
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "N": {"item": "minecraft:note_block"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"key": {
|
||||
"#": {"item": "minecraft:stone"},
|
||||
"N": {"item": "minecraft:note_block"},
|
||||
"R": {"tag": "c:dusts/redstone"}
|
||||
},
|
||||
"pattern": ["###", "#N#", "#R#"],
|
||||
"result": {"count": 1, "id": "computercraft:speaker"}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"key": {"#": {"tag": "c:stones"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"key": {"#": {"item": "minecraft:stone"}, "R": {"tag": "c:dusts/redstone"}},
|
||||
"pattern": ["###", "#R#", "###"],
|
||||
"result": {"count": 1, "id": "computercraft:wired_modem"}
|
||||
}
|
||||
|
@@ -38,6 +38,9 @@ import static net.minecraft.data.models.model.TextureMapping.getBlockTexture;
|
||||
|
||||
class BlockModelProvider {
|
||||
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
|
||||
private static final TextureSlot LEFT = TextureSlot.create("left");
|
||||
private static final TextureSlot RIGHT = TextureSlot.create("right");
|
||||
private static final TextureSlot BACKPACK = TextureSlot.create("backpack");
|
||||
|
||||
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
|
||||
@@ -58,7 +61,7 @@ class BlockModelProvider {
|
||||
private static final ModelTemplate TURTLE = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_base")),
|
||||
Optional.empty(),
|
||||
TextureSlot.TEXTURE
|
||||
TextureSlot.FRONT, TextureSlot.BACK, TextureSlot.TOP, TextureSlot.BOTTOM, LEFT, RIGHT, BACKPACK
|
||||
);
|
||||
private static final ModelTemplate TURTLE_UPGRADE_LEFT = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_upgrade_base_left")),
|
||||
@@ -167,7 +170,16 @@ class BlockModelProvider {
|
||||
}
|
||||
|
||||
private static void registerTurtle(BlockModelGenerators generators, TurtleBlock block) {
|
||||
var model = TURTLE.create(block, TextureMapping.defaultTexture(block), generators.modelOutput);
|
||||
var model = TURTLE.create(block, new TextureMapping()
|
||||
.put(TextureSlot.FRONT, getBlockTexture(block, "_front"))
|
||||
.put(TextureSlot.BACK, getBlockTexture(block, "_back"))
|
||||
.put(TextureSlot.TOP, getBlockTexture(block, "_top"))
|
||||
.put(TextureSlot.BOTTOM, getBlockTexture(block, "_bottom"))
|
||||
.put(LEFT, getBlockTexture(block, "_left"))
|
||||
.put(RIGHT, getBlockTexture(block, "_right"))
|
||||
.put(BACKPACK, getBlockTexture(block, "_backpack")),
|
||||
generators.modelOutput
|
||||
);
|
||||
generators.blockStateOutput.accept(
|
||||
MultiVariantGenerator.multiVariant(block, Variant.variant().with(VariantProperties.MODEL, model))
|
||||
.with(createHorizontalFacingDispatch())
|
||||
|
@@ -5,7 +5,9 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
import net.minecraft.data.DataProvider;
|
||||
@@ -20,7 +22,6 @@ import net.minecraft.world.level.block.Block;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -32,25 +33,25 @@ public final class DataProviders {
|
||||
}
|
||||
|
||||
public static void add(GeneratorSink generator) {
|
||||
var turtleUpgrades = generator.add(TurtleUpgradeProvider::new);
|
||||
var pocketUpgrades = generator.add(PocketUpgradeProvider::new);
|
||||
var fullRegistryPatch = RegistryPatchGenerator.createLookup(
|
||||
generator.registries(),
|
||||
Util.make(new RegistrySetBuilder(), builder -> {
|
||||
builder.add(ITurtleUpgrade.REGISTRY, TurtleUpgradeProvider::addUpgrades);
|
||||
builder.add(IPocketUpgrade.REGISTRY, PocketUpgradeProvider::addUpgrades);
|
||||
}));
|
||||
var fullRegistries = fullRegistryPatch.thenApply(RegistrySetBuilder.PatchedRegistries::full);
|
||||
|
||||
generator.add((out, registries) -> {
|
||||
var builder = new RegistrySetBuilder();
|
||||
builder.add(ModRegistry.TURTLE_UPGRADE, bs -> turtleUpgrades.getGeneratedUpgrades().forEach(bs::register));
|
||||
builder.add(ModRegistry.POCKET_UPGRADE, bs -> pocketUpgrades.getGeneratedUpgrades().forEach(bs::register));
|
||||
|
||||
return new RecipeProvider(out, generator.createPatchedRegistries(registries, builder).thenApply(RegistrySetBuilder.PatchedRegistries::full));
|
||||
});
|
||||
generator.registries(fullRegistryPatch);
|
||||
generator.add(out -> new RecipeProvider(out, fullRegistries));
|
||||
|
||||
var blockTags = generator.blockTags(TagProvider::blockTags);
|
||||
generator.itemTags(TagProvider::itemTags, blockTags);
|
||||
|
||||
generator.add((out, registries) -> new net.minecraft.data.loot.LootTableProvider(out, Set.of(), LootTableProvider.getTables(), registries));
|
||||
generator.add(out -> new net.minecraft.data.loot.LootTableProvider(out, Set.of(), LootTableProvider.getTables(), fullRegistries));
|
||||
|
||||
generator.add(out -> new ModelProvider(out, BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels));
|
||||
|
||||
generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades));
|
||||
generator.add(out -> new LanguageProvider(out, fullRegistries));
|
||||
|
||||
// Unfortunately we rely on some client-side classes in this code. We just load in the client side data provider
|
||||
// and invoke that.
|
||||
@@ -63,9 +64,9 @@ public final class DataProviders {
|
||||
}
|
||||
|
||||
public interface GeneratorSink {
|
||||
<T extends DataProvider> T add(DataProvider.Factory<T> factory);
|
||||
CompletableFuture<HolderLookup.Provider> registries();
|
||||
|
||||
<T extends DataProvider> T add(BiFunction<PackOutput, CompletableFuture<HolderLookup.Provider>, T> factory);
|
||||
<T extends DataProvider> T add(DataProvider.Factory<T> factory);
|
||||
|
||||
<T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output);
|
||||
|
||||
@@ -74,16 +75,10 @@ public final class DataProviders {
|
||||
TagsProvider<Item> itemTags(Consumer<TagProvider.ItemTagConsumer> tags, TagsProvider<Block> blocks);
|
||||
|
||||
/**
|
||||
* Extend our registries with additional entries.
|
||||
* Build new dynamic registries and save them to a pack.
|
||||
*
|
||||
* @param registries The existing registries.
|
||||
* @param patch The new registries to apply.
|
||||
* @return The built registries.
|
||||
* @param registries The patched registries to write.
|
||||
*/
|
||||
default CompletableFuture<RegistrySetBuilder.PatchedRegistries> createPatchedRegistries(
|
||||
CompletableFuture<HolderLookup.Provider> registries, RegistrySetBuilder patch
|
||||
) {
|
||||
return RegistryPatchGenerator.createLookup(registries, patch);
|
||||
}
|
||||
void registries(CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries);
|
||||
}
|
||||
}
|
||||
|
@@ -7,8 +7,8 @@ package dan200.computercraft.data;
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.core.metrics.Metric;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
@@ -17,6 +17,7 @@ import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
||||
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||
import dan200.computercraft.shared.config.ConfigFile;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
@@ -34,27 +35,28 @@ import java.util.stream.Stream;
|
||||
|
||||
public final class LanguageProvider implements DataProvider {
|
||||
private final PackOutput output;
|
||||
private final TurtleUpgradeDataProvider turtleUpgrades;
|
||||
private final PocketUpgradeDataProvider pocketUpgrades;
|
||||
private final CompletableFuture<HolderLookup.Provider> registries;
|
||||
|
||||
private final Map<String, String> translations = new HashMap<>();
|
||||
|
||||
public LanguageProvider(PackOutput output, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
|
||||
public LanguageProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
|
||||
this.output = output;
|
||||
this.turtleUpgrades = turtleUpgrades;
|
||||
this.pocketUpgrades = pocketUpgrades;
|
||||
this.registries = registries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> run(CachedOutput cachedOutput) {
|
||||
addTranslations();
|
||||
getExpectedKeys().forEach(x -> {
|
||||
if (!translations.containsKey(x)) throw new IllegalStateException("No translation for " + x);
|
||||
});
|
||||
|
||||
var json = new JsonObject();
|
||||
for (var pair : translations.entrySet()) json.addProperty(pair.getKey(), pair.getValue());
|
||||
return DataProvider.saveStable(cachedOutput, json, output.getOutputFolder().resolve("assets/" + ComputerCraftAPI.MOD_ID + "/lang/en_us.json"));
|
||||
return registries.thenCompose(registries -> {
|
||||
getExpectedKeys(registries).forEach(x -> {
|
||||
if (!translations.containsKey(x)) throw new IllegalStateException("No translation for " + x);
|
||||
});
|
||||
|
||||
var json = new JsonObject();
|
||||
for (var pair : translations.entrySet()) json.addProperty(pair.getKey(), pair.getValue());
|
||||
return DataProvider.saveStable(cachedOutput, json, output.getOutputFolder().resolve("assets/" + ComputerCraftAPI.MOD_ID + "/lang/en_us.json"));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,6 +107,8 @@ public final class LanguageProvider implements DataProvider {
|
||||
add(ComputerCraftTags.Items.TURTLE, "Turtles");
|
||||
add(ComputerCraftTags.Items.WIRED_MODEM, "Wired modems");
|
||||
add(ComputerCraftTags.Items.MONITOR, "Monitors");
|
||||
add(ComputerCraftTags.Items.DYEABLE, "Dyable items");
|
||||
add(ComputerCraftTags.Items.TURTLE_CAN_PLACE, "Turtle-placeable items");
|
||||
|
||||
// Turtle/pocket upgrades
|
||||
add("upgrade.minecraft.diamond_sword.adjective", "Melee");
|
||||
@@ -280,7 +284,7 @@ public final class LanguageProvider implements DataProvider {
|
||||
addConfigEntry(ConfigSpec.uploadNagDelay, "Upload nag delay");
|
||||
}
|
||||
|
||||
private Stream<String> getExpectedKeys() {
|
||||
private Stream<String> getExpectedKeys(HolderLookup.Provider registries) {
|
||||
return Stream.of(
|
||||
BuiltInRegistries.BLOCK.holders()
|
||||
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
@@ -288,8 +292,8 @@ public final class LanguageProvider implements DataProvider {
|
||||
BuiltInRegistries.ITEM.holders()
|
||||
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(x -> x.value().getDescriptionId()),
|
||||
turtleUpgrades.getGeneratedUpgrades().values().stream().flatMap(x -> getTranslationKeys(x.getAdjective())),
|
||||
pocketUpgrades.getGeneratedUpgrades().values().stream().flatMap(x -> getTranslationKeys(x.getAdjective())),
|
||||
registries.lookupOrThrow(ITurtleUpgrade.REGISTRY).listElements().flatMap(x -> getTranslationKeys(x.value().getAdjective())),
|
||||
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements().flatMap(x -> getTranslationKeys(x.value().getAdjective())),
|
||||
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
|
||||
ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey),
|
||||
ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey),
|
||||
|
@@ -5,7 +5,7 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
|
@@ -6,30 +6,23 @@ package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static dan200.computercraft.shared.ModRegistry.Items;
|
||||
|
||||
class PocketUpgradeProvider extends PocketUpgradeDataProvider {
|
||||
PocketUpgradeProvider(PackOutput output) {
|
||||
super(output);
|
||||
class PocketUpgradeProvider {
|
||||
public static void addUpgrades(BootstrapContext<IPocketUpgrade> upgrades) {
|
||||
upgrades.register(id("speaker"), new PocketSpeaker(new ItemStack(Items.SPEAKER.get())));
|
||||
upgrades.register(id("wireless_modem_normal"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_NORMAL.get()), false));
|
||||
upgrades.register(id("wireless_modem_advanced"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_ADVANCED.get()), true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addUpgrades(Consumer<Upgrade<IPocketUpgrade>> addUpgrade) {
|
||||
upgrade(id("speaker"), new PocketSpeaker(new ItemStack(Items.SPEAKER.get()))).add(addUpgrade);
|
||||
upgrade(id("wireless_modem_normal"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_NORMAL.get()), false)).add(addUpgrade);
|
||||
upgrade(id("wireless_modem_advanced"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_ADVANCED.get()), true)).add(addUpgrade);
|
||||
}
|
||||
|
||||
private static ResourceLocation id(String id) {
|
||||
return new ResourceLocation(ComputerCraftAPI.MOD_ID, id);
|
||||
private static ResourceKey<IPocketUpgrade> id(String id) {
|
||||
return ResourceKey.create(IPocketUpgrade.REGISTRY, new ResourceLocation(ComputerCraftAPI.MOD_ID, id));
|
||||
}
|
||||
}
|
||||
|
@@ -8,11 +8,13 @@ import com.google.gson.JsonObject;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.data.recipe.ShapedSpecBuilder;
|
||||
import dan200.computercraft.data.recipe.ShapelessSpecBuilder;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.ClearColourRecipe;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
@@ -54,14 +56,12 @@ import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
|
||||
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
|
||||
@@ -98,7 +98,6 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
turtleUpgrades(add, registries);
|
||||
turtleOverlays(add);
|
||||
|
||||
addSpecial(add, new PrintoutRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new DiskRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new ColourableRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new ClearColourRecipe(CraftingBookCategory.MISC));
|
||||
@@ -119,7 +118,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.requires(Items.PAPER)
|
||||
.requires(DyeItem.byColor(ofColour(colour)))
|
||||
.group("computercraft:disk")
|
||||
.unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get()))
|
||||
.unlockedBy("has_drive", inventoryChange(ModRegistry.Items.DISK_DRIVE.get()))
|
||||
.build(ImpostorShapelessRecipe::new)
|
||||
.save(output, new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1)));
|
||||
}
|
||||
@@ -139,7 +138,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
for (var turtleItem : turtleItems()) {
|
||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||
|
||||
registries.lookup(ModRegistry.TURTLE_UPGRADE).map(HolderLookup::listElements).orElse(Stream.empty()).forEach(upgradeHolder -> {
|
||||
registries.lookupOrThrow(ITurtleUpgrade.REGISTRY).listElements().forEach(upgradeHolder -> {
|
||||
var upgrade = upgradeHolder.value();
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder)))
|
||||
@@ -171,7 +170,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
for (var pocket : pocketComputerItems()) {
|
||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
|
||||
|
||||
registries.lookup(ModRegistry.POCKET_UPGRADE).map(HolderLookup::listElements).orElse(Stream.empty()).forEach(upgradeHolder -> {
|
||||
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements().forEach(upgradeHolder -> {
|
||||
var upgrade = upgradeHolder.value();
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder)))
|
||||
@@ -238,25 +237,25 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.pattern(" # ")
|
||||
.pattern("#R#")
|
||||
.pattern(" # ")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('R', ingredients.redstone())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_NORMAL.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_NORMAL.get())
|
||||
.pattern("###")
|
||||
.pattern("#R#")
|
||||
.pattern("#G#")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('R', ingredients.redstone())
|
||||
.define('G', ingredients.glassPane())
|
||||
.unlockedBy("has_redstone", inventoryChange(itemPredicate(ingredients.redstone())))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_ADVANCED.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#R#")
|
||||
.pattern("#G#")
|
||||
@@ -278,18 +277,18 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_COMMAND.get())
|
||||
.pattern("###")
|
||||
.pattern("#R#")
|
||||
.pattern("#G#")
|
||||
.define('#', ingredients.goldIngot())
|
||||
.define('R', Blocks.COMMAND_BLOCK)
|
||||
.define('R', Items.COMMAND_BLOCK)
|
||||
.define('G', ingredients.glassPane())
|
||||
.unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK))
|
||||
.unlockedBy("has_components", inventoryChange(Items.COMMAND_BLOCK))
|
||||
.save(add);
|
||||
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_NORMAL.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
.pattern("#I#")
|
||||
@@ -301,7 +300,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.save(add);
|
||||
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
.pattern("#I#")
|
||||
@@ -313,7 +312,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.save(add);
|
||||
|
||||
ShapedSpecBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#C#")
|
||||
.pattern(" B ")
|
||||
@@ -325,27 +324,27 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.DISK_DRIVE.get())
|
||||
.pattern("###")
|
||||
.pattern("#R#")
|
||||
.pattern("#R#")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('R', ingredients.redstone())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_NORMAL.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.MONITOR_NORMAL.get())
|
||||
.pattern("###")
|
||||
.pattern("#G#")
|
||||
.pattern("###")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('G', ingredients.glassPane())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_ADVANCED.get(), 4)
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.MONITOR_ADVANCED.get(), 4)
|
||||
.pattern("###")
|
||||
.pattern("#G#")
|
||||
.pattern("###")
|
||||
@@ -359,7 +358,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.pattern("###")
|
||||
.pattern("#A#")
|
||||
.pattern("#G#")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('A', Items.GOLDEN_APPLE)
|
||||
.define('G', ingredients.glassPane())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
@@ -390,23 +389,23 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade"));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTER.get())
|
||||
.pattern("###")
|
||||
.pattern("#R#")
|
||||
.pattern("#D#")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('R', ingredients.redstone())
|
||||
.define('D', ingredients.dye())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.SPEAKER.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.SPEAKER.get())
|
||||
.pattern("###")
|
||||
.pattern("#N#")
|
||||
.pattern("#R#")
|
||||
.define('#', ingredients.stone())
|
||||
.define('N', Blocks.NOTE_BLOCK)
|
||||
.define('#', Items.STONE)
|
||||
.define('N', Items.NOTE_BLOCK)
|
||||
.define('R', ingredients.redstone())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.save(add);
|
||||
@@ -416,42 +415,42 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.pattern("###")
|
||||
.pattern("#R#")
|
||||
.pattern("###")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('R', ingredients.redstone())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.unlockedBy("has_cable", inventoryChange(ModRegistry.Items.CABLE.get()))
|
||||
.save(add);
|
||||
|
||||
ShapelessRecipeBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRED_MODEM_FULL.get())
|
||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM_FULL.get())
|
||||
.requires(ModRegistry.Items.WIRED_MODEM.get())
|
||||
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_from"));
|
||||
ShapelessRecipeBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get())
|
||||
.requires(ModRegistry.Blocks.WIRED_MODEM_FULL.get())
|
||||
.requires(ModRegistry.Items.WIRED_MODEM_FULL.get())
|
||||
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_to"));
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRELESS_MODEM_NORMAL.get())
|
||||
.pattern("###")
|
||||
.pattern("#E#")
|
||||
.pattern("###")
|
||||
.define('#', ingredients.stone())
|
||||
.define('#', Items.STONE)
|
||||
.define('E', ingredients.enderPearl())
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.save(add);
|
||||
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get())
|
||||
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get())
|
||||
.pattern("###")
|
||||
.pattern("#E#")
|
||||
.pattern("###")
|
||||
.define('#', ingredients.goldIngot())
|
||||
.define('E', Items.ENDER_EYE)
|
||||
.unlockedBy("has_computer", inventoryChange(COMPUTER))
|
||||
.unlockedBy("has_wireless", inventoryChange(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get()))
|
||||
.unlockedBy("has_wireless", inventoryChange(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()))
|
||||
.save(add);
|
||||
|
||||
ShapelessSpecBuilder
|
||||
@@ -470,21 +469,25 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.build()
|
||||
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200"));
|
||||
|
||||
var pages = Ingredient.of(
|
||||
ModRegistry.Items.PRINTED_PAGE.get(),
|
||||
ModRegistry.Items.PRINTED_PAGES.get(),
|
||||
Items.PAPER
|
||||
);
|
||||
|
||||
ShapelessSpecBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_PAGES.get())
|
||||
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 2)
|
||||
.requires(ingredients.string())
|
||||
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
|
||||
.build(ImpostorShapelessRecipe::new)
|
||||
.unlockedBy("has_printer", inventoryChange(ModRegistry.Items.PRINTER.get()))
|
||||
.build(x -> new PrintoutRecipe(x, pages, 2))
|
||||
.save(add);
|
||||
|
||||
ShapelessSpecBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_BOOK.get())
|
||||
.requires(ingredients.leather())
|
||||
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 1)
|
||||
.requires(ingredients.string())
|
||||
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
|
||||
.build(ImpostorShapelessRecipe::new)
|
||||
.unlockedBy("has_printer", inventoryChange(ModRegistry.Items.PRINTER.get()))
|
||||
.build(x -> new PrintoutRecipe(x, pages, 1))
|
||||
.save(add);
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.integration.ExternalModTags;
|
||||
import net.minecraft.core.Registry;
|
||||
|
@@ -5,45 +5,40 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.ComputerCraftTags.Blocks;
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable;
|
||||
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
||||
import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import static dan200.computercraft.api.turtle.TurtleToolBuilder.tool;
|
||||
|
||||
class TurtleUpgradeProvider extends TurtleUpgradeDataProvider {
|
||||
TurtleUpgradeProvider(PackOutput output) {
|
||||
super(output);
|
||||
class TurtleUpgradeProvider {
|
||||
public static void addUpgrades(BootstrapContext<ITurtleUpgrade> upgrades) {
|
||||
upgrades.register(id("speaker"), new TurtleSpeaker(new ItemStack(ModRegistry.Items.SPEAKER.get())));
|
||||
upgrades.register(vanilla("crafting_table"), new TurtleCraftingTable(new ItemStack(Items.CRAFTING_TABLE)));
|
||||
upgrades.register(id("wireless_modem_normal"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()), false));
|
||||
upgrades.register(id("wireless_modem_advanced"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get()), true));
|
||||
|
||||
tool(vanilla("diamond_axe").location(), Items.DIAMOND_AXE).damageMultiplier(6.0f).register(upgrades);
|
||||
tool(vanilla("diamond_pickaxe"), Items.DIAMOND_PICKAXE).register(upgrades);
|
||||
tool(vanilla("diamond_hoe"), Items.DIAMOND_HOE).breakable(ComputerCraftTags.Blocks.TURTLE_HOE_BREAKABLE).register(upgrades);
|
||||
tool(vanilla("diamond_shovel"), Items.DIAMOND_SHOVEL).breakable(ComputerCraftTags.Blocks.TURTLE_SHOVEL_BREAKABLE).register(upgrades);
|
||||
tool(vanilla("diamond_sword"), Items.DIAMOND_SWORD).breakable(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).damageMultiplier(9.0f).register(upgrades);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addUpgrades(Consumer<Upgrade<ITurtleUpgrade>> addUpgrade) {
|
||||
upgrade(id("speaker"), new TurtleSpeaker(new ItemStack(ModRegistry.Items.SPEAKER.get()))).add(addUpgrade);
|
||||
upgrade(vanilla("crafting_table"), new TurtleCraftingTable(new ItemStack(Items.CRAFTING_TABLE))).add(addUpgrade);
|
||||
upgrade(id("wireless_modem_normal"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()), false)).add(addUpgrade);
|
||||
upgrade(id("wireless_modem_advanced"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get()), true)).add(addUpgrade);
|
||||
|
||||
tool(vanilla("diamond_axe"), Items.DIAMOND_AXE).damageMultiplier(6.0f).add(addUpgrade);
|
||||
tool(vanilla("diamond_pickaxe"), Items.DIAMOND_PICKAXE).add(addUpgrade);
|
||||
tool(vanilla("diamond_hoe"), Items.DIAMOND_HOE).breakable(Blocks.TURTLE_HOE_BREAKABLE).add(addUpgrade);
|
||||
tool(vanilla("diamond_shovel"), Items.DIAMOND_SHOVEL).breakable(Blocks.TURTLE_SHOVEL_BREAKABLE).add(addUpgrade);
|
||||
tool(vanilla("diamond_sword"), Items.DIAMOND_SWORD).breakable(Blocks.TURTLE_SWORD_BREAKABLE).damageMultiplier(9.0f).add(addUpgrade);
|
||||
private static ResourceKey<ITurtleUpgrade> id(String id) {
|
||||
return ITurtleUpgrade.createKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, id));
|
||||
}
|
||||
|
||||
private static ResourceLocation id(String id) {
|
||||
return new ResourceLocation(ComputerCraftAPI.MOD_ID, id);
|
||||
}
|
||||
|
||||
private static ResourceLocation vanilla(String id) {
|
||||
private static ResourceKey<ITurtleUpgrade> vanilla(String id) {
|
||||
// Naughty, please don't do this. Mostly here for some semblance of backwards compatibility.
|
||||
return new ResourceLocation("minecraft", id);
|
||||
return ITurtleUpgrade.createKey(new ResourceLocation("minecraft", id));
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,6 @@ import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.ShapelessRecipe;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
/**
|
||||
@@ -61,6 +60,6 @@ public final class ShapelessSpecBuilder extends AbstractRecipeBuilder<ShapelessS
|
||||
}
|
||||
|
||||
public FinishedRecipe build() {
|
||||
return build(spec -> new ShapelessRecipe(spec.properties().group(), spec.properties().category(), spec.result(), spec.ingredients()));
|
||||
return build(ShapelessRecipeSpec::create);
|
||||
}
|
||||
}
|
||||
|
@@ -5,11 +5,10 @@
|
||||
package dan200.computercraft.impl;
|
||||
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
|
||||
public final class PocketUpgrades {
|
||||
private static final UpgradeManager<IPocketUpgrade> registry = new UpgradeManager<>(
|
||||
IPocketUpgrade.typeRegistry(), ModRegistry.POCKET_UPGRADE, IPocketUpgrade::getType
|
||||
IPocketUpgrade.typeRegistry(), IPocketUpgrade.REGISTRY, IPocketUpgrade::getType
|
||||
);
|
||||
|
||||
private PocketUpgrades() {
|
||||
|
@@ -5,11 +5,10 @@
|
||||
package dan200.computercraft.impl;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
|
||||
public final class TurtleUpgrades {
|
||||
private static final UpgradeManager<ITurtleUpgrade> registry = new UpgradeManager<>(
|
||||
ITurtleUpgrade.typeRegistry(), ModRegistry.TURTLE_UPGRADE, ITurtleUpgrade::getType
|
||||
ITurtleUpgrade.typeRegistry(), ITurtleUpgrade.REGISTRY, ITurtleUpgrade::getType
|
||||
);
|
||||
|
||||
private TurtleUpgrades() {
|
||||
|
@@ -18,7 +18,6 @@ import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.command.UserLevel;
|
||||
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
|
||||
import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
|
||||
@@ -91,7 +90,6 @@ import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.cauldron.CauldronInteraction;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
@@ -99,7 +97,6 @@ import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
@@ -130,9 +127,6 @@ public final class ModRegistry {
|
||||
private ModRegistry() {
|
||||
}
|
||||
|
||||
public static final ResourceKey<Registry<ITurtleUpgrade>> TURTLE_UPGRADE = RegistryHelper.TURTLE_UPGRADE;
|
||||
public static final ResourceKey<Registry<IPocketUpgrade>> POCKET_UPGRADE = RegistryHelper.POCKET_UPGRADE;
|
||||
|
||||
public static final class Blocks {
|
||||
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK);
|
||||
|
||||
@@ -255,12 +249,16 @@ public final class ModRegistry {
|
||||
public static final RegistryEntry<TreasureDiskItem> TREASURE_DISK =
|
||||
REGISTRY.register("treasure_disk", () -> new TreasureDiskItem(properties().stacksTo(1)));
|
||||
|
||||
private static Item.Properties printoutProperties() {
|
||||
return properties().stacksTo(1).component(DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
|
||||
}
|
||||
|
||||
public static final RegistryEntry<PrintoutItem> PRINTED_PAGE = REGISTRY.register("printed_page",
|
||||
() -> new PrintoutItem(properties().stacksTo(1), PrintoutItem.Type.PAGE));
|
||||
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.PAGE));
|
||||
public static final RegistryEntry<PrintoutItem> PRINTED_PAGES = REGISTRY.register("printed_pages",
|
||||
() -> new PrintoutItem(properties().stacksTo(1), PrintoutItem.Type.PAGES));
|
||||
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.PAGES));
|
||||
public static final RegistryEntry<PrintoutItem> PRINTED_BOOK = REGISTRY.register("printed_book",
|
||||
() -> new PrintoutItem(properties().stacksTo(1), PrintoutItem.Type.BOOK));
|
||||
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.BOOK));
|
||||
|
||||
public static final RegistryEntry<BlockItem> SPEAKER = ofBlock(Blocks.SPEAKER, BlockItem::new);
|
||||
public static final RegistryEntry<BlockItem> DISK_DRIVE = ofBlock(Blocks.DISK_DRIVE, BlockItem::new);
|
||||
@@ -494,7 +492,7 @@ public final class ModRegistry {
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
|
||||
public static final RegistryEntry<RecipeSerializer<PrintoutRecipe>> PRINTOUT = register("printout", PrintoutRecipe.CODEC, PrintoutRecipe.STREAM_CODEC);
|
||||
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
|
||||
}
|
||||
|
||||
@@ -566,8 +564,8 @@ public final class ModRegistry {
|
||||
public static void register() {
|
||||
Blocks.REGISTRY.register();
|
||||
BlockEntities.REGISTRY.register();
|
||||
Items.REGISTRY.register();
|
||||
DataComponents.REGISTRY.register();
|
||||
Items.REGISTRY.register();
|
||||
TurtleUpgradeTypes.REGISTRY.register();
|
||||
PocketUpgradeTypes.REGISTRY.register();
|
||||
Menus.REGISTRY.register();
|
||||
@@ -602,7 +600,7 @@ public final class ModRegistry {
|
||||
|
||||
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle, HolderLookup.Provider registries) {
|
||||
out.accept(new ItemStack(turtle));
|
||||
registries.lookupOrThrow(TURTLE_UPGRADE).listElements()
|
||||
registries.lookupOrThrow(ITurtleUpgrade.REGISTRY).listElements()
|
||||
.filter(ModRegistry::isOurUpgrade)
|
||||
.map(x -> DataComponentUtil.createStack(turtle, DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(x)))
|
||||
.forEach(out::accept);
|
||||
@@ -610,7 +608,7 @@ public final class ModRegistry {
|
||||
|
||||
private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket, HolderLookup.Provider registries) {
|
||||
out.accept(new ItemStack(pocket));
|
||||
registries.lookupOrThrow(POCKET_UPGRADE).listElements()
|
||||
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements()
|
||||
.filter(ModRegistry::isOurUpgrade)
|
||||
.map(x -> DataComponentUtil.createStack(pocket, DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept);
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import com.google.gson.JsonObject;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
@@ -253,7 +253,6 @@ public record ComputerSelector(
|
||||
}
|
||||
|
||||
private static final class Builder {
|
||||
private OptionalInt instanceId = OptionalInt.empty();
|
||||
private @Nullable UUID instanceUuid = null;
|
||||
private OptionalInt computerId = OptionalInt.empty();
|
||||
private @Nullable String label;
|
||||
|
@@ -5,16 +5,13 @@
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
@@ -34,8 +31,6 @@ import net.minecraft.world.phys.BlockHitResult;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntity> extends HorizontalDirectionalBlock implements IBundledRedstoneBlock, EntityBlock {
|
||||
private static final ResourceLocation DROP = new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer");
|
||||
|
||||
protected final RegistryEntry<BlockEntityType<T>> type;
|
||||
private final BlockEntityTicker<T> serverTicker = (level, pos, state, computer) -> computer.serverTick();
|
||||
|
||||
@@ -154,12 +149,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) {
|
||||
return level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer ? computer : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
package dan200.computercraft.shared.details;
|
||||
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.tags.TagKey;
|
||||
|
@@ -5,6 +5,8 @@
|
||||
package dan200.computercraft.shared.integration;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
@@ -60,14 +62,14 @@ public final class RecipeModHelpers {
|
||||
List<ItemStack> upgradeItems = new ArrayList<>();
|
||||
for (var turtleSupplier : TURTLES) {
|
||||
var turtle = turtleSupplier.get();
|
||||
forEachRegistry(registries, ModRegistry.TURTLE_UPGRADE, upgrade ->
|
||||
forEachRegistry(registries, ITurtleUpgrade.REGISTRY, upgrade ->
|
||||
upgradeItems.add(DataComponentUtil.createStack(turtle, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
|
||||
);
|
||||
}
|
||||
|
||||
for (var pocketSupplier : POCKET_COMPUTERS) {
|
||||
var pocket = pocketSupplier.get();
|
||||
forEachRegistry(registries, ModRegistry.POCKET_UPGRADE, upgrade ->
|
||||
forEachRegistry(registries, IPocketUpgrade.REGISTRY, upgrade ->
|
||||
upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
|
||||
);
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ public class UpgradeRecipeGenerator<T> {
|
||||
if (initialised) return;
|
||||
initialised = true;
|
||||
|
||||
forEachRegistry(registries, ModRegistry.TURTLE_UPGRADE, holder -> {
|
||||
forEachRegistry(registries, ITurtleUpgrade.REGISTRY, holder -> {
|
||||
var upgrade = holder.value();
|
||||
var stack = upgrade.getCraftingItem();
|
||||
if (stack.isEmpty()) return;
|
||||
@@ -67,7 +67,7 @@ public class UpgradeRecipeGenerator<T> {
|
||||
turtleUpgrades.add(info);
|
||||
});
|
||||
|
||||
forEachRegistry(registries, ModRegistry.POCKET_UPGRADE, holder -> {
|
||||
forEachRegistry(registries, IPocketUpgrade.REGISTRY, holder -> {
|
||||
var upgrade = holder.value();
|
||||
var stack = upgrade.getCraftingItem();
|
||||
if (stack.isEmpty()) return;
|
||||
@@ -223,13 +223,13 @@ public class UpgradeRecipeGenerator<T> {
|
||||
var newStack = stack.copyWithCount(1);
|
||||
newStack.set(ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get(), left);
|
||||
newStack.set(ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), right);
|
||||
return stack;
|
||||
return newStack;
|
||||
}
|
||||
|
||||
private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back) {
|
||||
var newStack = stack.copyWithCount(1);
|
||||
newStack.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), back);
|
||||
return stack;
|
||||
return newStack;
|
||||
}
|
||||
|
||||
private T pocket(Ingredient upgrade, Ingredient pocketComputer, ItemStack result) {
|
||||
|
@@ -8,7 +8,9 @@ import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.core.component.DataComponentHolder;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
|
||||
@@ -93,6 +95,10 @@ public record PrintoutData(String title, List<Line> lines) {
|
||||
return DataResult.success(lines);
|
||||
}
|
||||
|
||||
public static PrintoutData getOrEmpty(DataComponentHolder holder) {
|
||||
return holder.getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of pages in this printout.
|
||||
*
|
||||
|
@@ -35,7 +35,7 @@ public class PrintoutItem extends Item {
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
|
||||
var title = getTitle(stack);
|
||||
var title = PrintoutData.getOrEmpty(stack).title();
|
||||
if (!title.isEmpty()) list.add(Component.literal(title));
|
||||
}
|
||||
|
||||
@@ -51,14 +51,4 @@ public class PrintoutItem extends Item {
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public static String getTitle(ItemStack stack) {
|
||||
var nbt = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
|
||||
return nbt == null ? "" : nbt.title();
|
||||
}
|
||||
|
||||
public static int getPageCount(ItemStack stack) {
|
||||
var nbt = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
|
||||
return nbt == null ? 1 : nbt.pages();
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@
|
||||
package dan200.computercraft.shared.media.items;
|
||||
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.RecordItem;
|
||||
@@ -13,7 +12,7 @@ import net.minecraft.world.item.RecordItem;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An implementation of IMedia for ItemRecords.
|
||||
* An implementation of {@link IMedia} for {@link RecordItem}.
|
||||
*/
|
||||
public final class RecordMedia implements IMedia {
|
||||
public static final RecordMedia INSTANCE = new RecordMedia();
|
||||
@@ -29,16 +28,12 @@ public final class RecordMedia implements IMedia {
|
||||
@Override
|
||||
public @Nullable String getAudioTitle(ItemStack stack) {
|
||||
var item = stack.getItem();
|
||||
if (!(item instanceof RecordItem)) return null;
|
||||
|
||||
return Component.translatable(item.getDescriptionId() + ".desc").getString();
|
||||
return item instanceof RecordItem record ? record.getDisplayName().getString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SoundEvent getAudio(ItemStack stack) {
|
||||
var item = stack.getItem();
|
||||
if (!(item instanceof RecordItem)) return null;
|
||||
|
||||
return ((RecordItem) item).getSound();
|
||||
return item instanceof RecordItem record ? record.getSound() : null;
|
||||
}
|
||||
}
|
||||
|
@@ -4,115 +4,141 @@
|
||||
|
||||
package dan200.computercraft.shared.media.recipes;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||
import dan200.computercraft.shared.recipe.RecipeProperties;
|
||||
import dan200.computercraft.shared.recipe.ShapelessRecipeSpec;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.ShapelessRecipe;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class PrintoutRecipe extends CustomRecipe {
|
||||
private final Ingredient leather;
|
||||
private final Ingredient string;
|
||||
/**
|
||||
* A recipe for combining one or more printed pages together.
|
||||
* <p>
|
||||
* This behaves similarly to a {@link ShapelessRecipe}, but allows a variable number of pages to appear as ingredients.
|
||||
*
|
||||
* @see PrintoutItem
|
||||
* @see PrintoutData
|
||||
*/
|
||||
public final class PrintoutRecipe extends ShapelessRecipe {
|
||||
public static final MapCodec<PrintoutRecipe> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
ShapelessRecipeSpec.CODEC.forGetter(PrintoutRecipe::toSpec),
|
||||
Ingredient.CODEC_NONEMPTY.fieldOf("printout").forGetter(x -> x.printout),
|
||||
ExtraCodecs.POSITIVE_INT.fieldOf("min_printouts").forGetter(x -> x.minPrintouts)
|
||||
).apply(instance, PrintoutRecipe::new));
|
||||
|
||||
public PrintoutRecipe(CraftingBookCategory category) {
|
||||
super(category);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, PrintoutRecipe> STREAM_CODEC = StreamCodec.composite(
|
||||
ShapelessRecipeSpec.STREAM_CODEC, PrintoutRecipe::toSpec,
|
||||
Ingredient.CONTENTS_STREAM_CODEC, x -> x.printout,
|
||||
ByteBufCodecs.VAR_INT, x -> x.minPrintouts,
|
||||
PrintoutRecipe::new
|
||||
);
|
||||
|
||||
var ingredients = PlatformHelper.get().getRecipeIngredients();
|
||||
leather = ingredients.leather();
|
||||
string = ingredients.string();
|
||||
private final NonNullList<Ingredient> ingredients;
|
||||
private final Ingredient printout;
|
||||
private final int minPrintouts;
|
||||
private final ShapelessRecipe innerRecipe;
|
||||
|
||||
private final ItemStack result;
|
||||
|
||||
/**
|
||||
* Construct a new {@link PrintoutRecipe}.
|
||||
*
|
||||
* @param spec The base {@link ShapelessRecipeSpec} for this recipe.
|
||||
* @param printout The items that will be treated as printed pages.
|
||||
* @param minPrintouts The minimum number of pages required.
|
||||
*/
|
||||
public PrintoutRecipe(
|
||||
ShapelessRecipeSpec spec, Ingredient printout, int minPrintouts
|
||||
) {
|
||||
// We use the full list of ingredients in the recipe itself, so that it behaves sensibly with recipe mods.
|
||||
super(spec.properties().group(), spec.properties().category(), spec.result(), concat(spec.ingredients(), printout, minPrintouts));
|
||||
|
||||
this.ingredients = spec.ingredients();
|
||||
this.printout = printout;
|
||||
this.minPrintouts = minPrintouts;
|
||||
this.result = spec.result();
|
||||
|
||||
// However, when testing whether the recipe matches, we only want to use the non-printout ingredients. To do
|
||||
// that, we create a hidden recipe with the main ingredients.
|
||||
this.innerRecipe = spec.create();
|
||||
}
|
||||
|
||||
private static NonNullList<Ingredient> concat(NonNullList<Ingredient> first, Ingredient pages, int pagesRequired) {
|
||||
var result = NonNullList.withSize(first.size() + pagesRequired, Ingredient.EMPTY);
|
||||
var idx = 0;
|
||||
for (var ingredient : first) result.set(idx++, ingredient);
|
||||
for (var i = 0; i < pagesRequired; i++) result.set(idx++, pages);
|
||||
return result;
|
||||
}
|
||||
|
||||
private ShapelessRecipeSpec toSpec() {
|
||||
return new ShapelessRecipeSpec(RecipeProperties.of(this), ingredients, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCraftInDimensions(int x, int y) {
|
||||
return x >= 3 && y >= 3;
|
||||
}
|
||||
public boolean matches(CraftingContainer inv, Level world) {
|
||||
var stackedContents = new StackedContents();
|
||||
|
||||
@Override
|
||||
public ItemStack getResultItem(HolderLookup.Provider registryAccess) {
|
||||
return new ItemStack(ModRegistry.Items.PRINTED_PAGES.get());
|
||||
}
|
||||
var inputs = 0;
|
||||
var printouts = 0;
|
||||
var pages = 0;
|
||||
var hasPrintout = false;
|
||||
|
||||
@Override
|
||||
public boolean matches(CraftingContainer inventory, Level world) {
|
||||
return !assemble(inventory, world.registryAccess()).isEmpty();
|
||||
}
|
||||
for (var j = 0; j < inv.getContainerSize(); ++j) {
|
||||
var stack = inv.getItem(j);
|
||||
if (stack.isEmpty()) continue;
|
||||
if (printout.test(stack)) {
|
||||
printouts++;
|
||||
|
||||
@Override
|
||||
public ItemStack assemble(CraftingContainer inventory, HolderLookup.Provider registryAccess) {
|
||||
// See if we match the recipe, and extract the input disk ID and dye colour
|
||||
var numPages = 0;
|
||||
var numPrintouts = 0;
|
||||
ItemStack[] printouts = null;
|
||||
var stringFound = false;
|
||||
var leatherFound = false;
|
||||
var printoutFound = false;
|
||||
for (var y = 0; y < inventory.getHeight(); y++) {
|
||||
for (var x = 0; x < inventory.getWidth(); x++) {
|
||||
var stack = inventory.getItem(x + y * inventory.getWidth());
|
||||
if (!stack.isEmpty()) {
|
||||
if (stack.getItem() instanceof PrintoutItem printout && printout.getType() != PrintoutItem.Type.BOOK) {
|
||||
if (printouts == null) printouts = new ItemStack[9];
|
||||
printouts[numPrintouts] = stack;
|
||||
numPages += PrintoutItem.getPageCount(stack);
|
||||
numPrintouts++;
|
||||
printoutFound = true;
|
||||
} else if (stack.getItem() == Items.PAPER) {
|
||||
if (printouts == null) {
|
||||
printouts = new ItemStack[9];
|
||||
}
|
||||
printouts[numPrintouts] = stack;
|
||||
numPages++;
|
||||
numPrintouts++;
|
||||
} else if (string.test(stack) && !stringFound) {
|
||||
stringFound = true;
|
||||
} else if (leather.test(stack) && !leatherFound) {
|
||||
leatherFound = true;
|
||||
} else {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build some pages with what was passed in
|
||||
if (numPages <= PrintoutData.MAX_PAGES && stringFound && printoutFound && numPrintouts >= (leatherFound ? 1 : 2)) {
|
||||
if (printouts == null) throw new IllegalStateException("Printouts must be non-null");
|
||||
var lines = new PrintoutData.Line[numPages * PrintoutData.LINES_PER_PAGE];
|
||||
var line = 0;
|
||||
|
||||
for (var printout = 0; printout < numPrintouts; printout++) {
|
||||
var pageText = printouts[printout].get(ModRegistry.DataComponents.PRINTOUT.get());
|
||||
if (pageText != null) {
|
||||
// Add a printout
|
||||
for (var pageLine : pageText.lines()) lines[line++] = pageLine;
|
||||
var printout = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
|
||||
if (printout == null) {
|
||||
pages++;
|
||||
} else {
|
||||
// Add a blank page
|
||||
for (var pageLine = 0; pageLine < PrintoutData.LINES_PER_PAGE; pageLine++) {
|
||||
lines[line++] = PrintoutData.Line.EMPTY;
|
||||
}
|
||||
hasPrintout = true;
|
||||
pages += printout.pages();
|
||||
}
|
||||
} else {
|
||||
inputs++;
|
||||
stackedContents.accountStack(stack, 1);
|
||||
}
|
||||
|
||||
var title = PrintoutItem.getTitle(printouts[0]);
|
||||
|
||||
return DataComponentUtil.createStack(
|
||||
leatherFound ? ModRegistry.Items.PRINTED_BOOK.get() : ModRegistry.Items.PRINTED_PAGES.get(),
|
||||
ModRegistry.DataComponents.PRINTOUT.get(), new PrintoutData(title, List.of(lines))
|
||||
);
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
return hasPrintout && printouts >= minPrintouts && pages <= PrintoutData.MAX_PAGES
|
||||
&& inputs == ingredients.size() && stackedContents.canCraft(innerRecipe, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack assemble(CraftingContainer inv, HolderLookup.Provider registries) {
|
||||
List<PrintoutData> data = new ArrayList<>();
|
||||
for (var j = 0; j < inv.getContainerSize(); ++j) {
|
||||
var stack = inv.getItem(j);
|
||||
if (!stack.isEmpty() && printout.test(stack)) data.add(PrintoutData.getOrEmpty(stack));
|
||||
}
|
||||
|
||||
if (data.isEmpty()) throw new IllegalStateException("Printouts must be non-null");
|
||||
|
||||
var lines = data.stream().flatMap(x -> x.lines().stream()).toList();
|
||||
|
||||
var result = super.assemble(inv, registries);
|
||||
result.set(ModRegistry.DataComponents.PRINTOUT.get(), new PrintoutData(data.getFirst().title(), lines));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
package dan200.computercraft.shared.peripheral.modem.wired;
|
||||
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
@@ -11,7 +11,6 @@ import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.container.BasicContainer;
|
||||
import dan200.computercraft.shared.container.BasicWorldlyContainer;
|
||||
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.util.ColourUtils;
|
||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -172,8 +171,7 @@ public final class PrinterBlockEntity extends AbstractContainerBlockEntity imple
|
||||
|
||||
static boolean isPaper(ItemStack stack) {
|
||||
var item = stack.getItem();
|
||||
return item == Items.PAPER
|
||||
|| (item instanceof PrintoutItem printout && printout.getType() == PrintoutItem.Type.PAGE);
|
||||
return item == Items.PAPER || item == ModRegistry.Items.PRINTED_PAGE.get();
|
||||
}
|
||||
|
||||
private boolean canInputPage() {
|
||||
|
@@ -4,11 +4,11 @@
|
||||
|
||||
package dan200.computercraft.shared.platform;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import dan200.computercraft.api.network.wired.WiredElement;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.impl.Services;
|
||||
import dan200.computercraft.shared.config.ConfigFile;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
@@ -47,26 +47,19 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* This extends {@linkplain dan200.computercraft.impl.PlatformHelper the API's loader abstraction layer}, adding
|
||||
* additional methods used by the actual mod.
|
||||
* Abstraction layer for Forge and Fabric. See implementations for more details.
|
||||
*/
|
||||
public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper {
|
||||
public interface PlatformHelper {
|
||||
/**
|
||||
* Get the current {@link PlatformHelper} instance.
|
||||
*
|
||||
* @return The current instance.
|
||||
*/
|
||||
static PlatformHelper get() {
|
||||
return (PlatformHelper) dan200.computercraft.impl.PlatformHelper.get();
|
||||
var instance = Instance.INSTANCE;
|
||||
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're running in a development environment.
|
||||
*
|
||||
* @return If we're running in a development environment.
|
||||
*/
|
||||
boolean isDevelopmentEnvironment();
|
||||
|
||||
/**
|
||||
* Create a new config builder.
|
||||
*
|
||||
@@ -83,16 +76,6 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
||||
*/
|
||||
<T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T>> registry);
|
||||
|
||||
/**
|
||||
* Determine if this resource should be loaded, based on platform-specific loot conditions.
|
||||
* <p>
|
||||
* This should only be called from the {@code apply} stage of a reload listener.
|
||||
*
|
||||
* @param object The root JSON object of this resource.
|
||||
* @return If this resource should be loaded.
|
||||
*/
|
||||
boolean shouldLoadResource(JsonObject object);
|
||||
|
||||
/**
|
||||
* Register a new argument type.
|
||||
*
|
||||
@@ -328,4 +311,21 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
||||
* @see ServerPlayerGameMode#useItemOn(ServerPlayer, Level, ItemStack, InteractionHand, BlockHitResult)
|
||||
*/
|
||||
InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock);
|
||||
|
||||
|
||||
final class Instance {
|
||||
static final @Nullable PlatformHelper INSTANCE;
|
||||
static final @Nullable Throwable ERROR;
|
||||
|
||||
static {
|
||||
// We don't want class initialisation to fail here (as that results in confusing errors). Instead, capture
|
||||
// the error and rethrow it when accessing. This should be JITted away in the common case.
|
||||
var helper = Services.tryLoad(PlatformHelper.class);
|
||||
INSTANCE = helper.instance();
|
||||
ERROR = helper.error();
|
||||
}
|
||||
|
||||
private Instance() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,6 @@ import net.minecraft.world.item.crafting.Ingredient;
|
||||
* @param redstone All {@link Items#REDSTONE} items.
|
||||
* @param string All {@link Items#STRING} items.
|
||||
* @param leather All {@link Items#LEATHER} items.
|
||||
* @param stone All {@link Items#STONE} items.
|
||||
* @param glassPane All {@link Items#GLASS_PANE} items.
|
||||
* @param goldIngot All {@link Items#GOLD_INGOT} items.
|
||||
* @param goldBlock All {@link Items#GOLD_BLOCK} items.
|
||||
@@ -26,7 +25,6 @@ public record RecipeIngredients(
|
||||
Ingredient redstone,
|
||||
Ingredient string,
|
||||
Ingredient leather,
|
||||
Ingredient stone,
|
||||
Ingredient glassPane,
|
||||
Ingredient goldIngot,
|
||||
Ingredient goldBlock,
|
||||
|
@@ -6,7 +6,7 @@ package dan200.computercraft.shared.recipe;
|
||||
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import dan200.computercraft.shared.util.RegistryHelper;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
|
@@ -49,4 +49,13 @@ public record ShapelessRecipeSpec(RecipeProperties properties, NonNullList<Ingre
|
||||
ItemStack.STREAM_CODEC, ShapelessRecipeSpec::result,
|
||||
ShapelessRecipeSpec::new
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a basic {@link ShapelessRecipe} from this spec.
|
||||
*
|
||||
* @return The newly constructed recipe.
|
||||
*/
|
||||
public ShapelessRecipe create() {
|
||||
return new ShapelessRecipe(properties().group(), properties().category(), result(), ingredients());
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.turtle.TurtleUtil;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||
import dan200.computercraft.shared.util.DropConsumer;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -49,37 +50,26 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
private static final TurtleCommandResult INEFFECTIVE = TurtleCommandResult.failure("Cannot break block with this tool");
|
||||
|
||||
final TurtleToolSpec spec;
|
||||
final ItemStack item;
|
||||
final float damageMulitiplier;
|
||||
final boolean allowEnchantments;
|
||||
final TurtleToolDurability consumeDurability;
|
||||
final @Nullable TagKey<Block> breakable;
|
||||
|
||||
public TurtleTool(TurtleToolSpec spec) {
|
||||
super(TurtleUpgradeType.TOOL, spec.adjective(), new ItemStack(spec.craftItem().orElse(spec.toolItem())));
|
||||
super(TurtleUpgradeType.TOOL, spec.adjective(), new ItemStack(spec.item()));
|
||||
this.spec = spec;
|
||||
item = new ItemStack(spec.toolItem());
|
||||
this.damageMulitiplier = spec.damageMultiplier();
|
||||
this.allowEnchantments = spec.allowEnchantments();
|
||||
this.consumeDurability = spec.consumeDurability();
|
||||
this.breakable = spec.breakable().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemSuitable(ItemStack stack) {
|
||||
if (consumeDurability == TurtleToolDurability.NEVER && stack.isDamaged()) return false;
|
||||
if (!allowEnchantments && isEnchanted(stack)) return false;
|
||||
if (spec.consumeDurability() == TurtleToolDurability.NEVER && stack.isDamaged()) return false;
|
||||
if (!spec.allowEnchantments() && isEnchanted(stack)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isEnchanted(ItemStack stack) {
|
||||
var enchantments = stack.get(DataComponents.ENCHANTMENTS);
|
||||
if (enchantments != null && !enchantments.isEmpty()) return true;
|
||||
|
||||
var modifiers = stack.get(DataComponents.ATTRIBUTE_MODIFIERS);
|
||||
if (modifiers != null && !modifiers.modifiers().isEmpty()) return true;
|
||||
|
||||
return false;
|
||||
// Only check whether the stack has been modified. We ignore components on the original item.
|
||||
var patch = stack.getComponentsPatch();
|
||||
return DataComponentUtil.isPresent(patch, DataComponents.ENCHANTMENTS, x -> !x.isEmpty())
|
||||
|| DataComponentUtil.isPresent(patch, DataComponents.ATTRIBUTE_MODIFIERS, x -> !x.modifiers().isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,9 +90,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
}
|
||||
|
||||
private void setToolStack(ITurtleAccess turtle, TurtleSide side, ItemStack oldStack, ItemStack stack) {
|
||||
var upgradeData = turtle.getUpgradeData(side);
|
||||
|
||||
var useDurability = switch (consumeDurability) {
|
||||
var useDurability = switch (spec.consumeDurability()) {
|
||||
case NEVER -> false;
|
||||
case WHEN_ENCHANTED -> isEnchanted(oldStack);
|
||||
case ALWAYS -> true;
|
||||
@@ -116,7 +104,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
}
|
||||
|
||||
// If the tool has changed, no clue what's going on.
|
||||
if (stack.getItem() != item.getItem()) return;
|
||||
if (stack.getItem() != spec.item()) return;
|
||||
|
||||
turtle.setUpgradeData(side, stack.getComponentsPatch());
|
||||
}
|
||||
@@ -218,7 +206,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
||||
* @see Player#attack(Entity)
|
||||
*/
|
||||
private boolean attack(ServerPlayer player, Direction direction, Entity entity) {
|
||||
var baseDamage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * damageMulitiplier;
|
||||
var baseDamage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * spec.damageMultiplier();
|
||||
var bonusDamage = EnchantmentHelper.getDamageBonus(player.getItemInHand(InteractionHand.MAIN_HAND), entity.getType());
|
||||
var damage = baseDamage + bonusDamage;
|
||||
if (damage <= 0) return false;
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import net.minecraft.core.component.DataComponentHolder;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -13,6 +14,7 @@ import net.minecraft.world.level.ItemLike;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Utilities for working with {@linkplain DataComponentType data components}.
|
||||
@@ -43,4 +45,18 @@ public class DataComponentUtil {
|
||||
public static <T> ItemStack createStack(ItemLike item, DataComponentType<T> type, @Nullable T value) {
|
||||
return set(new ItemStack(item), type, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a component is present in a {@link DataComponentPatch} and matches the supplied predicate.
|
||||
*
|
||||
* @param patch The current component patch.
|
||||
* @param component The component type.
|
||||
* @param check The predicate to check against.
|
||||
* @param <T> The type of component.
|
||||
* @return Whether the component is present in this patch, and matches the supplied predicate.
|
||||
*/
|
||||
public static <T> boolean isPresent(DataComponentPatch patch, DataComponentType<T> component, Predicate<T> check) {
|
||||
var value = patch.get(component);
|
||||
return value != null && value.isPresent() && check.test(value.get());
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,6 @@ public final class IDAssigner {
|
||||
|
||||
private final Path idFile;
|
||||
private final Path newIdFile;
|
||||
private boolean atomicMove = true;
|
||||
private @Nullable Map<String, Integer> ids;
|
||||
|
||||
public IDAssigner(Path path) {
|
||||
|
@@ -2,11 +2,8 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.impl;
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
@@ -18,9 +15,6 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public final class RegistryHelper {
|
||||
public static final ResourceKey<Registry<ITurtleUpgrade>> TURTLE_UPGRADE = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade"));
|
||||
public static final ResourceKey<Registry<IPocketUpgrade>> POCKET_UPGRADE = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
|
||||
|
||||
private RegistryHelper() {
|
||||
}
|
||||
|
@@ -6,7 +6,6 @@ package dan200.computercraft.shared.util;
|
||||
|
||||
import com.mojang.serialization.*;
|
||||
import com.mojang.serialization.codecs.KeyDispatchCodec;
|
||||
import dan200.computercraft.impl.RegistryHelper;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import com.google.errorprone.annotations.Keep;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ChunkLevel;
|
||||
@@ -132,6 +133,7 @@ public final class TickScheduler {
|
||||
/**
|
||||
* The current state of this token.
|
||||
*/
|
||||
@Keep
|
||||
private volatile State $state = State.IDLE;
|
||||
|
||||
public Token(BlockEntity owner) {
|
||||
|
@@ -218,5 +218,18 @@
|
||||
"upgrade.minecraft.diamond_hoe.adjective": "Contadina",
|
||||
"upgrade.minecraft.diamond_pickaxe.adjective": "Minatrice",
|
||||
"upgrade.minecraft.diamond_shovel.adjective": "Scavatrice",
|
||||
"upgrade.minecraft.diamond_sword.adjective": "Da Combattimento"
|
||||
"upgrade.minecraft.diamond_sword.adjective": "Da Combattimento",
|
||||
"tag.item.computercraft.computer": "Computer",
|
||||
"tag.item.computercraft.wired_modem": "Modem cablati",
|
||||
"argument.computercraft.computer.distance": "Distanza dall'entità",
|
||||
"argument.computercraft.computer.family": "Famiglia computer",
|
||||
"argument.computercraft.computer.id": "ID computer",
|
||||
"argument.computercraft.computer.instance": "ID istanza unica",
|
||||
"argument.computercraft.computer.label": "Etichetta computer",
|
||||
"argument.computercraft.unknown_computer_family": "Famiglia computer '%s' sconosciuta",
|
||||
"gui.computercraft.config.disabled_generic_methods": "Metodi generici disattivati",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "Una lista di metodi generici o sorgenti di metodi da disattivare. I metodi generici sono\nmetodi aggiunti a blocchi/entità blocchi quando non c'è un provider della periferica esplicito.\nQuesto include metodi dell'inventario (ad es. inventory.getItemDetail, inventory.pushItems) e,\nse su Forge, i metodi fluid_storage e energy_storage.\nI metodi in questa lista possono essere sia un gruppo intero di metodi (computer:inventory)\no un singolo metodo (computer:inventory#pushItems).\n",
|
||||
"tag.item.computercraft.monitor": "Monitor",
|
||||
"tag.item.computercraft.turtle": "Tartarughe",
|
||||
"tracking_field.computercraft.java_allocation.name": "Allocazioni Java"
|
||||
}
|
||||
|
@@ -2,30 +2,30 @@
|
||||
"parent": "block/block",
|
||||
"render_type": "translucent",
|
||||
"textures": {
|
||||
"particle": "#texture"
|
||||
"particle": "#front"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [ 2, 2, 2 ],
|
||||
"to": [ 14, 14, 13 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
|
||||
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
|
||||
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
|
||||
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
|
||||
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
|
||||
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
|
||||
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#bottom" },
|
||||
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
|
||||
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
|
||||
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
|
||||
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
|
||||
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 3, 6, 13 ],
|
||||
"to": [ 13, 13, 15 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
|
||||
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
|
||||
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
|
||||
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
|
||||
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
|
||||
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
|
||||
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
|
||||
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
|
||||
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
|
||||
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@@ -1,53 +1,67 @@
|
||||
{
|
||||
"parent": "computercraft:block/turtle_base",
|
||||
"textures": {
|
||||
"texture": "computercraft:block/turtle_colour"
|
||||
"texture": "computercraft:block/turtle_colour",
|
||||
"body_back": "computercraft:block/turtle_colour_body_back",
|
||||
"body_backpack": "computercraft:block/turtle_colour_body_backpack",
|
||||
"body_bottom": "computercraft:block/turtle_colour_body_bottom",
|
||||
"body_front": "computercraft:block/turtle_colour_body_front",
|
||||
"body_left": "computercraft:block/turtle_colour_body_left",
|
||||
"body_right": "computercraft:block/turtle_colour_body_right",
|
||||
"body_top": "computercraft:block/turtle_colour_body_top",
|
||||
"frame_back": "computercraft:block/turtle_colour_frame_back",
|
||||
"frame_backpack": "computercraft:block/turtle_colour_frame_backpack",
|
||||
"frame_bottom": "computercraft:block/turtle_colour_frame_bottom",
|
||||
"frame_front": "computercraft:block/turtle_colour_frame_front",
|
||||
"frame_left": "computercraft:block/turtle_colour_frame_left",
|
||||
"frame_right": "computercraft:block/turtle_colour_frame_right",
|
||||
"frame_top": "computercraft:block/turtle_colour_frame_top"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [ 2, 2, 2 ],
|
||||
"to": [ 14, 14, 13 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 5.75, 8.5, 2.75, 5.75 ], "texture": "#texture", "tintindex": 0 },
|
||||
"up": { "uv": [ 8.75, 5.75, 5.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
|
||||
"north": { "uv": [ 11.5, 11.5, 8.5, 8.5 ], "texture": "#texture", "tintindex": 0 },
|
||||
"south": { "uv": [ 5.75, 11.5, 2.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
|
||||
"west": { "uv": [ 8.5, 11.5, 5.75, 8.555 ], "texture": "#texture", "tintindex": 0 },
|
||||
"east": { "uv": [ 2.75, 11.5, 0, 8.5 ], "texture": "#texture", "tintindex": 0 }
|
||||
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_bottom", "tintindex": 0 },
|
||||
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_top", "tintindex": 0 },
|
||||
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_front", "tintindex": 0 },
|
||||
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_back", "tintindex": 0 },
|
||||
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_left", "tintindex": 0 },
|
||||
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_right", "tintindex": 0 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 3, 6, 13 ],
|
||||
"to": [ 13, 13, 15 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 11.75, 6.25, 9.25, 5.75 ], "texture": "#texture", "tintindex": 0 },
|
||||
"up": { "uv": [ 14.25, 5.75, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
|
||||
"south": { "uv": [ 11.75, 8, 9.25, 6.25 ], "texture": "#texture", "tintindex": 0 },
|
||||
"west": { "uv": [ 12.25, 8, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
|
||||
"east": { "uv": [ 9.25, 8, 8.75, 6.25 ], "texture": "#texture", "tintindex": 0 }
|
||||
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#body_backpack", "tintindex": 0 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 2, 2, 2 ],
|
||||
"to": [ 14, 14, 13 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
|
||||
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
|
||||
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
|
||||
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
|
||||
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
|
||||
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
|
||||
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_bottom" },
|
||||
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_top" },
|
||||
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_front" },
|
||||
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_back" },
|
||||
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_left" },
|
||||
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_right" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 3, 6, 13 ],
|
||||
"to": [ 13, 13, 15 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
|
||||
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
|
||||
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
|
||||
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
|
||||
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
|
||||
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#frame_backpack" },
|
||||
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#frame_backpack" },
|
||||
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#frame_backpack" },
|
||||
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#frame_backpack" },
|
||||
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#frame_backpack" }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@@ -1,6 +1,35 @@
|
||||
{
|
||||
"parent": "computercraft:block/turtle_overlay",
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"texture": "computercraft:block/turtle_elf_overlay"
|
||||
}
|
||||
}
|
||||
"back": "computercraft:block/turtle_elf_overlay_back",
|
||||
"backpack": "computercraft:block/turtle_elf_overlay_backpack",
|
||||
"front": "computercraft:block/turtle_elf_overlay_front",
|
||||
"left": "computercraft:block/turtle_elf_overlay_left",
|
||||
"right": "computercraft:block/turtle_elf_overlay_right",
|
||||
"top": "computercraft:block/turtle_elf_overlay_top"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [ 2, 2, 2 ],
|
||||
"to": [ 14, 14, 13 ],
|
||||
"faces": {
|
||||
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
|
||||
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
|
||||
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
|
||||
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
|
||||
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 3, 6, 13 ],
|
||||
"to": [ 13, 13, 15 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
|
||||
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
|
||||
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
|
||||
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
|
||||
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"particle": "#texture"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [ 2, 2, 2 ],
|
||||
"to": [ 14, 14, 13 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 2.75, 0, 5.75, 2.75 ], "texture": "#texture" },
|
||||
"up": { "uv": [ 5.75, 0, 8.75, 2.75 ], "texture": "#texture" },
|
||||
"north": { "uv": [ 8.5, 5.75, 11.5, 2.75 ], "texture": "#texture" },
|
||||
"south": { "uv": [ 2.75, 5.75, 5.75, 2.75 ], "texture": "#texture" },
|
||||
"west": { "uv": [ 0, 5.75, 2.75, 2.75 ], "texture": "#texture" },
|
||||
"east": { "uv": [ 5.75, 5.75, 8.5, 2.75 ], "texture": "#texture" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 3, 6, 13 ],
|
||||
"to": [ 13, 13, 15 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 9.25, 0, 11.75, 0.5 ], "texture": "#texture" },
|
||||
"up": { "uv": [ 11.75, 0, 14.25, 0.5 ], "texture": "#texture" },
|
||||
"south": { "uv": [ 9.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
|
||||
"west": { "uv": [ 8.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
|
||||
"east": { "uv": [ 11.75, 2.25, 12.25, 0.5 ], "texture": "#texture" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 1.5, 1.5, 1.5 ],
|
||||
"to": [ 14.5, 14.5, 13.5 ],
|
||||
"faces": {
|
||||
"down": { "uv": [ 2.75, 8, 5.75, 10.75 ], "texture": "#texture" },
|
||||
"up": { "uv": [ 5.75, 8, 8.75, 10.75 ], "texture": "#texture" },
|
||||
"north": { "uv": [ 8.5, 13.75, 11.5, 10.75 ], "texture": "#texture" },
|
||||
"south": { "uv": [ 2.75, 13.75, 5.75, 10.75 ], "texture": "#texture" },
|
||||
"west": { "uv": [ 0, 13.75, 2.75, 10.75 ], "texture": "#texture" },
|
||||
"east": { "uv": [ 5.75, 13.75, 8.5, 10.75 ], "texture": "#texture" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 533 B |
After Width: | Height: | Size: 489 B |
After Width: | Height: | Size: 421 B |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 502 B |
After Width: | Height: | Size: 517 B |
After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 258 B |
After Width: | Height: | Size: 229 B |
After Width: | Height: | Size: 361 B |
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 168 B |
After Width: | Height: | Size: 235 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 233 B |
After Width: | Height: | Size: 203 B |
After Width: | Height: | Size: 225 B |
After Width: | Height: | Size: 184 B |
After Width: | Height: | Size: 185 B |
Before Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 338 B |