mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-28 16:22:18 +00:00
Allow equipping pocket computers on the bottom
This allows equipping pocket computers on both the back (as before) and bottom of a pocket computer. The asymmetry is a little unfortunate here, but makes some sense with the crafting recipe (above goes behind, below goes on the bottom). - Move some functionality from IPocketAccess into a PocketComputer interface (and PocketComputerInternal) interface, used by the pocket API. IPocketAccess preserves the same interface as before. Unlike ITurtleAccess, we /don't/ expose the PocketSide in the public API. - Several pocket-computer methods (e.g. setUpgradeData, setColour) are now required to be called on the main thread, and when the computer is being held. This allows us to write back changes to the item immediately, rather than the next time the item is ticked. Sadly this doesn't actually remove the need for onCraftedPostProcess as I'd originally hoped, but I think does make the code a little simpler. - Rename "computercraft:pocket_computer" component to "computercraft:back_pocket_computer". - And finally, support multiple upgrades on the pocket computer. This is actually quite an easy change, just tedious — there's lots of places to update! Fixes #1406, and I think fixes #1148 — you can use a speaker to notify you now.
This commit is contained in:
parent
c20336286b
commit
0a0c80db41
@ -6,6 +6,7 @@ package dan200.computercraft.api.component;
|
|||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||||
|
import dan200.computercraft.api.pocket.PocketComputer;
|
||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,7 +21,7 @@ public class ComputerComponents {
|
|||||||
/**
|
/**
|
||||||
* The {@link IPocketAccess} associated with a pocket computer.
|
* The {@link IPocketAccess} associated with a pocket computer.
|
||||||
*/
|
*/
|
||||||
public static final ComputerComponent<IPocketAccess> POCKET = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "pocket");
|
public static final ComputerComponent<PocketComputer> POCKET = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "pocket");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component is only present on "command computers", and other computers with admin capabilities.
|
* This component is only present on "command computers", and other computers with admin capabilities.
|
||||||
|
@ -7,60 +7,15 @@ package dan200.computercraft.api.pocket;
|
|||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import net.minecraft.core.component.DataComponentPatch;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class for pocket computers.
|
* Access to a pocket computer for {@linkplain IPocketUpgrade pocket upgrades}.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.NonExtendable
|
@ApiStatus.NonExtendable
|
||||||
public interface IPocketAccess {
|
public interface IPocketAccess extends PocketComputer {
|
||||||
/**
|
|
||||||
* Get the level in which the pocket computer exists.
|
|
||||||
*
|
|
||||||
* @return The pocket computer's level.
|
|
||||||
*/
|
|
||||||
ServerLevel getLevel();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the position of the pocket computer.
|
|
||||||
*
|
|
||||||
* @return The pocket computer's position.
|
|
||||||
*/
|
|
||||||
Vec3 getPosition();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the entity holding this item.
|
|
||||||
* <p>
|
|
||||||
* This must be called on the server thread.
|
|
||||||
*
|
|
||||||
* @return The holding entity, or {@code null} if none exists.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
Entity getEntity();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the colour of this pocket computer as a RGB number.
|
|
||||||
*
|
|
||||||
* @return The colour this pocket computer is. This will be a RGB colour between {@code 0x000000} and
|
|
||||||
* {@code 0xFFFFFF} or -1 if it has no colour.
|
|
||||||
* @see #setColour(int)
|
|
||||||
*/
|
|
||||||
int getColour();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the colour of the pocket computer to a RGB number.
|
|
||||||
*
|
|
||||||
* @param colour The colour this pocket computer should be changed to. This should be a RGB colour between
|
|
||||||
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
|
||||||
* @see #getColour()
|
|
||||||
*/
|
|
||||||
void setColour(int colour);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the colour of this pocket computer's light as a RGB number.
|
* Get the colour of this pocket computer's light as a RGB number.
|
||||||
*
|
*
|
||||||
@ -92,7 +47,8 @@ public interface IPocketAccess {
|
|||||||
/**
|
/**
|
||||||
* Set the upgrade for this pocket computer, also updating the item stack.
|
* Set the upgrade for this pocket computer, also updating the item stack.
|
||||||
* <p>
|
* <p>
|
||||||
* Note this method is not thread safe - it must be called from the server thread.
|
* This method can only be called from the main server thread, when this computer is {@linkplain #isActive() is
|
||||||
|
* active}.
|
||||||
*
|
*
|
||||||
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
||||||
* @see #getUpgrade()
|
* @see #getUpgrade()
|
||||||
@ -114,6 +70,9 @@ public interface IPocketAccess {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the upgrade-specific data.
|
* Update the upgrade-specific data.
|
||||||
|
* <p>
|
||||||
|
* This method can only be called from the main server thread, when this computer is {@linkplain #isActive() is
|
||||||
|
* active}.
|
||||||
*
|
*
|
||||||
* @param data The new upgrade data.
|
* @param data The new upgrade data.
|
||||||
* @see #getUpgradeData()
|
* @see #getUpgradeData()
|
||||||
|
@ -12,7 +12,7 @@ import dan200.computercraft.impl.ComputerCraftAPIService;
|
|||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,7 +71,7 @@ public interface IPocketUpgrade extends UpgradeBase {
|
|||||||
/**
|
/**
|
||||||
* Called when the pocket computer is right clicked.
|
* Called when the pocket computer is right clicked.
|
||||||
*
|
*
|
||||||
* @param world The world the computer is in.
|
* @param level The world the computer is in.
|
||||||
* @param access The access object for the pocket item stack.
|
* @param access The access object for the pocket item stack.
|
||||||
* @param peripheral The peripheral for this upgrade.
|
* @param peripheral The peripheral for this upgrade.
|
||||||
* @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path
|
* @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path
|
||||||
@ -79,7 +79,7 @@ public interface IPocketUpgrade extends UpgradeBase {
|
|||||||
* access the GUI.
|
* access the GUI.
|
||||||
* @see #createPeripheral(IPocketAccess)
|
* @see #createPeripheral(IPocketAccess)
|
||||||
*/
|
*/
|
||||||
default boolean onRightClick(Level world, IPocketAccess access, @Nullable IPeripheral peripheral) {
|
default boolean onRightClick(ServerLevel level, IPocketAccess access, @Nullable IPeripheral peripheral) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pocket computer.
|
||||||
|
*
|
||||||
|
* @see IPocketAccess
|
||||||
|
* @see dan200.computercraft.api.component.ComputerComponents#POCKET
|
||||||
|
*/
|
||||||
|
@ApiStatus.NonExtendable
|
||||||
|
public interface PocketComputer {
|
||||||
|
/**
|
||||||
|
* Get the level in which the pocket computer exists.
|
||||||
|
*
|
||||||
|
* @return The pocket computer's level.
|
||||||
|
*/
|
||||||
|
ServerLevel getLevel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position of the pocket computer.
|
||||||
|
*
|
||||||
|
* @return The pocket computer's position.
|
||||||
|
*/
|
||||||
|
Vec3 getPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the entity holding this item.
|
||||||
|
* <p>
|
||||||
|
* This must be called on the server thread.
|
||||||
|
*
|
||||||
|
* @return The holding entity, or {@code null} if none exists.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Entity getEntity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this pocket computer is currently being held by a player, lectern, or other valid entity.
|
||||||
|
* <p>
|
||||||
|
* As pocket computers are backed by item stacks, you must check for validity before updating the computer.
|
||||||
|
* <p>
|
||||||
|
* This must be called on the server thread.
|
||||||
|
*
|
||||||
|
* @return Whether this computer is active.
|
||||||
|
*/
|
||||||
|
boolean isActive();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the colour of this pocket computer as an RGB number.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method can only be called from the main server thread, when this computer is {@linkplain #isActive() is
|
||||||
|
* active}.
|
||||||
|
*
|
||||||
|
* @return The colour this pocket computer is. This will be a RGB colour between {@code 0x000000} and
|
||||||
|
* {@code 0xFFFFFF} or -1 if it has no colour.
|
||||||
|
* @see #setColour(int)
|
||||||
|
*/
|
||||||
|
int getColour();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the colour of the pocket computer to an RGB number.
|
||||||
|
* <p>
|
||||||
|
* This method can only be called from the main server thread, when this computer is {@linkplain #isActive() is
|
||||||
|
* active}.
|
||||||
|
*
|
||||||
|
* @param colour The colour this pocket computer should be changed to. This should be a RGB colour between
|
||||||
|
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
||||||
|
* @see #getColour()
|
||||||
|
*/
|
||||||
|
void setColour(int colour);
|
||||||
|
|
||||||
|
}
|
@ -100,8 +100,10 @@ public final class LanguageProvider implements DataProvider {
|
|||||||
|
|
||||||
add(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), "Pocket Computer");
|
add(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), "Pocket Computer");
|
||||||
add(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().getDescriptionId() + ".upgraded", "%s Pocket Computer");
|
add(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().getDescriptionId() + ".upgraded", "%s Pocket Computer");
|
||||||
|
add(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().getDescriptionId() + ".upgraded_twice", "%s %s Pocket Computer");
|
||||||
add(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), "Advanced Pocket Computer");
|
add(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), "Advanced Pocket Computer");
|
||||||
add(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().getDescriptionId() + ".upgraded", "Advanced %s Pocket Computer");
|
add(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().getDescriptionId() + ".upgraded", "Advanced %s Pocket Computer");
|
||||||
|
add(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().getDescriptionId() + ".upgraded_twice", "Advanced %s %s Pocket Computer");
|
||||||
|
|
||||||
// Tags (for EMI)
|
// Tags (for EMI)
|
||||||
add(ComputerCraftTags.Items.COMPUTER, "Computers");
|
add(ComputerCraftTags.Items.COMPUTER, "Computers");
|
||||||
|
@ -141,7 +141,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
|||||||
|
|
||||||
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements().forEach(upgradeHolder -> {
|
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements().forEach(upgradeHolder -> {
|
||||||
var upgrade = upgradeHolder.value();
|
var upgrade = upgradeHolder.value();
|
||||||
customShaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder)))
|
customShaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder)))
|
||||||
.group(name.toString())
|
.group(name.toString())
|
||||||
.pattern("#")
|
.pattern("#")
|
||||||
.pattern("P")
|
.pattern("P")
|
||||||
|
@ -195,8 +195,10 @@
|
|||||||
"item.computercraft.disk": "Floppy Disk",
|
"item.computercraft.disk": "Floppy Disk",
|
||||||
"item.computercraft.pocket_computer_advanced": "Advanced Pocket Computer",
|
"item.computercraft.pocket_computer_advanced": "Advanced Pocket Computer",
|
||||||
"item.computercraft.pocket_computer_advanced.upgraded": "Advanced %s Pocket Computer",
|
"item.computercraft.pocket_computer_advanced.upgraded": "Advanced %s Pocket Computer",
|
||||||
|
"item.computercraft.pocket_computer_advanced.upgraded_twice": "Advanced %s %s Pocket Computer",
|
||||||
"item.computercraft.pocket_computer_normal": "Pocket Computer",
|
"item.computercraft.pocket_computer_normal": "Pocket Computer",
|
||||||
"item.computercraft.pocket_computer_normal.upgraded": "%s Pocket Computer",
|
"item.computercraft.pocket_computer_normal.upgraded": "%s Pocket Computer",
|
||||||
|
"item.computercraft.pocket_computer_normal.upgraded_twice": "%s %s Pocket Computer",
|
||||||
"item.computercraft.printed_book": "Printed Book",
|
"item.computercraft.printed_book": "Printed Book",
|
||||||
"item.computercraft.printed_page": "Printed Page",
|
"item.computercraft.printed_page": "Printed Page",
|
||||||
"item.computercraft.printed_pages": "Printed Pages",
|
"item.computercraft.printed_pages": "Printed Pages",
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"key": {"#": "computercraft:speaker", "P": "computercraft:pocket_computer_advanced"},
|
"key": {"#": "computercraft:speaker", "P": "computercraft:pocket_computer_advanced"},
|
||||||
"pattern": ["#", "P"],
|
"pattern": ["#", "P"],
|
||||||
"result": {
|
"result": {
|
||||||
"components": {"computercraft:pocket_upgrade": {"id": "computercraft:speaker"}},
|
"components": {"computercraft:back_pocket_upgrade": {"id": "computercraft:speaker"}},
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"id": "computercraft:pocket_computer_advanced"
|
"id": "computercraft:pocket_computer_advanced"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"key": {"#": "computercraft:wireless_modem_advanced", "P": "computercraft:pocket_computer_advanced"},
|
"key": {"#": "computercraft:wireless_modem_advanced", "P": "computercraft:pocket_computer_advanced"},
|
||||||
"pattern": ["#", "P"],
|
"pattern": ["#", "P"],
|
||||||
"result": {
|
"result": {
|
||||||
"components": {"computercraft:pocket_upgrade": {"id": "computercraft:wireless_modem_advanced"}},
|
"components": {"computercraft:back_pocket_upgrade": {"id": "computercraft:wireless_modem_advanced"}},
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"id": "computercraft:pocket_computer_advanced"
|
"id": "computercraft:pocket_computer_advanced"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"key": {"#": "computercraft:wireless_modem_normal", "P": "computercraft:pocket_computer_advanced"},
|
"key": {"#": "computercraft:wireless_modem_normal", "P": "computercraft:pocket_computer_advanced"},
|
||||||
"pattern": ["#", "P"],
|
"pattern": ["#", "P"],
|
||||||
"result": {
|
"result": {
|
||||||
"components": {"computercraft:pocket_upgrade": {"id": "computercraft:wireless_modem_normal"}},
|
"components": {"computercraft:back_pocket_upgrade": {"id": "computercraft:wireless_modem_normal"}},
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"id": "computercraft:pocket_computer_advanced"
|
"id": "computercraft:pocket_computer_advanced"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"key": {"#": "computercraft:speaker", "P": "computercraft:pocket_computer_normal"},
|
"key": {"#": "computercraft:speaker", "P": "computercraft:pocket_computer_normal"},
|
||||||
"pattern": ["#", "P"],
|
"pattern": ["#", "P"],
|
||||||
"result": {
|
"result": {
|
||||||
"components": {"computercraft:pocket_upgrade": {"id": "computercraft:speaker"}},
|
"components": {"computercraft:back_pocket_upgrade": {"id": "computercraft:speaker"}},
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"id": "computercraft:pocket_computer_normal"
|
"id": "computercraft:pocket_computer_normal"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"key": {"#": "computercraft:wireless_modem_advanced", "P": "computercraft:pocket_computer_normal"},
|
"key": {"#": "computercraft:wireless_modem_advanced", "P": "computercraft:pocket_computer_normal"},
|
||||||
"pattern": ["#", "P"],
|
"pattern": ["#", "P"],
|
||||||
"result": {
|
"result": {
|
||||||
"components": {"computercraft:pocket_upgrade": {"id": "computercraft:wireless_modem_advanced"}},
|
"components": {"computercraft:back_pocket_upgrade": {"id": "computercraft:wireless_modem_advanced"}},
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"id": "computercraft:pocket_computer_normal"
|
"id": "computercraft:pocket_computer_normal"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"key": {"#": "computercraft:wireless_modem_normal", "P": "computercraft:pocket_computer_normal"},
|
"key": {"#": "computercraft:wireless_modem_normal", "P": "computercraft:pocket_computer_normal"},
|
||||||
"pattern": ["#", "P"],
|
"pattern": ["#", "P"],
|
||||||
"result": {
|
"result": {
|
||||||
"components": {"computercraft:pocket_upgrade": {"id": "computercraft:wireless_modem_normal"}},
|
"components": {"computercraft:back_pocket_upgrade": {"id": "computercraft:wireless_modem_normal"}},
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"id": "computercraft:pocket_computer_normal"
|
"id": "computercraft:pocket_computer_normal"
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import net.minecraft.core.HolderLookup;
|
|||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.component.DataComponentPatch;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.codec.ByteBufCodecs;
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.resources.RegistryFixedCodec;
|
import net.minecraft.resources.RegistryFixedCodec;
|
||||||
@ -95,6 +96,29 @@ public final class UpgradeManager<T extends UpgradeBase> {
|
|||||||
// TODO: Would be nice if we could use the registration info here.
|
// TODO: Would be nice if we could use the registration info here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine our "creator mod" from a list of upgrades.
|
||||||
|
* <p>
|
||||||
|
* We attempt to find the first non-vanilla/non-CC upgrade.
|
||||||
|
*
|
||||||
|
* @param first The first upgrade.
|
||||||
|
* @param second The second upgrade.
|
||||||
|
* @return The owning mod id of this item.
|
||||||
|
*/
|
||||||
|
public String getOwner(@Nullable UpgradeData<T> first, @Nullable UpgradeData<T> second) {
|
||||||
|
if (first != null) {
|
||||||
|
var mod = getOwner(first.holder());
|
||||||
|
if (!mod.equals(ComputerCraftAPI.MOD_ID)) return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (second != null) {
|
||||||
|
var mod = getOwner(second.holder());
|
||||||
|
if (!mod.equals(ComputerCraftAPI.MOD_ID)) return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ComputerCraftAPI.MOD_ID;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public UpgradeData<T> get(HolderLookup.Provider registries, ItemStack stack) {
|
public UpgradeData<T> get(HolderLookup.Provider registries, ItemStack stack) {
|
||||||
if (stack.isEmpty()) return null;
|
if (stack.isEmpty()) return null;
|
||||||
@ -109,4 +133,16 @@ public final class UpgradeManager<T extends UpgradeBase> {
|
|||||||
.map(x -> UpgradeData.of(x, x.value().getUpgradeData(stack)))
|
.map(x -> UpgradeData.of(x, x.value().getUpgradeData(stack)))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Component getName(String baseString, @Nullable UpgradeBase first, @Nullable UpgradeBase second) {
|
||||||
|
if (first != null && second != null) {
|
||||||
|
return Component.translatable(baseString + ".upgraded_twice", second.getAdjective(), first.getAdjective());
|
||||||
|
} else if (first != null) {
|
||||||
|
return Component.translatable(baseString + ".upgraded", first.getAdjective());
|
||||||
|
} else if (second != null) {
|
||||||
|
return Component.translatable(baseString + ".upgraded", second.getAdjective());
|
||||||
|
} else {
|
||||||
|
return Component.translatable(baseString);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import com.llamalad7.mixinextras.sugar.Local;
|
|||||||
import com.mojang.datafixers.DataFixUtils;
|
import com.mojang.datafixers.DataFixUtils;
|
||||||
import com.mojang.datafixers.DataFixerBuilder;
|
import com.mojang.datafixers.DataFixerBuilder;
|
||||||
import com.mojang.datafixers.schemas.Schema;
|
import com.mojang.datafixers.schemas.Schema;
|
||||||
|
import dan200.computercraft.shared.datafix.RenamePocketComputerUpgradeFix;
|
||||||
import dan200.computercraft.shared.datafix.TurtleUpgradeComponentizationFix;
|
import dan200.computercraft.shared.datafix.TurtleUpgradeComponentizationFix;
|
||||||
import net.minecraft.util.datafix.DataFixers;
|
import net.minecraft.util.datafix.DataFixers;
|
||||||
import net.minecraft.util.datafix.fixes.ItemStackComponentizationFix;
|
import net.minecraft.util.datafix.fixes.ItemStackComponentizationFix;
|
||||||
@ -42,6 +43,26 @@ abstract class DataFixersMixin {
|
|||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a {@link RenamePocketComputerUpgradeFix} fix.
|
||||||
|
*
|
||||||
|
* @param schema The {@code V4314} schema.
|
||||||
|
* @param builder The current datafixer builder.
|
||||||
|
* @return The input schema.
|
||||||
|
*/
|
||||||
|
@ModifyArg(
|
||||||
|
method = "addFixers",
|
||||||
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/datafix/fixes/InlineBlockPosFormatFix;<init>(Lcom/mojang/datafixers/schemas/Schema;)V"),
|
||||||
|
index = 0,
|
||||||
|
allow = 1
|
||||||
|
)
|
||||||
|
@SuppressWarnings("UnusedMethod")
|
||||||
|
private static Schema addRenamePocketComputerUpgradeFix(Schema schema, @Local DataFixerBuilder builder) {
|
||||||
|
assertSchemaVersion(schema, RenamePocketComputerUpgradeFix.SCHEMA_VERSION);
|
||||||
|
builder.addFixer(new RenamePocketComputerUpgradeFix(schema));
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static void assertSchemaVersion(Schema schema, int version) {
|
private static void assertSchemaVersion(Schema schema, int version) {
|
||||||
if (schema.getVersionKey() != version) {
|
if (schema.getVersionKey() != version) {
|
||||||
|
@ -25,7 +25,7 @@ class V3818_3Mixin {
|
|||||||
@ModifyReturnValue(method = "components", at = @At("TAIL"))
|
@ModifyReturnValue(method = "components", at = @At("TAIL"))
|
||||||
@SuppressWarnings("UnusedMethod")
|
@SuppressWarnings("UnusedMethod")
|
||||||
private static SequencedMap<String, Supplier<TypeTemplate>> components(SequencedMap<String, Supplier<TypeTemplate>> types, Schema schema) {
|
private static SequencedMap<String, Supplier<TypeTemplate>> components(SequencedMap<String, Supplier<TypeTemplate>> types, Schema schema) {
|
||||||
ComponentizationFixers.addExtraTypes(types, schema);
|
ComponentizationFixers.addComponents(types, schema);
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import dan200.computercraft.api.upgrades.UpgradeBase;
|
|||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
|
import dan200.computercraft.impl.TurtleUpgrades;
|
||||||
import dan200.computercraft.shared.command.UserLevel;
|
import dan200.computercraft.shared.command.UserLevel;
|
||||||
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
|
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
|
||||||
import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
|
import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
|
||||||
@ -71,6 +72,7 @@ import dan200.computercraft.shared.platform.PlatformHelper;
|
|||||||
import dan200.computercraft.shared.platform.RegistrationHelper;
|
import dan200.computercraft.shared.platform.RegistrationHelper;
|
||||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||||
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketComputerInternal;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
||||||
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
||||||
@ -365,7 +367,7 @@ public final class ModRegistry {
|
|||||||
* @see TurtleItem
|
* @see TurtleItem
|
||||||
*/
|
*/
|
||||||
public static final RegistryEntry<DataComponentType<UpgradeData<ITurtleUpgrade>>> LEFT_TURTLE_UPGRADE = register("left_turtle_upgrade", b -> b
|
public static final RegistryEntry<DataComponentType<UpgradeData<ITurtleUpgrade>>> LEFT_TURTLE_UPGRADE = register("left_turtle_upgrade", b -> b
|
||||||
.persistent(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataCodec()).networkSynchronized(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataStreamCodec())
|
.persistent(TurtleUpgrades.instance().upgradeDataCodec()).networkSynchronized(TurtleUpgrades.instance().upgradeDataStreamCodec())
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -374,7 +376,7 @@ public final class ModRegistry {
|
|||||||
* @see TurtleItem
|
* @see TurtleItem
|
||||||
*/
|
*/
|
||||||
public static final RegistryEntry<DataComponentType<UpgradeData<ITurtleUpgrade>>> RIGHT_TURTLE_UPGRADE = register("right_turtle_upgrade", b -> b
|
public static final RegistryEntry<DataComponentType<UpgradeData<ITurtleUpgrade>>> RIGHT_TURTLE_UPGRADE = register("right_turtle_upgrade", b -> b
|
||||||
.persistent(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataCodec()).networkSynchronized(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataStreamCodec())
|
.persistent(TurtleUpgrades.instance().upgradeDataCodec()).networkSynchronized(TurtleUpgrades.instance().upgradeDataStreamCodec())
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -396,7 +398,16 @@ public final class ModRegistry {
|
|||||||
*
|
*
|
||||||
* @see PocketComputerItem
|
* @see PocketComputerItem
|
||||||
*/
|
*/
|
||||||
public static final RegistryEntry<DataComponentType<UpgradeData<IPocketUpgrade>>> POCKET_UPGRADE = register("pocket_upgrade", b -> b
|
public static final RegistryEntry<DataComponentType<UpgradeData<IPocketUpgrade>>> BACK_POCKET_UPGRADE = register("back_pocket_upgrade", b -> b
|
||||||
|
.persistent(PocketUpgrades.instance().upgradeDataCodec()).networkSynchronized(PocketUpgrades.instance().upgradeDataStreamCodec())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The back upgrade of a pocket computer.
|
||||||
|
*
|
||||||
|
* @see PocketComputerItem
|
||||||
|
*/
|
||||||
|
public static final RegistryEntry<DataComponentType<UpgradeData<IPocketUpgrade>>> BOTTOM_POCKET_UPGRADE = register("bottom_pocket_upgrade", b -> b
|
||||||
.persistent(PocketUpgrades.instance().upgradeDataCodec()).networkSynchronized(PocketUpgrades.instance().upgradeDataStreamCodec())
|
.persistent(PocketUpgrades.instance().upgradeDataCodec()).networkSynchronized(PocketUpgrades.instance().upgradeDataStreamCodec())
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -649,7 +660,7 @@ public final class ModRegistry {
|
|||||||
|
|
||||||
ComputerCraftAPI.registerAPIFactory(computer -> {
|
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||||
var pocket = computer.getComponent(ComputerComponents.POCKET);
|
var pocket = computer.getComponent(ComputerComponents.POCKET);
|
||||||
return pocket == null ? null : new PocketAPI(pocket);
|
return pocket == null ? null : new PocketAPI((PocketComputerInternal) pocket);
|
||||||
});
|
});
|
||||||
|
|
||||||
ComputerCraftAPI.registerAPIFactory(computer -> {
|
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||||
@ -751,7 +762,7 @@ public final class ModRegistry {
|
|||||||
out.accept(new ItemStack(pocket));
|
out.accept(new ItemStack(pocket));
|
||||||
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements()
|
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements()
|
||||||
.filter(ModRegistry::isOurUpgrade)
|
.filter(ModRegistry::isOurUpgrade)
|
||||||
.map(x -> DataComponentUtil.createStack(pocket, DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept);
|
.map(x -> DataComponentUtil.createStack(pocket, DataComponents.BACK_POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isOurUpgrade(Holder.Reference<? extends UpgradeBase> upgrade) {
|
private static boolean isOurUpgrade(Holder.Reference<? extends UpgradeBase> upgrade) {
|
||||||
|
@ -204,21 +204,30 @@ public class ComponentizationFixers {
|
|||||||
return newUpgrade;
|
return newUpgrade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add our custom data components to the datafixer system.
|
* Add our custom data components to the datafixer system.
|
||||||
*
|
*
|
||||||
* @param types The component type definition.
|
* @param types The component type definition.
|
||||||
* @param schema The current schema.
|
* @param schema The current schema.
|
||||||
* @see UpgradeManager#upgradeDataCodec()
|
* @see UpgradeManager#upgradeDataCodec()
|
||||||
* @see ModRegistry.DataComponents#POCKET_UPGRADE
|
* @see ModRegistry.DataComponents#BOTTOM_POCKET_UPGRADE
|
||||||
|
* @see ModRegistry.DataComponents#BACK_POCKET_UPGRADE
|
||||||
* @see ModRegistry.DataComponents#LEFT_TURTLE_UPGRADE
|
* @see ModRegistry.DataComponents#LEFT_TURTLE_UPGRADE
|
||||||
* @see ModRegistry.DataComponents#RIGHT_TURTLE_UPGRADE
|
* @see ModRegistry.DataComponents#RIGHT_TURTLE_UPGRADE
|
||||||
*/
|
*/
|
||||||
public static void addExtraTypes(Map<String, Supplier<TypeTemplate>> types, Schema schema) {
|
public static void addComponents(Map<String, Supplier<TypeTemplate>> types, Schema schema) {
|
||||||
// Create a codec for UpgradeData
|
// Create a codec for UpgradeData
|
||||||
Supplier<TypeTemplate> upgradeData = () -> DSL.optionalFields("components", References.DATA_COMPONENTS.in(schema));
|
Supplier<TypeTemplate> upgradeData = () -> DSL.optionalFields("components", References.DATA_COMPONENTS.in(schema));
|
||||||
|
|
||||||
types.put("computercraft:pocket_upgrade", upgradeData);
|
if (schema.getVersionKey() < RenamePocketComputerUpgradeFix.SCHEMA_VERSION) {
|
||||||
|
types.put("computercraft:pocket_upgrade", upgradeData);
|
||||||
|
} else {
|
||||||
|
// Add extra upgrades on later versions. Really this should be done by overriding
|
||||||
|
types.put("computercraft:back_pocket_upgrade", upgradeData);
|
||||||
|
types.put("computercraft:bottom_pocket_upgrade", upgradeData);
|
||||||
|
}
|
||||||
|
|
||||||
types.put("computercraft:left_turtle_upgrade", upgradeData);
|
types.put("computercraft:left_turtle_upgrade", upgradeData);
|
||||||
types.put("computercraft:right_turtle_upgrade", upgradeData);
|
types.put("computercraft:right_turtle_upgrade", upgradeData);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.datafix;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.DataFix;
|
||||||
|
import com.mojang.datafixers.DataFixUtils;
|
||||||
|
import com.mojang.datafixers.TypeRewriteRule;
|
||||||
|
import com.mojang.datafixers.schemas.Schema;
|
||||||
|
import com.mojang.datafixers.types.Type;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import net.minecraft.util.datafix.fixes.DataComponentRemainderFix;
|
||||||
|
import net.minecraft.util.datafix.fixes.FoodToConsumableFix;
|
||||||
|
import net.minecraft.util.datafix.fixes.References;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames {@code "computercraft:pocket_upgrade"} to {@link ModRegistry.DataComponents#BACK_POCKET_UPGRADE
|
||||||
|
* "computercraft:back_pocket_upgrade"}.
|
||||||
|
*
|
||||||
|
* @see ComponentizationFixers#addComponents(Map, Schema)
|
||||||
|
*/
|
||||||
|
public final class RenamePocketComputerUpgradeFix extends DataFix {
|
||||||
|
public static final int SCHEMA_VERSION = DataFixUtils.makeKey(4314, 0);
|
||||||
|
|
||||||
|
public RenamePocketComputerUpgradeFix(Schema outputSchema) {
|
||||||
|
super(outputSchema, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a rewrite rule to rename a component.
|
||||||
|
* <p>
|
||||||
|
* We use {@link #writeFixAndRead(String, Type, Type, Function)} rather than
|
||||||
|
* {@link #fixTypeEverywhereTyped(String, Type, Function)}, as the types don't neatly line up. This is consistent
|
||||||
|
* with what {@link FoodToConsumableFix} does.
|
||||||
|
* <p>
|
||||||
|
* {@link DataComponentRemainderFix} <em>does</em> use {@code fixTypeEverywhereTyped}. However, none of the
|
||||||
|
* components it references are in the component map, so don't cause the type to change!
|
||||||
|
*
|
||||||
|
* @return The constructed rewrite rule.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected TypeRewriteRule makeRule() {
|
||||||
|
return writeFixAndRead(
|
||||||
|
"Pocket upgrade rename",
|
||||||
|
getInputSchema().getType(References.DATA_COMPONENTS),
|
||||||
|
getOutputSchema().getType(References.DATA_COMPONENTS),
|
||||||
|
dynamic -> dynamic.renameField("computercraft:pocket_upgrade", "computercraft:back_pocket_upgrade")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RenamePocketComputerUpgradeFix.class);
|
||||||
|
}
|
@ -69,7 +69,7 @@ public final class RecipeModHelpers {
|
|||||||
for (var pocketSupplier : POCKET_COMPUTERS) {
|
for (var pocketSupplier : POCKET_COMPUTERS) {
|
||||||
var pocket = pocketSupplier.get();
|
var pocket = pocketSupplier.get();
|
||||||
forEachRegistry(registries, IPocketUpgrade.REGISTRY, upgrade ->
|
forEachRegistry(registries, IPocketUpgrade.REGISTRY, upgrade ->
|
||||||
upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
|
upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
|||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketSide;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
@ -134,14 +135,22 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
return Collections.unmodifiableList(recipes);
|
return Collections.unmodifiableList(recipes);
|
||||||
} else if (stack.getItem() instanceof PocketComputerItem) {
|
} else if (stack.getItem() instanceof PocketComputerItem) {
|
||||||
// Suggest possible upgrades which can be applied to this turtle
|
// Suggest possible upgrades which can be applied to this turtle
|
||||||
var back = PocketComputerItem.getUpgrade(stack);
|
var back = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BACK);
|
||||||
if (back != null) return List.of();
|
var bottom = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BOTTOM);
|
||||||
|
if (back != null && bottom != null) return List.of();
|
||||||
|
|
||||||
List<T> recipes = new ArrayList<>();
|
List<T> recipes = new ArrayList<>();
|
||||||
var ingredient = new SlotDisplay.ItemStackSlotDisplay(stack);
|
var ingredient = new SlotDisplay.ItemStackSlotDisplay(stack);
|
||||||
for (var upgrade : pocketUpgrades) {
|
for (var upgrade : pocketUpgrades) {
|
||||||
if (upgrade.pocket == null) throw new NullPointerException();
|
if (upgrade.pocket == null) throw new NullPointerException();
|
||||||
recipes.add(pocket(upgrade.ingredient, ingredient, pocketWith(stack, UpgradeData.ofDefault(upgrade.pocket))));
|
|
||||||
|
if (back == null) {
|
||||||
|
recipes.add(pocket(upgrade.ingredient, ingredient, pocketWith(stack, UpgradeData.ofDefault(upgrade.pocket), bottom)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bottom == null) {
|
||||||
|
recipes.add(pocket(ingredient, upgrade.ingredient, pocketWith(stack, back, UpgradeData.ofDefault(upgrade.pocket))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.unmodifiableList(recipes);
|
return Collections.unmodifiableList(recipes);
|
||||||
@ -208,9 +217,22 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
} else if (stack.getItem() instanceof PocketComputerItem) {
|
} else if (stack.getItem() instanceof PocketComputerItem) {
|
||||||
List<T> recipes = new ArrayList<>(0);
|
List<T> recipes = new ArrayList<>(0);
|
||||||
|
|
||||||
var back = PocketComputerItem.getUpgradeWithData(stack);
|
var back = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BACK);
|
||||||
|
var bottom = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BOTTOM);
|
||||||
if (back != null) {
|
if (back != null) {
|
||||||
recipes.add(pocket(new SlotDisplay.ItemStackSlotDisplay(back.getUpgradeItem()), new SlotDisplay.ItemStackSlotDisplay(pocketWith(stack, null)), stack));
|
recipes.add(pocket(
|
||||||
|
new SlotDisplay.ItemStackSlotDisplay(back.getUpgradeItem()),
|
||||||
|
new SlotDisplay.ItemStackSlotDisplay(pocketWith(stack, null, bottom)),
|
||||||
|
stack
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bottom != null) {
|
||||||
|
recipes.add(pocket(
|
||||||
|
new SlotDisplay.ItemStackSlotDisplay(pocketWith(stack, back, null)),
|
||||||
|
new SlotDisplay.ItemStackSlotDisplay(bottom.getUpgradeItem()),
|
||||||
|
stack
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.unmodifiableList(recipes);
|
return Collections.unmodifiableList(recipes);
|
||||||
@ -226,15 +248,16 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
return newStack;
|
return newStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back) {
|
private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back, @Nullable UpgradeData<IPocketUpgrade> bottom) {
|
||||||
var newStack = stack.copyWithCount(1);
|
var newStack = stack.copyWithCount(1);
|
||||||
newStack.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), back);
|
newStack.set(ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), back);
|
||||||
|
newStack.set(ModRegistry.DataComponents.BOTTOM_POCKET_UPGRADE.get(), bottom);
|
||||||
return newStack;
|
return newStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private T pocket(SlotDisplay upgrade, SlotDisplay pocketComputer, ItemStack result) {
|
private T pocket(SlotDisplay top, SlotDisplay bottom, ItemStack result) {
|
||||||
return wrap.apply(new ShapedCraftingRecipeDisplay(
|
return wrap.apply(new ShapedCraftingRecipeDisplay(
|
||||||
1, 2, List.of(upgrade, pocketComputer), new SlotDisplay.ItemStackSlotDisplay(result), CRAFTING_STATION
|
1, 2, List.of(top, bottom), new SlotDisplay.ItemStackSlotDisplay(result), CRAFTING_STATION
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +306,7 @@ public class UpgradeRecipeGenerator<T> {
|
|||||||
recipes.add(pocket(
|
recipes.add(pocket(
|
||||||
ingredient,
|
ingredient,
|
||||||
new SlotDisplay.ItemSlotDisplay(pocketItem),
|
new SlotDisplay.ItemSlotDisplay(pocketItem),
|
||||||
DataComponentUtil.createStack(pocketItem, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(pocket))
|
DataComponentUtil.createStack(pocketItem, ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), UpgradeData.ofDefault(pocket))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.speaker;
|
|||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,15 +16,15 @@ import dan200.computercraft.shared.network.server.ServerNetworking;
|
|||||||
public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral {
|
public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral {
|
||||||
public static final String ADJECTIVE = "upgrade.computercraft.speaker.adjective";
|
public static final String ADJECTIVE = "upgrade.computercraft.speaker.adjective";
|
||||||
|
|
||||||
|
protected abstract ServerLevel getLevel();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void detach(IComputerAccess computer) {
|
public void detach(IComputerAccess computer) {
|
||||||
super.detach(computer);
|
super.detach(computer);
|
||||||
|
|
||||||
// We could be in the process of shutting down the server, so we can't send packets in this case.
|
// We could be in the process of shutting down the server, so we can't send packets in this case.
|
||||||
var level = getPosition().level();
|
var server = getLevel().getServer();
|
||||||
if (level == null) return;
|
if (server.isStopped()) return;
|
||||||
var server = level.getServer();
|
|
||||||
if (server == null || server.isStopped()) return;
|
|
||||||
|
|
||||||
ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server);
|
ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server);
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,11 @@ package dan200.computercraft.shared.pocket.apis;
|
|||||||
|
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketComputerInternal;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketSide;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
@ -41,9 +42,9 @@ import java.util.Objects;
|
|||||||
* @cc.module pocket
|
* @cc.module pocket
|
||||||
*/
|
*/
|
||||||
public class PocketAPI implements ILuaAPI {
|
public class PocketAPI implements ILuaAPI {
|
||||||
private final IPocketAccess pocket;
|
private final PocketComputerInternal pocket;
|
||||||
|
|
||||||
public PocketAPI(IPocketAccess pocket) {
|
public PocketAPI(PocketComputerInternal pocket) {
|
||||||
this.pocket = pocket;
|
this.pocket = pocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ public class PocketAPI implements ILuaAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search the player's inventory for another upgrade, replacing the existing one with that item if found.
|
* Search the player's inventory for another upgrade, replacing the existing back upgrade with that item if found.
|
||||||
* <p>
|
* <p>
|
||||||
* This inventory search starts from the player's currently selected slot, allowing you to prioritise upgrades.
|
* This inventory search starts from the player's currently selected slot, allowing you to prioritise upgrades.
|
||||||
*
|
*
|
||||||
@ -63,10 +64,29 @@ public class PocketAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction(mainThread = true)
|
@LuaFunction(mainThread = true)
|
||||||
public final Object[] equipBack() {
|
public final Object[] equipBack() {
|
||||||
|
return equip(PocketSide.BACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the player's inventory for another upgrade, replacing the existing bottom upgrade with that item if found.
|
||||||
|
* <p>
|
||||||
|
* This inventory search starts from the player's currently selected slot, allowing you to prioritise upgrades.
|
||||||
|
*
|
||||||
|
* @return The result of equipping.
|
||||||
|
* @cc.treturn boolean If an item was equipped.
|
||||||
|
* @cc.treturn string|nil The reason an item was not equipped.
|
||||||
|
*/
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final Object[] equipBottom() {
|
||||||
|
return equip(PocketSide.BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] equip(PocketSide side) {
|
||||||
var entity = pocket.getEntity();
|
var entity = pocket.getEntity();
|
||||||
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
||||||
|
|
||||||
var inventory = player.getInventory();
|
var inventory = player.getInventory();
|
||||||
var previousUpgrade = pocket.getUpgrade();
|
var previousUpgrade = pocket.getUpgrade(side);
|
||||||
|
|
||||||
// Attempt to find the upgrade, starting in the main segment, and then looking in the opposite
|
// Attempt to find the upgrade, starting in the main segment, and then looking in the opposite
|
||||||
// one. We start from the position the item is currently in and loop round to the start.
|
// one. We start from the position the item is currently in and loop round to the start.
|
||||||
@ -82,13 +102,13 @@ public class PocketAPI implements ILuaAPI {
|
|||||||
if (previousUpgrade != null) storeItem(player, previousUpgrade.getUpgradeItem());
|
if (previousUpgrade != null) storeItem(player, previousUpgrade.getUpgradeItem());
|
||||||
|
|
||||||
// Set the new upgrade
|
// Set the new upgrade
|
||||||
pocket.setUpgrade(newUpgrade);
|
pocket.setUpgrade(side, newUpgrade);
|
||||||
|
|
||||||
return new Object[]{ true };
|
return new Object[]{ true };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the pocket computer's current upgrade.
|
* Remove the pocket computer's back upgrade.
|
||||||
*
|
*
|
||||||
* @return The result of unequipping.
|
* @return The result of unequipping.
|
||||||
* @cc.treturn boolean If the upgrade was unequipped.
|
* @cc.treturn boolean If the upgrade was unequipped.
|
||||||
@ -96,13 +116,29 @@ public class PocketAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction(mainThread = true)
|
@LuaFunction(mainThread = true)
|
||||||
public final Object[] unequipBack() {
|
public final Object[] unequipBack() {
|
||||||
|
return unequip(PocketSide.BACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the pocket computer's bottom upgrade.
|
||||||
|
*
|
||||||
|
* @return The result of unequipping.
|
||||||
|
* @cc.treturn boolean If the upgrade was unequipped.
|
||||||
|
* @cc.treturn string|nil The reason an upgrade was not unequipped.
|
||||||
|
*/
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final Object[] unequipBottom() {
|
||||||
|
return unequip(PocketSide.BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] unequip(PocketSide side) {
|
||||||
var entity = pocket.getEntity();
|
var entity = pocket.getEntity();
|
||||||
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
||||||
var previousUpgrade = pocket.getUpgrade();
|
|
||||||
|
|
||||||
|
var previousUpgrade = pocket.getUpgrade(side);
|
||||||
if (previousUpgrade == null) return new Object[]{ false, "Nothing to unequip" };
|
if (previousUpgrade == null) return new Object[]{ false, "Nothing to unequip" };
|
||||||
|
|
||||||
pocket.setUpgrade(null);
|
pocket.setUpgrade(side, null);
|
||||||
|
|
||||||
storeItem(player, previousUpgrade.getUpgradeItem());
|
storeItem(player, previousUpgrade.getUpgradeItem());
|
||||||
|
|
||||||
|
@ -8,46 +8,48 @@ import dan200.computercraft.api.pocket.IPocketAccess;
|
|||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
|
import dan200.computercraft.core.util.Nullability;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
|
||||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
import net.minecraft.core.component.DataComponentPatch;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
|
import net.minecraft.core.component.DataComponentType;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.util.ARGB;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds additional state for a pocket computer. This includes pocket computer upgrade,
|
* Holds additional state for a pocket computer. This includes pocket computer upgrade,
|
||||||
* {@linkplain IPocketAccess#getLight() light colour} and {@linkplain IPocketAccess#getColour() colour}.
|
* {@linkplain IPocketAccess#getLight() light colour} and {@linkplain IPocketAccess#getColour() colour}.
|
||||||
* <p>
|
* <p>
|
||||||
* This state is read when the brain is created, and written back to the holding item stack when the holding entity is
|
* This state is read when the brain is created, and then written back to the item whenever changed.
|
||||||
* ticked (see {@link #updateItem(ItemStack)}).
|
|
||||||
*/
|
*/
|
||||||
public final class PocketBrain implements IPocketAccess {
|
public final class PocketBrain implements PocketComputerInternal {
|
||||||
private final PocketServerComputer computer;
|
private final PocketServerComputer computer;
|
||||||
|
|
||||||
private PocketHolder holder;
|
private PocketHolder holder;
|
||||||
private Vec3 position;
|
private Vec3 position;
|
||||||
|
|
||||||
private boolean dirty = false;
|
private final Map<PocketSide, UpgradeAccess> upgrades = new EnumMap<>(PocketSide.class);
|
||||||
private @Nullable UpgradeData<IPocketUpgrade> upgrade;
|
|
||||||
private int colour = -1;
|
|
||||||
private int lightColour = -1;
|
|
||||||
|
|
||||||
public PocketBrain(PocketHolder holder, @Nullable UpgradeData<IPocketUpgrade> upgrade, int colour, ServerComputer.Properties properties) {
|
public PocketBrain(PocketHolder holder, ServerComputer.Properties properties) {
|
||||||
this.computer = new PocketServerComputer(this, holder, properties);
|
this.computer = new PocketServerComputer(this, holder, properties);
|
||||||
this.holder = holder;
|
this.holder = holder;
|
||||||
this.position = holder.pos();
|
this.position = holder.pos();
|
||||||
this.upgrade = upgrade;
|
|
||||||
this.colour = colour;
|
upgrades.put(PocketSide.BACK, new UpgradeAccess(ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), ComputerSide.BACK));
|
||||||
invalidatePeripheral();
|
upgrades.put(PocketSide.BOTTOM, new UpgradeAccess(ModRegistry.DataComponents.BOTTOM_POCKET_UPGRADE.get(), ComputerSide.BOTTOM));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,25 +85,6 @@ public final class PocketBrain implements IPocketAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Write back properties of the pocket brain to the item.
|
|
||||||
*
|
|
||||||
* @param stack The pocket computer stack to update.
|
|
||||||
* @return Whether the item was changed.
|
|
||||||
*/
|
|
||||||
public boolean updateItem(ItemStack stack) {
|
|
||||||
if (!dirty) return false;
|
|
||||||
this.dirty = false;
|
|
||||||
|
|
||||||
if (colour == -1) {
|
|
||||||
stack.remove(DataComponents.DYED_COLOR);
|
|
||||||
} else {
|
|
||||||
DataComponentUtil.setDyeColour(stack, colour);
|
|
||||||
}
|
|
||||||
PocketComputerItem.setUpgrade(stack, upgrade);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ServerLevel getLevel() {
|
public ServerLevel getLevel() {
|
||||||
return computer.getLevel();
|
return computer.getLevel();
|
||||||
@ -114,71 +97,214 @@ public final class PocketBrain implements IPocketAccess {
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requireMainThread() {
|
||||||
|
if (!computer.getLevel().getServer().isSameThread()) {
|
||||||
|
throw new IllegalStateException("Must be called from the main thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack requireStack() {
|
||||||
|
requireMainThread();
|
||||||
|
var stack = holder.getStack(computer);
|
||||||
|
if (stack.isEmpty()) throw new IllegalStateException("Pocket computer is not active");
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Entity getEntity() {
|
public @Nullable Entity getEntity() {
|
||||||
|
requireMainThread();
|
||||||
return holder instanceof PocketHolder.EntityHolder entity && holder.isValid(computer) ? entity.entity() : null;
|
return holder instanceof PocketHolder.EntityHolder entity && holder.isValid(computer) ? entity.entity() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
requireMainThread();
|
||||||
|
return holder.isValid(computer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getColour() {
|
public int getColour() {
|
||||||
return colour;
|
return DyedItemColor.getOrDefault(requireStack(), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setColour(int colour) {
|
public void setColour(int colour) {
|
||||||
if (this.colour == colour) return;
|
var stack = requireStack();
|
||||||
dirty = true;
|
|
||||||
this.colour = colour;
|
if (DyedItemColor.getOrDefault(stack, -1) == colour) return;
|
||||||
|
|
||||||
|
if (colour == -1) {
|
||||||
|
stack.remove(DataComponents.DYED_COLOR);
|
||||||
|
} else {
|
||||||
|
DataComponentUtil.setDyeColour(stack, colour);
|
||||||
|
}
|
||||||
|
holder.setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLight() {
|
public int getLight() {
|
||||||
return lightColour;
|
// Take the average of all upgrade lights. This is very naive, and just works in sRGB, rather than
|
||||||
|
// linear colour space.
|
||||||
|
int count = 0, totalR = 0, totalG = 0, totalB = 0;
|
||||||
|
for (var upgrade : upgrades.values()) {
|
||||||
|
var colour = upgrade.lightColour;
|
||||||
|
if (colour == -1) continue;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
totalR += ARGB.red(colour);
|
||||||
|
totalG += ARGB.green(colour);
|
||||||
|
totalB += ARGB.blue(colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
return count == 0 ? -1 : ARGB.color(totalR / count, totalG / count, totalB / count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
for (var holder : upgrades.values()) {
|
||||||
|
if (holder.upgrade == null) continue;
|
||||||
|
holder.upgrade.upgrade().update(holder, computer.getPeripheral(holder.side));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onRightClick(ServerLevel level) {
|
||||||
|
for (var holder : upgrades.values()) {
|
||||||
|
if (holder.upgrade == null) continue;
|
||||||
|
return holder.upgrade.upgrade().onRightClick(level, holder, computer.getPeripheral(holder.side));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpgradeAccess getUpgradeAccess(PocketSide side) {
|
||||||
|
return Nullability.assertNonNull(upgrades.get(side));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLight(int colour) {
|
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade(PocketSide side) {
|
||||||
if (colour < 0 || colour > 0xFFFFFF) colour = -1;
|
return getUpgradeAccess(side).getUpgrade();
|
||||||
lightColour = colour;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataComponentPatch getUpgradeData() {
|
public void setUpgrade(PocketSide side, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||||
var upgrade = this.upgrade;
|
getUpgradeAccess(side).setUpgrade(upgrade);
|
||||||
return upgrade == null ? DataComponentPatch.EMPTY : upgrade.data();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setUpgrades(@Nullable UpgradeData<IPocketUpgrade> back, @Nullable UpgradeData<IPocketUpgrade> bottom) {
|
||||||
public void setUpgradeData(DataComponentPatch data) {
|
getUpgradeAccess(PocketSide.BACK).setUpgradeDirect(back);
|
||||||
var upgrade = this.upgrade;
|
getUpgradeAccess(PocketSide.BOTTOM).setUpgradeDirect(bottom);
|
||||||
if (upgrade == null) return;
|
|
||||||
this.upgrade = UpgradeData.of(upgrade.holder(), data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private final class UpgradeAccess implements IPocketAccess {
|
||||||
public void invalidatePeripheral() {
|
private final DataComponentType<UpgradeData<IPocketUpgrade>> component;
|
||||||
var peripheral = upgrade == null ? null : upgrade.upgrade().createPeripheral(this);
|
private final ComputerSide side;
|
||||||
computer.setPeripheral(ComputerSide.BACK, peripheral);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private @Nullable UpgradeData<IPocketUpgrade> upgrade;
|
||||||
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade() {
|
private int lightColour = -1;
|
||||||
return upgrade;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private UpgradeAccess(DataComponentType<UpgradeData<IPocketUpgrade>> component, ComputerSide side) {
|
||||||
* Set the upgrade for this pocket computer, also updating the item stack.
|
this.component = component;
|
||||||
* <p>
|
this.side = side;
|
||||||
* Note this method is not thread safe - it must be called from the server thread.
|
}
|
||||||
*
|
|
||||||
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
|
||||||
if (Objects.equals(this.upgrade, upgrade)) return;
|
|
||||||
|
|
||||||
this.upgrade = upgrade;
|
@Override
|
||||||
dirty = true;
|
public ServerLevel getLevel() {
|
||||||
invalidatePeripheral();
|
return PocketBrain.this.getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3 getPosition() {
|
||||||
|
return PocketBrain.this.getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Entity getEntity() {
|
||||||
|
return PocketBrain.this.getEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
return PocketBrain.this.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColour() {
|
||||||
|
return PocketBrain.this.getColour();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColour(int colour) {
|
||||||
|
PocketBrain.this.setColour(colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLight() {
|
||||||
|
return lightColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLight(int colour) {
|
||||||
|
if (colour < 0 || colour > 0xFFFFFF) colour = -1;
|
||||||
|
lightColour = colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataComponentPatch getUpgradeData() {
|
||||||
|
var upgrade = this.upgrade;
|
||||||
|
return upgrade == null ? DataComponentPatch.EMPTY : upgrade.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUpgradeData(DataComponentPatch data) {
|
||||||
|
var stack = requireStack();
|
||||||
|
|
||||||
|
var upgrade = this.upgrade;
|
||||||
|
if (upgrade == null || upgrade.data().equals(data)) return;
|
||||||
|
|
||||||
|
this.upgrade = UpgradeData.of(upgrade.holder(), data);
|
||||||
|
stack.set(component, upgrade);
|
||||||
|
holder.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidatePeripheral() {
|
||||||
|
var peripheral = upgrade == null ? null : upgrade.upgrade().createPeripheral(this);
|
||||||
|
computer.setPeripheral(side, peripheral);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade() {
|
||||||
|
return upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the upgrade for this pocket computer, also updating the item stack.
|
||||||
|
* <p>
|
||||||
|
* Note this method is not thread safe - it must be called from the server thread.
|
||||||
|
*
|
||||||
|
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||||
|
var stack = requireStack();
|
||||||
|
|
||||||
|
if (!setUpgradeDirect(upgrade)) return;
|
||||||
|
|
||||||
|
stack.set(component, upgrade);
|
||||||
|
holder.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an upgrade without writing it back to the stack.
|
||||||
|
*
|
||||||
|
* @param upgrade The upgrade to set.
|
||||||
|
* @return Whether the upgrade changed.
|
||||||
|
*/
|
||||||
|
private boolean setUpgradeDirect(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||||
|
if (Objects.equals(this.upgrade, upgrade)) return false;
|
||||||
|
|
||||||
|
this.upgrade = upgrade;
|
||||||
|
lightColour = -1;
|
||||||
|
invalidatePeripheral();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.pocket.core;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
|
import dan200.computercraft.api.pocket.PocketComputer;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal version of {@link PocketComputer}.
|
||||||
|
* <p>
|
||||||
|
* This exposes additional functionality we don't want in the public API, but where we don't want access to the full
|
||||||
|
* {@link PocketBrain} interface.
|
||||||
|
*/
|
||||||
|
public interface PocketComputerInternal extends PocketComputer {
|
||||||
|
@Nullable
|
||||||
|
UpgradeData<IPocketUpgrade> getUpgrade(PocketSide side);
|
||||||
|
|
||||||
|
void setUpgrade(PocketSide side, @Nullable UpgradeData<IPocketUpgrade> upgrade);
|
||||||
|
}
|
@ -15,6 +15,7 @@ import net.minecraft.world.entity.Entity;
|
|||||||
import net.minecraft.world.entity.EquipmentSlot;
|
import net.minecraft.world.entity.EquipmentSlot;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.item.ItemEntity;
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,13 +43,23 @@ public sealed interface PocketHolder {
|
|||||||
*/
|
*/
|
||||||
BlockPos blockPos();
|
BlockPos blockPos();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current pocket computer stack.
|
||||||
|
*
|
||||||
|
* @param computer The owning computer.
|
||||||
|
* @return The found stack. This is empty if no valid stack is found.
|
||||||
|
*/
|
||||||
|
ItemStack getStack(ServerComputer computer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this holder is still valid for a particular computer.
|
* Determine if this holder is still valid for a particular computer.
|
||||||
*
|
*
|
||||||
* @param computer The current computer.
|
* @param computer The current computer.
|
||||||
* @return Whether this holder is valid.
|
* @return Whether this holder is valid.
|
||||||
*/
|
*/
|
||||||
boolean isValid(ServerComputer computer);
|
default boolean isValid(ServerComputer computer) {
|
||||||
|
return !getStack(computer).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the pocket computer item as having changed.
|
* Mark the pocket computer item as having changed.
|
||||||
@ -99,8 +110,11 @@ public sealed interface PocketHolder {
|
|||||||
*/
|
*/
|
||||||
record PlayerHolder(ServerPlayer entity, int slot) implements EntityHolder {
|
record PlayerHolder(ServerPlayer entity, int slot) implements EntityHolder {
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(ServerComputer computer) {
|
public ItemStack getStack(ServerComputer computer) {
|
||||||
return entity().isAlive() && PocketComputerItem.isServerComputer(computer, entity().getInventory().getItem(this.slot()));
|
if (!entity().isAlive()) return ItemStack.EMPTY;
|
||||||
|
|
||||||
|
var item = entity().getInventory().getItem(this.slot());
|
||||||
|
return PocketComputerItem.isServerComputer(computer, item) ? item : ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -117,8 +131,11 @@ public sealed interface PocketHolder {
|
|||||||
*/
|
*/
|
||||||
record LivingEntityHolder(LivingEntity entity, EquipmentSlot slot) implements EntityHolder {
|
record LivingEntityHolder(LivingEntity entity, EquipmentSlot slot) implements EntityHolder {
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(ServerComputer computer) {
|
public ItemStack getStack(ServerComputer computer) {
|
||||||
return entity().isAlive() && PocketComputerItem.isServerComputer(computer, entity().getItemBySlot(slot()));
|
if (!entity().isAlive()) return ItemStack.EMPTY;
|
||||||
|
|
||||||
|
var item = entity().getItemBySlot(this.slot());
|
||||||
|
return PocketComputerItem.isServerComputer(computer, item) ? item : ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -134,8 +151,11 @@ public sealed interface PocketHolder {
|
|||||||
*/
|
*/
|
||||||
record ItemEntityHolder(ItemEntity entity) implements EntityHolder {
|
record ItemEntityHolder(ItemEntity entity) implements EntityHolder {
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(ServerComputer computer) {
|
public ItemStack getStack(ServerComputer computer) {
|
||||||
return entity().isAlive() && PocketComputerItem.isServerComputer(computer, this.entity().getItem());
|
if (!entity().isAlive()) return ItemStack.EMPTY;
|
||||||
|
|
||||||
|
var item = entity().getItem();
|
||||||
|
return PocketComputerItem.isServerComputer(computer, item) ? item : ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -166,8 +186,11 @@ public sealed interface PocketHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(ServerComputer computer) {
|
public ItemStack getStack(ServerComputer computer) {
|
||||||
return !lectern().isRemoved() && PocketComputerItem.isServerComputer(computer, lectern.getItem());
|
if (lectern.isRemoved()) return ItemStack.EMPTY;
|
||||||
|
|
||||||
|
var item = lectern.getItem();
|
||||||
|
return PocketComputerItem.isServerComputer(computer, item) ? item : ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.pocket.core;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The side a {@linkplain IPocketUpgrade pocket upgrade} will be equipped on.
|
||||||
|
*
|
||||||
|
* @see PocketBrain
|
||||||
|
*/
|
||||||
|
public enum PocketSide {
|
||||||
|
BACK,
|
||||||
|
BOTTOM,
|
||||||
|
}
|
@ -5,11 +5,10 @@
|
|||||||
package dan200.computercraft.shared.pocket.items;
|
package dan200.computercraft.shared.pocket.items;
|
||||||
|
|
||||||
import dan200.computercraft.annotations.ForgeOverride;
|
import dan200.computercraft.annotations.ForgeOverride;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
|
||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
|
import dan200.computercraft.impl.UpgradeManager;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
@ -22,6 +21,7 @@ import dan200.computercraft.shared.platform.PlatformHelper;
|
|||||||
import dan200.computercraft.shared.pocket.core.PocketBrain;
|
import dan200.computercraft.shared.pocket.core.PocketBrain;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketHolder;
|
import dan200.computercraft.shared.pocket.core.PocketHolder;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketSide;
|
||||||
import dan200.computercraft.shared.util.*;
|
import dan200.computercraft.shared.util.*;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
@ -37,7 +37,6 @@ import net.minecraft.world.entity.item.ItemEntity;
|
|||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.component.DyedItemColor;
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
@ -69,9 +68,7 @@ public class PocketComputerItem extends Item {
|
|||||||
brain.computer().keepAlive();
|
brain.computer().keepAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update pocket upgrade
|
brain.tick();
|
||||||
var upgrade = brain.getUpgrade();
|
|
||||||
if (upgrade != null) upgrade.upgrade().update(brain, brain.computer().getPeripheral(ComputerSide.BACK));
|
|
||||||
|
|
||||||
if (updateItem(stack, brain)) holder.setChanged();
|
if (updateItem(stack, brain)) holder.setChanged();
|
||||||
}
|
}
|
||||||
@ -84,7 +81,7 @@ public class PocketComputerItem extends Item {
|
|||||||
* @return Whether the item was changed.
|
* @return Whether the item was changed.
|
||||||
*/
|
*/
|
||||||
private boolean updateItem(ItemStack stack, PocketBrain brain) {
|
private boolean updateItem(ItemStack stack, PocketBrain brain) {
|
||||||
var changed = brain.updateItem(stack);
|
var changed = false;
|
||||||
var computer = brain.computer();
|
var computer = brain.computer();
|
||||||
|
|
||||||
// Sync label
|
// Sync label
|
||||||
@ -134,14 +131,7 @@ public class PocketComputerItem extends Item {
|
|||||||
var computer = brain.computer();
|
var computer = brain.computer();
|
||||||
computer.turnOn();
|
computer.turnOn();
|
||||||
|
|
||||||
var stop = false;
|
var stop = brain.onRightClick((ServerLevel) world);
|
||||||
var upgrade = getUpgrade(stack);
|
|
||||||
if (upgrade != null) {
|
|
||||||
stop = upgrade.onRightClick(world, brain, computer.getPeripheral(ComputerSide.BACK));
|
|
||||||
// Sync back just in case. We don't need to setChanged, as we'll return the item anyway.
|
|
||||||
updateItem(stack, brain);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stop) openImpl(player, stack, holder, hand == InteractionHand.OFF_HAND, computer);
|
if (!stop) openImpl(player, stack, holder, hand == InteractionHand.OFF_HAND, computer);
|
||||||
}
|
}
|
||||||
return InteractionResult.SUCCESS;
|
return InteractionResult.SUCCESS;
|
||||||
@ -172,20 +162,13 @@ public class PocketComputerItem extends Item {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getName(ItemStack stack) {
|
public Component getName(ItemStack stack) {
|
||||||
var baseString = getDescriptionId();
|
return UpgradeManager.getName(getDescriptionId(), getUpgrade(stack, PocketSide.BACK), getUpgrade(stack, PocketSide.BOTTOM));
|
||||||
var upgrade = getUpgrade(stack);
|
|
||||||
if (upgrade != null) {
|
|
||||||
return Component.translatable(baseString + ".upgraded", upgrade.getAdjective());
|
|
||||||
} else {
|
|
||||||
return super.getName(stack);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ForgeOverride
|
@ForgeOverride
|
||||||
public String getCreatorModId(HolderLookup.Provider registries, ItemStack stack) {
|
public String getCreatorModId(HolderLookup.Provider registries, ItemStack stack) {
|
||||||
var upgrade = getUpgradeWithData(stack);
|
return PocketUpgrades.instance().getOwner(getUpgradeWithData(stack, PocketSide.BACK), getUpgradeWithData(stack, PocketSide.BOTTOM));
|
||||||
return upgrade != null ? PocketUpgrades.instance().getOwner(upgrade.holder()) : ComputerCraftAPI.MOD_ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PocketBrain getOrCreateBrain(ServerLevel level, PocketHolder holder, ItemStack stack) {
|
private PocketBrain getOrCreateBrain(ServerLevel level, PocketHolder holder, ItemStack stack) {
|
||||||
@ -200,12 +183,11 @@ public class PocketComputerItem extends Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var computerID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.Computer::new, IDAssigner.COMPUTER);
|
var computerID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.Computer::new, IDAssigner.COMPUTER);
|
||||||
var brain = new PocketBrain(
|
var brain = new PocketBrain(holder, ServerComputer.properties(computerID, getFamily())
|
||||||
holder, getUpgradeWithData(stack), DyedItemColor.getOrDefault(stack, -1),
|
.label(getLabel(stack))
|
||||||
ServerComputer.properties(computerID, getFamily())
|
.storageCapacity(StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), -1))
|
||||||
.label(getLabel(stack))
|
|
||||||
.storageCapacity(StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), -1))
|
|
||||||
);
|
);
|
||||||
|
brain.setUpgrades(getUpgradeWithData(stack, PocketSide.BACK), getUpgradeWithData(stack, PocketSide.BOTTOM));
|
||||||
var computer = brain.computer();
|
var computer = brain.computer();
|
||||||
|
|
||||||
stack.set(ModRegistry.DataComponents.COMPUTER.get(), new ServerComputerReference(registry.getSessionID(), computer.register()));
|
stack.set(ModRegistry.DataComponents.COMPUTER.get(), new ServerComputerReference(registry.getSessionID(), computer.register()));
|
||||||
@ -245,9 +227,7 @@ public class PocketComputerItem extends Item {
|
|||||||
var computer = getServerComputer(server, stack);
|
var computer = getServerComputer(server, stack);
|
||||||
if (computer == null) return;
|
if (computer == null) return;
|
||||||
|
|
||||||
var brain = computer.getBrain();
|
computer.getBrain().setUpgrades(getUpgradeWithData(stack, PocketSide.BACK), getUpgradeWithData(stack, PocketSide.BOTTOM));
|
||||||
brain.setUpgrade(getUpgradeWithData(stack));
|
|
||||||
brain.setColour(DyedItemColor.getOrDefault(stack, -1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComputerFamily getFamily() {
|
public ComputerFamily getFamily() {
|
||||||
@ -264,16 +244,15 @@ public class PocketComputerItem extends Item {
|
|||||||
return stack.getOrDefault(ModRegistry.DataComponents.ON.get(), false);
|
return stack.getOrDefault(ModRegistry.DataComponents.ON.get(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable IPocketUpgrade getUpgrade(ItemStack stack) {
|
public static @Nullable IPocketUpgrade getUpgrade(ItemStack stack, PocketSide side) {
|
||||||
var upgrade = getUpgradeWithData(stack);
|
var upgrade = getUpgradeWithData(stack, side);
|
||||||
return upgrade == null ? null : upgrade.upgrade();
|
return upgrade == null ? null : upgrade.upgrade();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable UpgradeData<IPocketUpgrade> getUpgradeWithData(ItemStack stack) {
|
public static @Nullable UpgradeData<IPocketUpgrade> getUpgradeWithData(ItemStack stack, PocketSide side) {
|
||||||
return stack.get(ModRegistry.DataComponents.POCKET_UPGRADE.get());
|
return stack.get(switch (side) {
|
||||||
}
|
case BACK -> ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get();
|
||||||
|
case BOTTOM -> ModRegistry.DataComponents.BOTTOM_POCKET_UPGRADE.get();
|
||||||
public static void setUpgrade(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
});
|
||||||
stack.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), upgrade);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,21 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
|||||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
public final class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||||
private final IPocketAccess access;
|
private final IPocketAccess access;
|
||||||
|
|
||||||
public PocketSpeakerPeripheral(IPocketAccess access) {
|
public PocketSpeakerPeripheral(IPocketAccess access) {
|
||||||
this.access = access;
|
this.access = access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerLevel getLevel() {
|
||||||
|
return access.getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpeakerPosition getPosition() {
|
public SpeakerPosition getPosition() {
|
||||||
var entity = access.getEntity();
|
var entity = access.getEntity();
|
||||||
|
@ -8,6 +8,7 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
|
|||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketSide;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@ -48,29 +49,37 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe {
|
|||||||
|
|
||||||
if (computer.isEmpty()) return ItemStack.EMPTY;
|
if (computer.isEmpty()) return ItemStack.EMPTY;
|
||||||
|
|
||||||
if (PocketComputerItem.getUpgradeWithData(computer) != null) return ItemStack.EMPTY;
|
|
||||||
|
|
||||||
// Check for upgrades around the item
|
// Check for upgrades around the item
|
||||||
UpgradeData<IPocketUpgrade> upgrade = null;
|
UpgradeData<IPocketUpgrade> above = null, below = null;
|
||||||
for (var y = 0; y < inventory.height(); y++) {
|
for (var y = 0; y < inventory.height(); y++) {
|
||||||
for (var x = 0; x < inventory.width(); x++) {
|
for (var x = 0; x < inventory.width(); x++) {
|
||||||
var item = inventory.getItem(x, y);
|
var item = inventory.getItem(x, y);
|
||||||
if (x == computerX && y == computerY) continue;
|
if (item.isEmpty() || (x == computerX && y == computerY)) continue;
|
||||||
|
|
||||||
if (x == computerX && y == computerY - 1) {
|
if (x == computerX && y == computerY - 1) {
|
||||||
upgrade = PocketUpgrades.instance().get(registryAccess, item);
|
above = PocketUpgrades.instance().get(registryAccess, item);
|
||||||
if (upgrade == null) return ItemStack.EMPTY;
|
if (above == null) return ItemStack.EMPTY;
|
||||||
} else if (!item.isEmpty()) {
|
} else if (x == computerX && y == computerY + 1) {
|
||||||
|
below = PocketUpgrades.instance().get(registryAccess, item);
|
||||||
|
if (below == null) return ItemStack.EMPTY;
|
||||||
|
} else {
|
||||||
return ItemStack.EMPTY;
|
return ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upgrade == null) return ItemStack.EMPTY;
|
// Abort if we have no upgrades
|
||||||
|
if (above == null && below == null) return ItemStack.EMPTY;
|
||||||
|
// Or if we've already got an upgrade in that slot.
|
||||||
|
if ((above != null && PocketComputerItem.getUpgrade(computer, PocketSide.BACK) != null)
|
||||||
|
|| (below != null && PocketComputerItem.getUpgrade(computer, PocketSide.BOTTOM) != null)) {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the new stack
|
// Construct the new stack
|
||||||
var result = computer.copyWithCount(1);
|
var result = computer.copyWithCount(1);
|
||||||
result.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), upgrade);
|
if (above != null) result.set(ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), above);
|
||||||
|
if (below != null) result.set(ModRegistry.DataComponents.BOTTOM_POCKET_UPGRADE.get(), below);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
package dan200.computercraft.shared.turtle.items;
|
package dan200.computercraft.shared.turtle.items;
|
||||||
|
|
||||||
import dan200.computercraft.annotations.ForgeOverride;
|
import dan200.computercraft.annotations.ForgeOverride;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.impl.TurtleUpgrades;
|
import dan200.computercraft.impl.TurtleUpgrades;
|
||||||
|
import dan200.computercraft.impl.UpgradeManager;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
||||||
@ -30,39 +30,13 @@ public class TurtleItem extends BlockItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getName(ItemStack stack) {
|
public Component getName(ItemStack stack) {
|
||||||
var baseString = descriptionId;
|
return UpgradeManager.getName(getDescriptionId(), getUpgrade(stack, TurtleSide.LEFT), getUpgrade(stack, TurtleSide.RIGHT));
|
||||||
var left = getUpgrade(stack, TurtleSide.LEFT);
|
|
||||||
var right = getUpgrade(stack, TurtleSide.RIGHT);
|
|
||||||
if (left != null && right != null) {
|
|
||||||
return Component.translatable(baseString + ".upgraded_twice", right.getAdjective(), left.getAdjective());
|
|
||||||
} else if (left != null) {
|
|
||||||
return Component.translatable(baseString + ".upgraded", left.getAdjective());
|
|
||||||
} else if (right != null) {
|
|
||||||
return Component.translatable(baseString + ".upgraded", right.getAdjective());
|
|
||||||
} else {
|
|
||||||
return Component.translatable(baseString);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ForgeOverride
|
@ForgeOverride
|
||||||
public String getCreatorModId(HolderLookup.Provider registries, ItemStack stack) {
|
public String getCreatorModId(HolderLookup.Provider registries, ItemStack stack) {
|
||||||
// Determine our "creator mod" from the upgrades. We attempt to find the first non-vanilla/non-CC
|
return TurtleUpgrades.instance().getOwner(getUpgradeWithData(stack, TurtleSide.LEFT), getUpgradeWithData(stack, TurtleSide.RIGHT));
|
||||||
// upgrade (starting from the left).
|
|
||||||
|
|
||||||
var left = getUpgradeWithData(stack, TurtleSide.LEFT);
|
|
||||||
if (left != null) {
|
|
||||||
var mod = TurtleUpgrades.instance().getOwner(left.holder());
|
|
||||||
if (!mod.equals(ComputerCraftAPI.MOD_ID)) return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
var right = getUpgradeWithData(stack, TurtleSide.RIGHT);
|
|
||||||
if (right != null) {
|
|
||||||
var mod = TurtleUpgrades.instance().getOwner(right.holder());
|
|
||||||
if (!mod.equals(ComputerCraftAPI.MOD_ID)) return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComputerCraftAPI.MOD_ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side) {
|
public static @Nullable ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side) {
|
||||||
@ -71,7 +45,10 @@ public class TurtleItem extends BlockItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(ItemStack stack, TurtleSide side) {
|
public static @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(ItemStack stack, TurtleSide side) {
|
||||||
return stack.get(side == TurtleSide.LEFT ? ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get() : ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get());
|
return stack.get(switch (side) {
|
||||||
|
case LEFT -> ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get();
|
||||||
|
case RIGHT -> ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable TurtleOverlay getOverlay(ItemStack stack) {
|
public static @Nullable TurtleOverlay getOverlay(ItemStack stack) {
|
||||||
|
@ -13,18 +13,24 @@ import dan200.computercraft.api.upgrades.UpgradeType;
|
|||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
public class TurtleSpeaker extends AbstractTurtleUpgrade {
|
public class TurtleSpeaker extends AbstractTurtleUpgrade {
|
||||||
private static class Peripheral extends UpgradeSpeakerPeripheral {
|
private static final class Peripheral extends UpgradeSpeakerPeripheral {
|
||||||
final ITurtleAccess turtle;
|
final ITurtleAccess turtle;
|
||||||
|
|
||||||
Peripheral(ITurtleAccess turtle) {
|
Peripheral(ITurtleAccess turtle) {
|
||||||
this.turtle = turtle;
|
this.turtle = turtle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerLevel getLevel() {
|
||||||
|
return (ServerLevel) turtle.getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpeakerPosition getPosition() {
|
public SpeakerPosition getPosition() {
|
||||||
return SpeakerPosition.of(turtle.getLevel(), Vec3.atCenterOf(turtle.getPosition()));
|
return SpeakerPosition.of(turtle.getLevel(), Vec3.atCenterOf(turtle.getPosition()));
|
||||||
|
@ -65,6 +65,12 @@ public abstract class NonNegativeId implements TooltipProvider {
|
|||||||
out.accept(Component.translatable(translation, id()).withStyle(ChatFormatting.GRAY));
|
out.accept(Component.translatable(translation, id()).withStyle(ChatFormatting.GRAY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
var className = getClass().getName();
|
||||||
|
return className.substring(className.lastIndexOf('.') + 1) + "(" + id + ")";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("EqualsGetClass") // We want to distinguish different subclasses.
|
@SuppressWarnings("EqualsGetClass") // We want to distinguish different subclasses.
|
||||||
public final boolean equals(Object o) {
|
public final boolean equals(Object o) {
|
||||||
|
@ -11,13 +11,14 @@ import dan200.computercraft.api.upgrades.UpgradeData
|
|||||||
import dan200.computercraft.client.pocket.ClientPocketComputers
|
import dan200.computercraft.client.pocket.ClientPocketComputers
|
||||||
import dan200.computercraft.core.apis.TermAPI
|
import dan200.computercraft.core.apis.TermAPI
|
||||||
import dan200.computercraft.gametest.api.*
|
import dan200.computercraft.gametest.api.*
|
||||||
import dan200.computercraft.gametest.api.GameTest
|
import dan200.computercraft.impl.PocketUpgrades
|
||||||
import dan200.computercraft.mixin.gametest.GameTestHelperAccessor
|
import dan200.computercraft.mixin.gametest.GameTestHelperAccessor
|
||||||
import dan200.computercraft.shared.ModRegistry
|
import dan200.computercraft.shared.ModRegistry
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState
|
import dan200.computercraft.shared.computer.core.ComputerState
|
||||||
import dan200.computercraft.shared.util.DataComponentUtil
|
import dan200.computercraft.shared.util.DataComponentUtil
|
||||||
import dan200.computercraft.shared.util.NonNegativeId
|
import dan200.computercraft.shared.util.NonNegativeId
|
||||||
import dan200.computercraft.test.core.computer.getApi
|
import dan200.computercraft.test.core.computer.getApi
|
||||||
|
import dan200.computercraft.test.shared.ItemStackMatcher.isStack
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.component.DataComponentPatch
|
import net.minecraft.core.component.DataComponentPatch
|
||||||
import net.minecraft.core.component.DataComponents
|
import net.minecraft.core.component.DataComponents
|
||||||
@ -26,6 +27,8 @@ import net.minecraft.gametest.framework.GameTestSequence
|
|||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.item.Items
|
||||||
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -130,7 +133,7 @@ class Pocket_Computer_Test {
|
|||||||
it.applyComponents(
|
it.applyComponents(
|
||||||
DataComponentPatch.builder()
|
DataComponentPatch.builder()
|
||||||
.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.Computer(123))
|
.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.Computer(123))
|
||||||
.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade))
|
.set(ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade))
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -138,4 +141,86 @@ class Pocket_Computer_Test {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that turtles can be crafted with upgrades.
|
||||||
|
*/
|
||||||
|
@GameTest(template = Structures.DEFAULT)
|
||||||
|
fun Can_upgrades_be_crafted(helper: GameTestHelper) = helper.immediate {
|
||||||
|
fun pocket(back: UpgradeData<IPocketUpgrade>? = null, bottom: UpgradeData<IPocketUpgrade>? = null): ItemStack {
|
||||||
|
val item = ItemStack(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
|
||||||
|
item.set(ModRegistry.DataComponents.BACK_POCKET_UPGRADE.get(), back)
|
||||||
|
item.set(ModRegistry.DataComponents.BOTTOM_POCKET_UPGRADE.get(), bottom)
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
val registries = helper.level.registryAccess()
|
||||||
|
val speaker = PocketUpgrades.instance().get(registries, ItemStack(ModRegistry.Items.SPEAKER.get()))!!
|
||||||
|
val modem =
|
||||||
|
PocketUpgrades.instance().get(registries, ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()))!!
|
||||||
|
|
||||||
|
// Check we can craft with upgrades
|
||||||
|
assertThat(
|
||||||
|
"Craft with item below",
|
||||||
|
helper.craftItem(
|
||||||
|
ItemStack.EMPTY, pocket(), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack(ModRegistry.Items.SPEAKER.get()), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
|
||||||
|
),
|
||||||
|
isStack(pocket(bottom = speaker)),
|
||||||
|
)
|
||||||
|
assertThat(
|
||||||
|
"Craft with item above",
|
||||||
|
helper.craftItem(
|
||||||
|
ItemStack.EMPTY, ItemStack(ModRegistry.Items.SPEAKER.get()), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, pocket(), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
|
||||||
|
),
|
||||||
|
isStack(pocket(back = speaker)),
|
||||||
|
)
|
||||||
|
assertThat(
|
||||||
|
"Craft with two items",
|
||||||
|
helper.craftItem(
|
||||||
|
ItemStack.EMPTY, ItemStack(ModRegistry.Items.SPEAKER.get()), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, pocket(), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()), ItemStack.EMPTY,
|
||||||
|
),
|
||||||
|
isStack(pocket(back = speaker, bottom = modem)),
|
||||||
|
)
|
||||||
|
assertThat(
|
||||||
|
"Maintains upgrades",
|
||||||
|
helper.craftItem(
|
||||||
|
ItemStack.EMPTY, pocket(back = speaker), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
|
||||||
|
),
|
||||||
|
isStack(pocket(back = speaker, bottom = modem)),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cannot craft when already have item
|
||||||
|
helper.assertNotCraftable(
|
||||||
|
ItemStack.EMPTY, ItemStack(ModRegistry.Items.SPEAKER.get()), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, pocket(back = modem), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cannot craft with an invalid upgrade
|
||||||
|
helper.assertNotCraftable(
|
||||||
|
ItemStack.EMPTY, pocket(), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack(Items.DIRT), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cannot craft with extra items in the inventory
|
||||||
|
helper.assertNotCraftable(
|
||||||
|
ItemStack(Items.DIRT), ItemStack(ModRegistry.Items.SPEAKER.get()), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, pocket(), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
|
||||||
|
)
|
||||||
|
helper.assertNotCraftable(
|
||||||
|
ItemStack.EMPTY, ItemStack(ModRegistry.Items.SPEAKER.get()), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, pocket(), ItemStack.EMPTY,
|
||||||
|
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack(Items.DIRT),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,27 @@ if not pocket then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = pocket.equipBack()
|
if select('#', ...) > 1 then
|
||||||
if not ok then
|
local programName = arg[0] or fs.getName(shell.getRunningProgram())
|
||||||
printError(err)
|
print("Usage: " .. programName .. " <side>")
|
||||||
else
|
return
|
||||||
print("Item equipped")
|
end
|
||||||
|
|
||||||
|
local function equip(fn)
|
||||||
|
local ok, err = fn()
|
||||||
|
if not ok then
|
||||||
|
printError(err)
|
||||||
|
else
|
||||||
|
print("Item equipped")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local side = ... or "back"
|
||||||
|
if side == "back" then
|
||||||
|
equip(pocket.equipBack)
|
||||||
|
elseif side == "bottom" then
|
||||||
|
equip(pocket.equipBottom)
|
||||||
|
else
|
||||||
|
printError("Unknown side. Expected 'back' or 'bottom'.")
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
@ -7,9 +7,27 @@ if not pocket then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = pocket.unequipBack()
|
if select('#', ...) > 1 then
|
||||||
if not ok then
|
local programName = arg[0] or fs.getName(shell.getRunningProgram())
|
||||||
printError(err)
|
print("Usage: " .. programName .. " <side>")
|
||||||
else
|
return
|
||||||
print("Item unequipped")
|
end
|
||||||
|
|
||||||
|
local function unequip(fn)
|
||||||
|
local ok, err = fn()
|
||||||
|
if not ok then
|
||||||
|
printError(err)
|
||||||
|
else
|
||||||
|
print("Item unequipped")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local side = ... or "back"
|
||||||
|
if side == "back" then
|
||||||
|
unequip(pocket.unequipBack)
|
||||||
|
elseif side == "bottom" then
|
||||||
|
unequip(pocket.unequipBottom)
|
||||||
|
else
|
||||||
|
printError("Unknown side. Expected 'back' or 'bottom'.")
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared.integration.rei;
|
|||||||
|
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketSide;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
import me.shedaniel.rei.api.common.entry.comparison.ItemComparatorRegistry;
|
import me.shedaniel.rei.api.common.entry.comparison.ItemComparatorRegistry;
|
||||||
@ -30,8 +31,14 @@ public class REIComputerCraft implements REICommonPlugin {
|
|||||||
}, ModRegistry.Items.TURTLE_NORMAL.get(), ModRegistry.Items.TURTLE_ADVANCED.get());
|
}, ModRegistry.Items.TURTLE_NORMAL.get(), ModRegistry.Items.TURTLE_ADVANCED.get());
|
||||||
|
|
||||||
registry.register((context, stack) -> {
|
registry.register((context, stack) -> {
|
||||||
var upgrade = PocketComputerItem.getUpgradeWithData(stack);
|
long hash = 1;
|
||||||
return upgrade == null ? 1 : upgrade.holder().key().location().hashCode();
|
|
||||||
|
var back = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BACK);
|
||||||
|
var bottom = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BOTTOM);
|
||||||
|
if (back != null) hash = hash * 31 + back.holder().key().location().hashCode();
|
||||||
|
if (bottom != null) hash = hash * 31 + bottom.holder().key().location().hashCode();
|
||||||
|
|
||||||
|
return hash;
|
||||||
}, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
|
}, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
|
||||||
|
|
||||||
registry.register((context, stack) -> DyedItemColor.getOrDefault(stack, -1), ModRegistry.Items.DISK.get());
|
registry.register((context, stack) -> DyedItemColor.getOrDefault(stack, -1), ModRegistry.Items.DISK.get());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user