diff --git a/src/generated/resources/assets/cosmiccore/blockstates/soul_tester.json b/src/generated/resources/assets/cosmiccore/blockstates/soul_tester.json new file mode 100644 index 000000000..48ee0184a --- /dev/null +++ b/src/generated/resources/assets/cosmiccore/blockstates/soul_tester.json @@ -0,0 +1,76 @@ +{ + "variants": { + "facing=east,upwards_facing=east": { + "gtceu:z": 270, + "model": "cosmiccore:block/machine/soul_tester", + "y": 90 + }, + "facing=east,upwards_facing=north": { + "model": "cosmiccore:block/machine/soul_tester", + "y": 90 + }, + "facing=east,upwards_facing=south": { + "gtceu:z": 180, + "model": "cosmiccore:block/machine/soul_tester", + "y": 90 + }, + "facing=east,upwards_facing=west": { + "gtceu:z": 90, + "model": "cosmiccore:block/machine/soul_tester", + "y": 90 + }, + "facing=north,upwards_facing=east": { + "gtceu:z": 270, + "model": "cosmiccore:block/machine/soul_tester" + }, + "facing=north,upwards_facing=north": { + "model": "cosmiccore:block/machine/soul_tester" + }, + "facing=north,upwards_facing=south": { + "gtceu:z": 180, + "model": "cosmiccore:block/machine/soul_tester" + }, + "facing=north,upwards_facing=west": { + "gtceu:z": 90, + "model": "cosmiccore:block/machine/soul_tester" + }, + "facing=south,upwards_facing=east": { + "gtceu:z": 270, + "model": "cosmiccore:block/machine/soul_tester", + "y": 180 + }, + "facing=south,upwards_facing=north": { + "model": "cosmiccore:block/machine/soul_tester", + "y": 180 + }, + "facing=south,upwards_facing=south": { + "gtceu:z": 180, + "model": "cosmiccore:block/machine/soul_tester", + "y": 180 + }, + "facing=south,upwards_facing=west": { + "gtceu:z": 90, + "model": "cosmiccore:block/machine/soul_tester", + "y": 180 + }, + "facing=west,upwards_facing=east": { + "gtceu:z": 270, + "model": "cosmiccore:block/machine/soul_tester", + "y": 270 + }, + "facing=west,upwards_facing=north": { + "model": "cosmiccore:block/machine/soul_tester", + "y": 270 + }, + "facing=west,upwards_facing=south": { + "gtceu:z": 180, + "model": "cosmiccore:block/machine/soul_tester", + "y": 270 + }, + "facing=west,upwards_facing=west": { + "gtceu:z": 90, + "model": "cosmiccore:block/machine/soul_tester", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/cosmiccore/blockstates/star_ladder.json b/src/generated/resources/assets/cosmiccore/blockstates/star_ladder.json index ad9a7da96..e1e4793e5 100644 --- a/src/generated/resources/assets/cosmiccore/blockstates/star_ladder.json +++ b/src/generated/resources/assets/cosmiccore/blockstates/star_ladder.json @@ -1,76 +1,7 @@ { "variants": { - "facing=east,upwards_facing=east": { - "gtceu:z": 270, - "model": "cosmiccore:block/machine/star_ladder", - "y": 90 - }, - "facing=east,upwards_facing=north": { - "model": "cosmiccore:block/machine/star_ladder", - "y": 90 - }, - "facing=east,upwards_facing=south": { - "gtceu:z": 180, - "model": "cosmiccore:block/machine/star_ladder", - "y": 90 - }, - "facing=east,upwards_facing=west": { - "gtceu:z": 90, - "model": "cosmiccore:block/machine/star_ladder", - "y": 90 - }, - "facing=north,upwards_facing=east": { - "gtceu:z": 270, + "": { "model": "cosmiccore:block/machine/star_ladder" - }, - "facing=north,upwards_facing=north": { - "model": "cosmiccore:block/machine/star_ladder" - }, - "facing=north,upwards_facing=south": { - "gtceu:z": 180, - "model": "cosmiccore:block/machine/star_ladder" - }, - "facing=north,upwards_facing=west": { - "gtceu:z": 90, - "model": "cosmiccore:block/machine/star_ladder" - }, - "facing=south,upwards_facing=east": { - "gtceu:z": 270, - "model": "cosmiccore:block/machine/star_ladder", - "y": 180 - }, - "facing=south,upwards_facing=north": { - "model": "cosmiccore:block/machine/star_ladder", - "y": 180 - }, - "facing=south,upwards_facing=south": { - "gtceu:z": 180, - "model": "cosmiccore:block/machine/star_ladder", - "y": 180 - }, - "facing=south,upwards_facing=west": { - "gtceu:z": 90, - "model": "cosmiccore:block/machine/star_ladder", - "y": 180 - }, - "facing=west,upwards_facing=east": { - "gtceu:z": 270, - "model": "cosmiccore:block/machine/star_ladder", - "y": 270 - }, - "facing=west,upwards_facing=north": { - "model": "cosmiccore:block/machine/star_ladder", - "y": 270 - }, - "facing=west,upwards_facing=south": { - "gtceu:z": 180, - "model": "cosmiccore:block/machine/star_ladder", - "y": 270 - }, - "facing=west,upwards_facing=west": { - "gtceu:z": 90, - "model": "cosmiccore:block/machine/star_ladder", - "y": 270 } } } \ No newline at end of file diff --git a/src/generated/resources/assets/cosmiccore/lang/en_ud.json b/src/generated/resources/assets/cosmiccore/lang/en_ud.json index a875b8e7c..c150b0f21 100644 --- a/src/generated/resources/assets/cosmiccore/lang/en_ud.json +++ b/src/generated/resources/assets/cosmiccore/lang/en_ud.json @@ -280,6 +280,7 @@ "block.cosmiccore.somarust_casing": "buısɐƆ ʇsnɹɐɯoS", "block.cosmiccore.soul_muted_casing": "buısɐƆ pǝʇnW ןnoS", "block.cosmiccore.soul_stained_steel_aluminium_plated_casing": "buısɐƆ pǝʇɐןԀ ɯnıuıɯnןⱯ ןǝǝʇS pǝuıɐʇS ןnoS", + "block.cosmiccore.soul_tester": "ɹǝʇsǝ⟘ ןnoS", "block.cosmiccore.spirit_crucible": "ǝןqıɔnɹƆ ʇıɹıdS", "block.cosmiccore.star_ladder": "ɹǝppɐꞀ ɹɐʇS", "block.cosmiccore.star_ladder_research_hub": "qnH ɥɔɹɐǝsǝᴚ ɹǝppɐꞀ ɹɐʇS", @@ -1383,6 +1384,7 @@ "item.cosmiccore.simple_rebreather.tooltip": "˙sʇuǝɯuoɹıʌuǝ ㄥ§ɹıⱯ uıɥ⟘q§ uı uıɐɹp uǝbʎxo sǝɔnpǝᴚㄥ§", "item.cosmiccore.somatic_processing_assembly": "pɹɐoᗺ ʎןqɯǝssⱯ buıssǝɔoɹdoʇɐɯoS", "item.cosmiccore.soul_cut_lucid_cpu_chip": "dıɥƆ ∩ԀƆ pıɔnꞀ ʇnƆ ןnoS", + "item.cosmiccore.soul_reader": "ɹǝpɐǝᴚ ʞɹoʍʇǝN ןnoS", "item.cosmiccore.sov_blood_orb": "qɹO pooןᗺ ubıǝɹǝʌoS", "item.cosmiccore.space_advanced_nanomuscle_chestplate": "ǝʇɐןdʇsǝɥƆ ǝʇınS ǝɔɐdS ™ǝןɔsnWouɐN pǝɔuɐʌpⱯ", "item.cosmiccore.space_advanced_quarktech_chestplate": "ǝʇɐןdʇsǝɥƆ ǝʇınS ǝɔɐdS ™ɥɔǝ⟘ʞɹɐnὉ pǝɔuɐʌpⱯ", diff --git a/src/generated/resources/assets/cosmiccore/lang/en_us.json b/src/generated/resources/assets/cosmiccore/lang/en_us.json index 6011dc292..67057a1a4 100644 --- a/src/generated/resources/assets/cosmiccore/lang/en_us.json +++ b/src/generated/resources/assets/cosmiccore/lang/en_us.json @@ -280,6 +280,7 @@ "block.cosmiccore.somarust_casing": "Somarust Casing", "block.cosmiccore.soul_muted_casing": "Soul Muted Casing", "block.cosmiccore.soul_stained_steel_aluminium_plated_casing": "Soul Stained Steel Aluminium Plated Casing", + "block.cosmiccore.soul_tester": "Soul Tester", "block.cosmiccore.spirit_crucible": "Spirit Crucible", "block.cosmiccore.star_ladder": "Star Ladder", "block.cosmiccore.star_ladder_research_hub": "Star Ladder Research Hub", @@ -1382,6 +1383,7 @@ "item.cosmiccore.simple_rebreather": "Simple Rebreather", "item.cosmiccore.simple_rebreather.tooltip": "§7Reduces oxygen drain in §bThin Air§7 environments.", "item.cosmiccore.somatic_processing_assembly": "Somatoprocessing Assembly Board", + "item.cosmiccore.soul_reader": "Soul Network Reader", "item.cosmiccore.soul_cut_lucid_cpu_chip": "Soul Cut Lucid CPU Chip", "item.cosmiccore.sov_blood_orb": "Sovereign Blood Orb", "item.cosmiccore.space_advanced_nanomuscle_chestplate": "Advanced NanoMuscle™ Space Suite Chestplate", @@ -1993,5 +1995,42 @@ "tooltip.cosmiccore.thermia_hatch_limit": "§cTemp. Limit: %sK", "tooltip.gt_scythe.energy": "Energy: %s / %s EU", "tooltip.gt_scythe.no_energy": "§cNot enough energy.", - "tooltip.gt_scythe.per_hit": "Cost: %s EU / hit" + "tooltip.gt_scythe.per_hit": "Cost: %s EU / hit", + "recipe.cosmiccore.raw_soul_in": "Consumes: %s Raw souls", + "recipe.cosmiccore.refined_soul_in": "Consumes: %s Refined souls", + "recipe.cosmiccore.proud_soul_in": "Consumes: %s Proud souls", + "recipe.cosmiccore.greedy_soul_in": "Consumes: %s Greedy souls", + "recipe.cosmiccore.lustful_soul_in": "Consumes: %s Lustful souls", + "recipe.cosmiccore.envious_soul_in": "Consumes: %s Envious souls", + "recipe.cosmiccore.gluttonous_soul_in": "Consumes: %s Gluttonous souls", + "recipe.cosmiccore.wrathful_soul_in": "Consumes: %s Wrathful souls", + "recipe.cosmiccore.slothful_soul_in": "Consumes: %s Slothful souls", + "recipe.cosmiccore.temporal_soul_in": "Consumes: %s Temporal souls", + "recipe.cosmiccore.raw_soul_out": "Produces: %s Raw souls", + "recipe.cosmiccore.refined_soul_out": "Produces: %s Refined souls", + "recipe.cosmiccore.proud_soul_out": "Produces: %s Proud souls", + "recipe.cosmiccore.greedy_soul_out": "Produces: %s Greedy souls", + "recipe.cosmiccore.lustful_soul_out": "Produces: %s Lustful souls", + "recipe.cosmiccore.envious_soul_out": "Produces: %s Envious souls", + "recipe.cosmiccore.gluttonous_soul_out": "Produces: %s Gluttonous souls", + "recipe.cosmiccore.wrathful_soul_out": "Produces: %s Wrathful souls", + "recipe.cosmiccore.slothful_soul_out": "Produces: %s Slothful souls", + "recipe.cosmiccore.temporal_soul_out": "Produces: %s Temporal souls", + "gui.cosmiccore.soul.raw.name": "Raw souls", + "gui.cosmiccore.soul.refined.name": "Refined souls", + "gui.cosmiccore.soul.proud.name": "Proud souls", + "gui.cosmiccore.soul.greedy.name": "Greedy souls", + "gui.cosmiccore.soul.lustful.name": "Lustful souls", + "gui.cosmiccore.soul.envious.name": "Envious souls", + "gui.cosmiccore.soul.gluttonous.name": "Gluttonous souls", + "gui.cosmiccore.soul.wrathful.name": "Wrathful souls", + "gui.cosmiccore.soul.slothful.name": "Slothful souls", + "gui.cosmiccore.soul.temporal.name": "Temporal souls", + "gui.cosmiccore.soul.empty_network": "Network is empty", + "gui.cosmiccore.soul.network_contents": "--- Network Contents ---", + "gui.cosmiccore.soul.reset": "Soul network has been reset", + "gui.cosmiccore.soul.add": "Added %s %s souls to the network", + "gui.cosmiccore.soul.remove": "removed %s %s souls to the network", + "gui.cosmiccore.soul.capacity": "Capacity: %s souls", + "gui.cosmiccore.soul.set_tier": "the network is now tier %s" } \ No newline at end of file diff --git a/src/generated/resources/assets/cosmiccore/models/block/machine/soul_tester.json b/src/generated/resources/assets/cosmiccore/models/block/machine/soul_tester.json new file mode 100644 index 000000000..af218192d --- /dev/null +++ b/src/generated/resources/assets/cosmiccore/models/block/machine/soul_tester.json @@ -0,0 +1,90 @@ +{ + "parent": "minecraft:block/block", + "loader": "gtceu:machine", + "machine": "cosmiccore:soul_tester", + "texture_overrides": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe" + }, + "variants": { + "is_formed=false,recipe_logic_status=idle": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_emissive" + } + } + }, + "is_formed=false,recipe_logic_status=suspend": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_emissive" + } + } + }, + "is_formed=false,recipe_logic_status=waiting": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_active_emissive" + } + } + }, + "is_formed=false,recipe_logic_status=working": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_active_emissive" + } + } + }, + "is_formed=true,recipe_logic_status=idle": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_emissive" + } + } + }, + "is_formed=true,recipe_logic_status=suspend": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_emissive" + } + } + }, + "is_formed=true,recipe_logic_status=waiting": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_active_emissive" + } + } + }, + "is_formed=true,recipe_logic_status=working": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "overlay_front": "gtceu:block/multiblock/coke_oven/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/coke_oven/overlay_front_active_emissive" + } + } + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/cosmiccore/models/item/soul_reader.json b/src/generated/resources/assets/cosmiccore/models/item/soul_reader.json new file mode 100644 index 000000000..28c03ad37 --- /dev/null +++ b/src/generated/resources/assets/cosmiccore/models/item/soul_reader.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "cosmiccore:item/soul_reader" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/cosmiccore/models/item/soul_tester.json b/src/generated/resources/assets/cosmiccore/models/item/soul_tester.json new file mode 100644 index 000000000..29eddc535 --- /dev/null +++ b/src/generated/resources/assets/cosmiccore/models/item/soul_tester.json @@ -0,0 +1,3 @@ +{ + "parent": "cosmiccore:block/machine/soul_tester" +} \ No newline at end of file diff --git a/src/main/java/com/ghostipedia/cosmiccore/CosmicCore.java b/src/main/java/com/ghostipedia/cosmiccore/CosmicCore.java index 1f80f134a..43dc7782f 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/CosmicCore.java +++ b/src/main/java/com/ghostipedia/cosmiccore/CosmicCore.java @@ -3,12 +3,14 @@ import com.ghostipedia.cosmiccore.api.capability.CosmicCapabilities; import com.ghostipedia.cosmiccore.api.item.LinkedTerminalBehavior; import com.ghostipedia.cosmiccore.api.pattern.CosmicPredicates; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulIngredient; import com.ghostipedia.cosmiccore.api.recipe.lookup.MapEmberIngredient; import com.ghostipedia.cosmiccore.api.recipe.lookup.MapSoulIngredient; import com.ghostipedia.cosmiccore.api.registries.CosmicRegistration; import com.ghostipedia.cosmiccore.client.CosmicCoreClient; import com.ghostipedia.cosmiccore.common.airControl.OxygenItemCap; import com.ghostipedia.cosmiccore.common.airControl.OxygenRules; +import com.ghostipedia.cosmiccore.common.commands.argument.SoulTypeArgument; import com.ghostipedia.cosmiccore.common.data.*; import com.ghostipedia.cosmiccore.common.data.materials.CosmicMaterialSet; import com.ghostipedia.cosmiccore.common.data.materials.CosmicMaterials; @@ -36,6 +38,8 @@ import com.lowdragmc.lowdraglib.Platform; +import net.minecraft.commands.synchronization.ArgumentTypeInfos; +import net.minecraft.commands.synchronization.SingletonArgumentInfo; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -111,12 +115,14 @@ public void modifyExistingMaterials(PostMaterialEvent event) { @SubscribeEvent public void commonSetup(FMLCommonSetupEvent event) { event.enqueueWork(() -> { - MapIngredientTypeManager.registerMapIngredient(Integer.class, MapSoulIngredient::convertToMapIngredient); + MapIngredientTypeManager.registerMapIngredient(SoulIngredient.class, MapSoulIngredient::from); MapIngredientTypeManager.registerMapIngredient(Double.class, MapEmberIngredient::convertToMapIngredient); GridLinkables.register(CosmicItems.LINKED_TERMINAL, LinkedTerminalBehavior.handler); CCoreNetwork.init(); OxygenRules.registerAirRanges(); DimensionMobScaling.registerScaling(); + ArgumentTypeInfos.registerByClass(SoulTypeArgument.class, + SingletonArgumentInfo.contextFree(SoulTypeArgument::soulType)); }); } diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/capability/ISoulContainer.java b/src/main/java/com/ghostipedia/cosmiccore/api/capability/ISoulContainer.java index 472f01c68..5c058b663 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/api/capability/ISoulContainer.java +++ b/src/main/java/com/ghostipedia/cosmiccore/api/capability/ISoulContainer.java @@ -1,21 +1,9 @@ package com.ghostipedia.cosmiccore.api.capability; -import wayoftime.bloodmagic.core.data.SoulNetwork; - -import java.util.UUID; +import com.ghostipedia.cosmiccore.api.data.souls.SoulNetwork; public interface ISoulContainer { - /** - * @return the UUID of the player associated to the container's network - */ - UUID getOwner(); - - /** - * @param playerUUID: the player to whom we attach the container - */ - void setOwner(UUID playerUUID); - /** * @return the soul network attached to the container */ diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/capability/recipe/SoulRecipeCapability.java b/src/main/java/com/ghostipedia/cosmiccore/api/capability/recipe/SoulRecipeCapability.java index 076f0b279..47b3534e7 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/api/capability/recipe/SoulRecipeCapability.java +++ b/src/main/java/com/ghostipedia/cosmiccore/api/capability/recipe/SoulRecipeCapability.java @@ -1,71 +1,173 @@ package com.ghostipedia.cosmiccore.api.capability.recipe; -import com.ghostipedia.cosmiccore.api.recipe.lookup.MapSoulIngredient; +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; +import com.ghostipedia.cosmiccore.api.machine.trait.NotifiableSoulContainer; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulIngredient; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulStack; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.IRecipeCapabilityHolder; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.content.Content; import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; -import com.gregtechceu.gtceu.api.recipe.content.SerializerInteger; -import com.gregtechceu.gtceu.api.recipe.lookup.ingredient.AbstractMapIngredient; +import com.gregtechceu.gtceu.api.recipe.content.IContentSerializer; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.utils.LocalizationUtils; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import com.mojang.serialization.Codec; import org.apache.commons.lang3.mutable.MutableInt; -import org.jetbrains.annotations.Nullable; -import java.util.Collection; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; -public class SoulRecipeCapability extends RecipeCapability { +public class SoulRecipeCapability extends RecipeCapability { public final static SoulRecipeCapability CAP = new SoulRecipeCapability(); protected SoulRecipeCapability() { - super("soul", 0x5E2129FF, true, 10, SerializerInteger.INSTANCE); + super("soul", 0x5E2129FF, true, 10, SerializerSoulIngredient.INSTANCE); } @Override - public Integer copyInner(Integer content) { - return content; + public boolean isRecipeSearchFilter() { + return true; } + // TODO: try to remove @Override - public Integer copyWithModifier(Integer content, ContentModifier modifier) { - return modifier.apply(content); + public SoulIngredient copyInner(SoulIngredient content) { + return super.copyInner(content); } @Override - public @Nullable List getDefaultMapIngredient(Object ingredient) { - List ingredients = new ObjectArrayList<>(1); - if (ingredient instanceof Integer essence) ingredients.add(new MapSoulIngredient(essence)); - return ingredients; + public SoulIngredient copyWithModifier(SoulIngredient content, ContentModifier modifier) { + var modifiedStack = content.stack().withAmount(modifier.apply(content.stack().amount())); + return SoulIngredient.of(modifiedStack); } @Override public List compressIngredients(Collection ingredients) { - // TODO: Figure out what it needs to do - return super.compressIngredients(ingredients); + List list = new ArrayList<>(ingredients.size()); + for (Object item : ingredients) { + if (item instanceof SoulIngredient soul) { + var isEqual = false; + for (Object obj : list) { + if (obj instanceof SoulIngredient soulIngredient && + soul.stack().type().equals(soulIngredient.stack().type())) { + isEqual = true; + break; + } + } + if (isEqual) continue; + list.add(item); + } + } + return list; + } + + /// Get the total available input of each soul type. + /// The value is the sum of the available amount in each hatch. + /// The available amount is the minimum between the contained souls and the available throughput. + private static Map getInputContents(IRecipeCapabilityHolder holder) { + var handlerLists = holder.getCapabilitiesForIO(IO.IN); + if (handlerLists.isEmpty()) return new HashMap<>(); + + var totalThroughput = 0; + var totalSouls = new HashMap(); + + for (var handlerList : handlerLists) { + if (!handlerList.hasCapability(SoulRecipeCapability.CAP)) continue; + var soulHandlers = handlerList.getCapability(SoulRecipeCapability.CAP); + for (var handler : soulHandlers) { + var soulHandler = (NotifiableSoulContainer) handler; + totalThroughput += soulHandler.getThroughput(); + for (var content : soulHandler.getContents()) { + if (content instanceof SoulIngredient soulIngredient) { + totalSouls.put(soulIngredient.stack().type(), soulIngredient.stack().amount()); + } else throw new IllegalArgumentException("Invalid content type"); + } + } + } + + final int finalTotalThroughput = totalThroughput; + return totalSouls.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> Math.min(entry.getValue(), finalTotalThroughput))); } @Override - public boolean isRecipeSearchFilter() { - return true; + public int getMaxParallelByInput(IRecipeCapabilityHolder holder, GTRecipe recipe, int limit, boolean tick) { + if (!holder.hasCapabilityProxies()) return 0; + + var inputs = (tick ? recipe.tickInputs : recipe.inputs).get(this); + if (inputs == null || inputs.isEmpty()) return 0; + + var totalInputs = getInputContents(holder); + + var parallelMap = inputs.stream() + .map(content -> (SoulIngredient) content.getContent()) + .collect(Collectors.toMap( + ingredient -> ingredient.stack().type(), + ingredient -> { + int available = totalInputs.getOrDefault(ingredient.stack().type(), 0); + int required = ingredient.stack().amount(); + return required == 0 ? Integer.MAX_VALUE : available / required; + }, + Math::min)); + + int maxParallel = parallelMap.values().stream() + .mapToInt(Integer::intValue) + .min() + .orElse(0); + + return Math.min(limit, maxParallel); } @Override public void addXEIInfo(WidgetGroup group, int xOffset, GTRecipe recipe, List contents, boolean perTick, boolean isInput, MutableInt yOffset) { - int soul = contents.stream().map(Content::getContent).mapToInt(SoulRecipeCapability.CAP::of).sum(); + String type = contents.stream().map(Content::getContent).map(SoulRecipeCapability.CAP::of) + .map(SoulIngredient::stack).map(SoulStack::type).map(SoulType::getSerializedName).findFirst() + .orElse(""); + long soul = contents.stream().map(Content::getContent).map(SoulRecipeCapability.CAP::of) + .map(SoulIngredient::stack).mapToLong(SoulStack::amount).sum(); if (isInput) { group.addWidget(new LabelWidget(3 - xOffset, yOffset.addAndGet(10), - LocalizationUtils.format("cosmiccore.recipe.soul_in", soul))); + LocalizationUtils.format("recipe.cosmiccore." + type + "_soul_in", soul))); } else { group.addWidget(new LabelWidget(3 - xOffset, yOffset.addAndGet(10), - LocalizationUtils.format("cosmiccore.recipe.soul_out", soul))); + LocalizationUtils.format("recipe.cosmiccore." + type + "_soul_out", soul))); + } + } + + private static class SerializerSoulIngredient implements IContentSerializer { + + public static SerializerSoulIngredient INSTANCE = new SerializerSoulIngredient(); + + @Override + public SoulIngredient of(Object o) { + if (o instanceof SoulStack stack) return SoulIngredient.of(stack); + else if (o instanceof SoulIngredient ingredient) return ingredient; + return SoulIngredient.of(new SoulStack(SoulType.Raw, 0)); + } + + @Override + public SoulIngredient defaultValue() { + return SoulIngredient.of(new SoulStack(SoulType.Raw, 0)); + } + + @Override + public Class contentClass() { + return SoulIngredient.class; + } + + @Override + public Codec codec() { + return SoulIngredient.CODEC; } } } diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/capability/souls/SoulType.java b/src/main/java/com/ghostipedia/cosmiccore/api/capability/souls/SoulType.java new file mode 100644 index 000000000..8e9e4a4a9 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/api/capability/souls/SoulType.java @@ -0,0 +1,66 @@ +package com.ghostipedia.cosmiccore.api.capability.souls; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import net.minecraft.util.StringRepresentable; + +import com.mojang.serialization.Codec; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum SoulType implements StringRepresentable { + + Raw("raw", ChatFormatting.DARK_RED), + Refined("refined", ChatFormatting.GRAY), + Proud("proud", ChatFormatting.DARK_PURPLE), + Greedy("greedy", ChatFormatting.YELLOW), + Lustful("lustful", ChatFormatting.LIGHT_PURPLE), + Envious("envious", ChatFormatting.GREEN), + Gluttonous("gluttonous", ChatFormatting.GOLD), + Wrathful("wrathful", ChatFormatting.RED), + Slothful("slothful", ChatFormatting.AQUA), + Temporal("temporal", ChatFormatting.DARK_AQUA); + + private final String name; + private final ChatFormatting color; + + SoulType(String name, ChatFormatting color) { + this.name = name; + this.color = color; + } + + @Override + public @NotNull String getSerializedName() { + return this.name; + } + + public static final Codec CODEC = StringRepresentable.fromEnum(SoulType::values); + + private static final Map BY_NAME = Arrays.stream(values()) + .collect(Collectors.toMap(SoulType::getSerializedName, Function.identity())); + + public static SoulType byName(String name) { + return BY_NAME.get(name); + } + + public Component toComponent(int amount) { + return toComponent(amount, true); + } + + public Component toComponent(int amount, boolean formatted) { + MutableComponent nameComp = Component.translatable("gui.cosmiccore.soul." + name + ".name"); + MutableComponent amountComp = Component.literal(" : " + amount).withStyle(Style.EMPTY); + if (formatted) { + nameComp = nameComp.withStyle(ChatFormatting.BOLD, this.color); + amountComp = amountComp.withStyle(ChatFormatting.RESET); + } + + return nameComp.append(amountComp); + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/codec/CosmicCodecUtils.java b/src/main/java/com/ghostipedia/cosmiccore/api/codec/CosmicCodecUtils.java new file mode 100644 index 000000000..fa965ca71 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/api/codec/CosmicCodecUtils.java @@ -0,0 +1,3 @@ +package com.ghostipedia.cosmiccore.api.codec; + +public class CosmicCodecUtils {} diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/data/souls/SoulNetwork.java b/src/main/java/com/ghostipedia/cosmiccore/api/data/souls/SoulNetwork.java new file mode 100644 index 000000000..4c8227b44 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/api/data/souls/SoulNetwork.java @@ -0,0 +1,94 @@ +package com.ghostipedia.cosmiccore.api.data.souls; + +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulStack; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraftforge.common.util.INBTSerializable; + +import lombok.Setter; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SoulNetwork implements INBTSerializable { + + @Setter + private Runnable dirtyCallback; + + private final Map contents = new ConcurrentHashMap<>(); + + public SoulNetwork() {} + + public SoulStack add(SoulStack stack, int throughput, int capacity, boolean simulate) { + int currentAmount = this.contents.getOrDefault(stack.type(), 0); + + int amountToAdd = Math.min(stack.amount(), throughput); // Respect throughput + amountToAdd = Math.min(amountToAdd, capacity - currentAmount); // Respect network capacity + amountToAdd = Math.max(0, amountToAdd); // Ensure we don't add a negative amount + + if (!simulate && amountToAdd > 0) { + this.contents.put(stack.type(), currentAmount + amountToAdd); + if (dirtyCallback != null) dirtyCallback.run(); + } + + System.out.println(this); + + return stack.withAmount(amountToAdd); + } + + public SoulStack syphon(SoulStack stack, boolean simulate) { + var currentSoulContent = this.contents.getOrDefault(stack.type(), 0); + int amountToSyphon = Math.min(stack.amount(), currentSoulContent); + + if (!simulate && amountToSyphon > 0) { + this.contents.put(stack.type(), currentSoulContent - amountToSyphon); + if (dirtyCallback != null) dirtyCallback.run(); + } + + return stack.withAmount(amountToSyphon); + } + + public void reset() { + this.contents.clear(); + if (dirtyCallback != null) dirtyCallback.run(); + } + + public List getContents() { + return contents.entrySet().stream() + .map(kvp -> new SoulStack(kvp.getKey(), kvp.getValue())) + .toList(); + } + + @Override + public String toString() { + return "SoulNetwork{contents=" + contents + '}'; + } + + @Override + public CompoundTag serializeNBT() { + var tag = new CompoundTag(); + var listTag = new ListTag(); + this.contents.forEach((soulType, amount) -> { + var contentTag = new CompoundTag(); + contentTag.putString("type", soulType.getSerializedName()); + contentTag.putInt("amount", amount); + listTag.add(contentTag); + }); + tag.put("contents", listTag); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag compoundTag) { + this.contents.clear(); + ListTag listTag = compoundTag.getList("contents", Tag.TAG_COMPOUND); + for (Tag t : listTag) { + var contentTag = (CompoundTag) t; + this.contents.put(SoulType.byName(contentTag.getString("type")), contentTag.getInt("amount")); + } + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/data/souls/SoulNetworkSavedData.java b/src/main/java/com/ghostipedia/cosmiccore/api/data/souls/SoulNetworkSavedData.java new file mode 100644 index 000000000..8dd5a6feb --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/api/data/souls/SoulNetworkSavedData.java @@ -0,0 +1,71 @@ +package com.ghostipedia.cosmiccore.api.data.souls; + +import com.ghostipedia.cosmiccore.CosmicCore; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.saveddata.SavedData; + +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.UUID; + +public class SoulNetworkSavedData extends SavedData { + + private static final String DATA_NAME = CosmicCore.MOD_ID + "_soul_network_data"; + private static final String SOUL_NETWORK_MAPPING = "soul_network_mapping"; + private static final String SOUL_NETWORK_UUID = "soul_network_uuid"; + private static final String SOUL_NETWORK_DATA = "soul_network_data"; + + private final HashMap soulNetworkMapping = new HashMap<>(20, 0.9f); + + public static SoulNetworkSavedData getOrCreate(ServerLevel serverLevel) { + return serverLevel.getDataStorage().computeIfAbsent(SoulNetworkSavedData::new, SoulNetworkSavedData::new, + DATA_NAME); + } + + public static SoulNetwork getSoulNetwork(ServerLevel level, UUID owner) { + SoulNetworkSavedData savedData = getOrCreate(level); + return savedData.getNetwork(owner); + } + + public SoulNetwork getNetwork(UUID owner) { + return soulNetworkMapping.computeIfAbsent(owner, id -> { + SoulNetwork network = new SoulNetwork(); + network.setDirtyCallback(this::setDirty); + setDirty(); + return network; + }); + } + + public SoulNetworkSavedData() {} + + public SoulNetworkSavedData(CompoundTag nbt) { + var list = nbt.getList(SOUL_NETWORK_MAPPING, CompoundTag.TAG_COMPOUND); + for (Tag tag : list) { + if (tag instanceof CompoundTag compoundTag) { + var uuid = UUID.fromString(compoundTag.getString(SOUL_NETWORK_UUID)); + var data = new SoulNetwork(); + data.deserializeNBT(compoundTag.getCompound(SOUL_NETWORK_DATA)); + data.setDirtyCallback(this::setDirty); + soulNetworkMapping.put(uuid, data); + } + } + } + + @Override + public @NotNull CompoundTag save(@NotNull CompoundTag compoundTag) { + var soulNetworkDataList = new ListTag(); + for (var entry : soulNetworkMapping.entrySet()) { + var tag = new CompoundTag(); + tag.putString(SOUL_NETWORK_UUID, entry.getKey().toString()); + tag.put(SOUL_NETWORK_DATA, entry.getValue().serializeNBT()); + soulNetworkDataList.add(tag); + } + compoundTag.put(SOUL_NETWORK_MAPPING, soulNetworkDataList); + return compoundTag; + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/machine/trait/NotifiableSoulContainer.java b/src/main/java/com/ghostipedia/cosmiccore/api/machine/trait/NotifiableSoulContainer.java index 4383432dd..36bcef060 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/api/machine/trait/NotifiableSoulContainer.java +++ b/src/main/java/com/ghostipedia/cosmiccore/api/machine/trait/NotifiableSoulContainer.java @@ -1,140 +1,107 @@ package com.ghostipedia.cosmiccore.api.machine.trait; -import com.ghostipedia.cosmiccore.api.capability.ISoulContainer; -import com.ghostipedia.cosmiccore.api.capability.recipe.SoulRecipeCapability; +import com.ghostipedia.cosmiccore.api.capability.recipe.CosmicRecipeCapabilities; +import com.ghostipedia.cosmiccore.api.data.souls.SoulNetwork; +import com.ghostipedia.cosmiccore.api.data.souls.SoulNetworkSavedData; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulIngredient; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulStack; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; -import com.gregtechceu.gtceu.api.machine.ConditionalSubscriptionHandler; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.trait.NotifiableRecipeHandlerTrait; import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.common.machine.owner.FTBOwner; -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; +import net.minecraft.server.level.ServerLevel; import lombok.Getter; -import wayoftime.bloodmagic.core.data.SoulNetwork; -import wayoftime.bloodmagic.core.data.SoulTicket; -import wayoftime.bloodmagic.util.helper.NetworkHelper; +import org.jetbrains.annotations.NotNull; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.UUID; -public class NotifiableSoulContainer extends NotifiableRecipeHandlerTrait implements ISoulContainer { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(NotifiableSoulContainer.class, - NotifiableRecipeHandlerTrait.MANAGED_FIELD_HOLDER); +public class NotifiableSoulContainer extends NotifiableRecipeHandlerTrait { @Getter - private final IO handlerIO; - private final ConditionalSubscriptionHandler conditionalSubscriptionHandler; + public final IO handlerIO; @Getter - @Persisted - @DescSynced - private UUID owner; + private final int throughput; @Getter - @DescSynced - private int currentEssence; - - @Persisted - private int maxCapacity; - - @Persisted - private int maxConsumption; + private final int capacity; - public NotifiableSoulContainer(MetaMachine machine, IO io, int maxCapacity, int maxConsumption) { + public NotifiableSoulContainer(MetaMachine machine, IO io, int throughput, int capacity) { super(machine); this.handlerIO = io; - this.currentEssence = -1; - this.maxCapacity = maxCapacity; - this.maxConsumption = maxConsumption; - conditionalSubscriptionHandler = new ConditionalSubscriptionHandler(machine, this::querySoulNetwork, - () -> owner != null); - } - - private void querySoulNetwork() { - if (this.machine.getOffsetTimer() % 20 != 0) return; - - var network = this.getSoulNetwork(); - if (network == null) return; - - var essence = network.getCurrentEssence(); - if (this.currentEssence == essence) return; - - this.currentEssence = essence; - this.notifyListeners(); + this.throughput = throughput; + this.capacity = capacity; } - @Override - public List handleRecipeInner(IO io, GTRecipe recipe, List left, - boolean simulate) { - ISoulContainer container = this; - if (container.getOwner() == null) return null; - - int lifeEssence = left.stream().reduce(0, Integer::sum); - if (io == IO.IN) { - var canOutput = Math.min(this.maxConsumption, container.getSoulNetwork().getCurrentEssence()); - if (!simulate) lifeEssence = container.getSoulNetwork().syphon( - SoulTicket.block(this.machine.getLevel(), this.machine.getPos(), Math.min(canOutput, lifeEssence)), - false); - lifeEssence = lifeEssence - canOutput; - } else if (io == IO.OUT) { - var canInput = this.maxCapacity - container.getSoulNetwork().getCurrentEssence(); - if (!simulate) lifeEssence = container.getSoulNetwork().add( - SoulTicket.block(this.machine.getLevel(), this.machine.getPos(), Math.min(canInput, lifeEssence)), - this.maxCapacity); - lifeEssence = lifeEssence - canInput; + private SoulNetwork getSoulNetwork() { + if (this.machine.getLevel() instanceof ServerLevel serverLevel) { + return SoulNetworkSavedData.getSoulNetwork(serverLevel, getOwner()); } - - return lifeEssence <= 0 ? null : Collections.singletonList(lifeEssence); + return new SoulNetwork(); } - @Override - public List getContents() { - if (this.owner == null) return Collections.emptyList(); - return List.of(this.getSoulNetwork().getCurrentEssence()); - } - - @Override - public double getTotalContentAmount() { - if (this.owner == null) return 0; - return this.getSoulNetwork().getCurrentEssence(); + private UUID getOwner() { + var team = ((FTBOwner) this.machine.getOwner()).getPlayerTeam(this.machine.getOwnerUUID()); + return team != null ? team.getTeamId() : this.machine.getOwnerUUID(); } @Override - public RecipeCapability getCapability() { - return SoulRecipeCapability.CAP; - } + public List handleRecipeInner(IO io, GTRecipe recipe, List left, boolean simulate) { + if (io != handlerIO) return left; + if (io != IO.IN && io != IO.OUT) return left.isEmpty() ? null : left; + + var network = getSoulNetwork(); + List result = new ArrayList<>(); + + for (SoulIngredient ingredient : left) { + SoulStack requiredStack = ingredient.stack(); + if (requiredStack.isEmpty()) continue; + + if (io == IO.IN) { + SoulStack consumedStack = network.syphon(requiredStack, simulate); + if (consumedStack.amount() < requiredStack.amount()) { + result.add(SoulIngredient + .of(requiredStack.withAmount(requiredStack.amount() - consumedStack.amount()))); + } + } else { + SoulStack canInput = network.add(requiredStack, throughput, capacity, simulate); + SoulStack reminder = requiredStack.withAmount(requiredStack.amount() - canInput.amount()); + if (reminder.amount() > 0) result.add(SoulIngredient.of(reminder)); + } + } - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; + return result.isEmpty() ? null : result; } @Override - public SoulNetwork getSoulNetwork() { - return NetworkHelper.getSoulNetwork(this.owner); + public @NotNull List getContents() { + return getSoulNetwork().getContents().stream() + .map(SoulIngredient::new) + .map(Object.class::cast) + .toList(); } @Override public int getSize() { - return 1; + return getSoulNetwork().getContents().size(); } @Override - public void setOwner(UUID owner) { - this.owner = owner; - conditionalSubscriptionHandler.updateSubscription(); + public double getTotalContentAmount() { + return getSoulNetwork().getContents().stream() + .mapToInt(SoulStack::amount) + .sum(); } @Override - public void onMachineLoad() { - super.onMachineLoad(); - conditionalSubscriptionHandler.initialize(this.machine.getLevel()); + public RecipeCapability getCapability() { + return CosmicRecipeCapabilities.SOUL; } } diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/recipe/content/SerializerSoulStack.java b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/content/SerializerSoulStack.java new file mode 100644 index 000000000..857d867c4 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/content/SerializerSoulStack.java @@ -0,0 +1,47 @@ +package com.ghostipedia.cosmiccore.api.recipe.content; + +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulStack; + +import com.gregtechceu.gtceu.api.recipe.content.IContentSerializer; + +import net.minecraft.network.FriendlyByteBuf; + +import com.mojang.serialization.Codec; + +public class SerializerSoulStack implements IContentSerializer { + + public static SerializerSoulStack INSTANCE = new SerializerSoulStack(); + + private SerializerSoulStack() {} + + @Override + public void toNetwork(FriendlyByteBuf buf, SoulStack content) { + content.toNetwork(buf); + } + + @Override + public SoulStack fromNetwork(FriendlyByteBuf buf) { + return SoulStack.fromNetwork(buf); + } + + @Override + public SoulStack of(Object o) { + if (o instanceof SoulStack stack) return stack; + else return SoulStack.EMPTY; + } + + @Override + public SoulStack defaultValue() { + return SoulStack.EMPTY; + } + + @Override + public Class contentClass() { + return SoulStack.class; + } + + @Override + public Codec codec() { + return SoulStack.CODEC; + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/recipe/ingredient/SoulIngredient.java b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/ingredient/SoulIngredient.java new file mode 100644 index 000000000..f10639c85 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/ingredient/SoulIngredient.java @@ -0,0 +1,39 @@ +package com.ghostipedia.cosmiccore.api.recipe.ingredient; + +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; + +import com.mojang.serialization.Codec; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Predicate; + +public record SoulIngredient(SoulStack stack) implements Predicate { + + public static final Codec CODEC = SoulStack.CODEC.xmap(SoulIngredient::new, SoulIngredient::stack); + + public static SoulIngredient of(final SoulStack stack) { + return new SoulIngredient(stack); + } + + public static SoulIngredient of(SoulType soulType, int amount) { + return new SoulIngredient(new SoulStack(soulType, amount)); + } + + @Override + public boolean test(SoulStack soulStack) { + return this.stack.type() == soulStack.type() && this.stack.amount() <= soulStack.amount(); + } + + @Override + public @NotNull String toString() { + return "SoulIngredient{stack=" + stack + "}"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SoulIngredient other)) { + return false; + } + return stack.equals(other.stack); + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/recipe/ingredient/SoulStack.java b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/ingredient/SoulStack.java new file mode 100644 index 000000000..528e250c7 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/ingredient/SoulStack.java @@ -0,0 +1,43 @@ +package com.ghostipedia.cosmiccore.api.recipe.ingredient; + +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; + +import net.minecraft.network.FriendlyByteBuf; + +import com.google.common.base.Preconditions; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import lombok.With; + +@With +public record SoulStack(SoulType type, int amount) { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + SoulType.CODEC.fieldOf("type").forGetter(SoulStack::type), + Codec.INT.fieldOf("amount").forGetter(SoulStack::amount)).apply(instance, SoulStack::new)); + + public static final SoulStack EMPTY = new SoulStack(SoulType.Raw, 0); + + public boolean isEmpty() { + return this.amount <= 0; + } + + public SoulStack add(int amount) { + Preconditions.checkArgument(this.amount + amount >= 0, "Resulting amount must be non-negative"); + return new SoulStack(this.type, this.amount + amount); + } + + public SoulStack sum(SoulStack a, SoulStack b) { + Preconditions.checkArgument(a.type == b.type, "SoulStack types don't match"); + return a.add(b.amount); + } + + public void toNetwork(FriendlyByteBuf buf) { + buf.writeEnum(this.type); + buf.writeVarInt(this.amount); + } + + public static SoulStack fromNetwork(FriendlyByteBuf buf) { + return new SoulStack(buf.readEnum(SoulType.class), buf.readVarInt()); + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/api/recipe/lookup/MapSoulIngredient.java b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/lookup/MapSoulIngredient.java index 2ab62d0f4..fdcb9792b 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/api/recipe/lookup/MapSoulIngredient.java +++ b/src/main/java/com/ghostipedia/cosmiccore/api/recipe/lookup/MapSoulIngredient.java @@ -1,34 +1,38 @@ package com.ghostipedia.cosmiccore.api.recipe.lookup; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulIngredient; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulStack; + import com.gregtechceu.gtceu.api.recipe.lookup.ingredient.AbstractMapIngredient; -import java.util.Collections; import java.util.List; public class MapSoulIngredient extends AbstractMapIngredient { - public final Integer souls; + public final SoulStack stack; - public MapSoulIngredient(Integer souls) { - this.souls = souls; + public MapSoulIngredient(SoulStack stack) { + this.stack = stack; } @Override protected int hash() { - return MapSoulIngredient.class.hashCode(); + return 0; } @Override - public boolean equals(Object obj) { - return obj instanceof MapSoulIngredient; + public boolean equals(Object o) { + if (!(o instanceof MapSoulIngredient other)) return false; + return stack.type().equals(other.stack.type()); } - @Override - public String toString() { - return "MapSoulIngredient{" + "souls=" + souls + '}'; + public static List from(SoulIngredient soulIngredient) { + SoulStack stack = soulIngredient.stack(); + return List.of(new MapSoulIngredient(stack)); } - public static List convertToMapIngredient(Integer essence) { - return Collections.singletonList(new MapSoulIngredient(essence)); + @Override + public String toString() { + return "MapSoulIngredient{stack=" + stack + "}"; } } diff --git a/src/main/java/com/ghostipedia/cosmiccore/common/commands/SoulCommand.java b/src/main/java/com/ghostipedia/cosmiccore/common/commands/SoulCommand.java new file mode 100644 index 000000000..dab0039a8 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/common/commands/SoulCommand.java @@ -0,0 +1,95 @@ +package com.ghostipedia.cosmiccore.common.commands; + +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; +import com.ghostipedia.cosmiccore.api.data.souls.SoulNetworkSavedData; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulStack; +import com.ghostipedia.cosmiccore.common.commands.argument.SoulTypeArgument; +import com.ghostipedia.cosmiccore.common.item.SoulNetworkReaderItem; + +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.context.CommandContext; +import dev.ftb.mods.ftbteams.data.TeamArgument; + +import java.util.UUID; + +import static net.minecraft.commands.Commands.*; + +public class SoulCommand { + + public static void register(CommandDispatcher dispatcher, CommandBuildContext buildContext) { + dispatcher.register( + literal("soul") + .requires(source -> source.hasPermission(LEVEL_ADMINS)) + .then(literal("player") + .then(argument("player", EntityArgument.player()) + .then(literal("info").executes(ctx -> displayInfo(ctx, + EntityArgument.getPlayer(ctx, "player").getUUID()))) + .then(literal("reset").executes(ctx -> resetNetwork(ctx, + EntityArgument.getPlayer(ctx, "player").getUUID()))) + .then(literal("add").then(argument("type", SoulTypeArgument.soulType()) + .then(argument("amount", IntegerArgumentType.integer()) + .executes(ctx -> addSouls(ctx, + EntityArgument.getPlayer(ctx, "player").getUUID(), + SoulTypeArgument.get(ctx, "type"), + IntegerArgumentType.getInteger(ctx, "amount")))))) + .then(literal("syphon").then(argument("type", SoulTypeArgument.soulType()) + .then(argument("amount", IntegerArgumentType.integer()) + .executes(ctx -> syphon(ctx, + EntityArgument.getPlayer(ctx, "player").getUUID(), + SoulTypeArgument.get(ctx, "type"), + IntegerArgumentType.getInteger(ctx, "amount")))))))) + .then(literal("team") + .then(argument("team", TeamArgument.create()) + .then(literal("info").executes( + ctx -> displayInfo(ctx, TeamArgument.get(ctx, "team").getTeamId()))) + .then(literal("reset").executes( + ctx -> resetNetwork(ctx, TeamArgument.get(ctx, "team").getTeamId()))) + .then(literal("add").then(argument("type", SoulTypeArgument.soulType()) + .then(argument("amount", IntegerArgumentType.integer()) + .executes(ctx -> addSouls(ctx, + TeamArgument.get(ctx, "team").getTeamId(), + SoulTypeArgument.get(ctx, "type"), + IntegerArgumentType.getInteger(ctx, "amount")))))) + .then(literal("syphon").then(argument("type", SoulTypeArgument.soulType()) + .then(argument("amount", IntegerArgumentType.integer()) + .executes(ctx -> syphon(ctx, + TeamArgument.get(ctx, "team").getTeamId(), + SoulTypeArgument.get(ctx, "type"), + IntegerArgumentType.getInteger(ctx, "amount"))))))))); + } + + private static int displayInfo(CommandContext context, UUID owner) { + var network = SoulNetworkSavedData.getSoulNetwork(context.getSource().getLevel(), owner); + context.getSource().sendSuccess(() -> SoulNetworkReaderItem.displaySoulNetworkInfo(network), false); + return 1; + } + + private static int resetNetwork(CommandContext context, UUID owner) { + var network = SoulNetworkSavedData.getSoulNetwork(context.getSource().getLevel(), owner); + network.reset(); + context.getSource().sendSuccess(() -> Component.translatable("gui.cosmiccore.soul.reset"), false); + return 1; + } + + private static int addSouls(CommandContext context, UUID owner, SoulType type, int amount) { + var network = SoulNetworkSavedData.getSoulNetwork(context.getSource().getLevel(), owner); + network.add(new SoulStack(type, amount), Integer.MAX_VALUE, Integer.MAX_VALUE, false); + context.getSource().sendSuccess( + () -> Component.translatable("gui.cosmiccore.soul.add", amount, type.getSerializedName()), false); + return 1; + } + + private static int syphon(CommandContext context, UUID owner, SoulType type, int amount) { + var network = SoulNetworkSavedData.getSoulNetwork(context.getSource().getLevel(), owner); + network.syphon(new SoulStack(type, amount), false); + context.getSource().sendSuccess( + () -> Component.translatable("gui.cosmiccore.soul.remove", amount, type.getSerializedName()), false); + return 1; + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/common/commands/argument/SoulTypeArgument.java b/src/main/java/com/ghostipedia/cosmiccore/common/commands/argument/SoulTypeArgument.java new file mode 100644 index 000000000..c4b02e0f9 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/common/commands/argument/SoulTypeArgument.java @@ -0,0 +1,58 @@ +package com.ghostipedia.cosmiccore.common.commands.argument; + +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.network.chat.Component; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class SoulTypeArgument implements ArgumentType { + + private static final Collection EXAMPLES = Arrays.stream(SoulType.values()) + .map(SoulType::getSerializedName) + .collect(Collectors.toList()); + + public static final DynamicCommandExceptionType ERROR_INVALID_VALUE = new DynamicCommandExceptionType( + (object) -> Component.translatable("argument.enum.invalid", object)); + + public static SoulTypeArgument soulType() { + return new SoulTypeArgument(); + } + + public static SoulType get(CommandContext context, String name) { + return context.getArgument(name, SoulType.class); + } + + @Override + public SoulType parse(StringReader reader) throws CommandSyntaxException { + String s = reader.readUnquotedString(); + SoulType soulType = SoulType.byName(s); + if (soulType == null) { + throw ERROR_INVALID_VALUE.create(s); + } + return soulType; + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return SharedSuggestionProvider.suggest(EXAMPLES, builder); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicItems.java b/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicItems.java index 9c19c840b..be034882e 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicItems.java +++ b/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicItems.java @@ -11,6 +11,7 @@ import com.ghostipedia.cosmiccore.common.item.AsteroidTargetingChipItem; import com.ghostipedia.cosmiccore.common.item.CosmicScytheItem; import com.ghostipedia.cosmiccore.common.item.OxygenTankItem; +import com.ghostipedia.cosmiccore.common.item.SoulNetworkReaderItem; import com.ghostipedia.cosmiccore.common.item.armor.ChestSanguineWarptechSuite; import com.ghostipedia.cosmiccore.common.item.armor.HelmetSanguineWarptechSuite; import com.ghostipedia.cosmiccore.common.item.armor.SanguineWarptechSuite; @@ -108,6 +109,14 @@ public class CosmicItems { "cosmiccore"); // Modules + public static final ItemEntry SOUL_READER = REGISTRATE + .item("soul_reader", SoulNetworkReaderItem::new) + .lang("Soul Network Reader") + .properties(p -> p.stacksTo(1)) + .tag() + .defaultModel() + .register(); + public static final ItemEntry ETHERIC_SPIRIT_ITEM = REGISTRATE .item("etheric_spirit", (properties -> new SpiritShardItem(properties, CosmicItems.ETHERIC_SPIRIT))) .lang("Etheric Spirit") diff --git a/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicMachines.java b/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicMachines.java index 6cf807d16..9edc89ed8 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicMachines.java +++ b/src/main/java/com/ghostipedia/cosmiccore/common/data/CosmicMachines.java @@ -49,6 +49,7 @@ import com.gregtechceu.gtceu.common.machine.multiblock.electric.PowerSubstationMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.EnergyHatchPartMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.FluidHatchPartMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.primitive.PrimitiveWorkableMachine; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.utils.FormattingUtil; @@ -71,7 +72,6 @@ import static com.ghostipedia.cosmiccore.common.machine.multiblock.electric.hpca.HPCAMachine.*; import static com.ghostipedia.cosmiccore.gtbridge.CosmicRecipeTypes.BIO_LAB; import static com.gregtechceu.gtceu.api.GTValues.*; -import static com.gregtechceu.gtceu.api.capability.recipe.IO.IN; import static com.gregtechceu.gtceu.api.capability.recipe.IO.OUT; import static com.gregtechceu.gtceu.api.machine.property.GTMachineModelProperties.*; import static com.gregtechceu.gtceu.api.pattern.Predicates.*; @@ -84,7 +84,6 @@ import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.CENTRIFUGE_RECIPES; import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.DUMMY_RECIPES; import static com.gregtechceu.gtceu.common.data.machines.GTMachineUtils.*; -import static com.gregtechceu.gtceu.common.data.machines.GTMachineUtils.registerTieredMachines; import static com.gregtechceu.gtceu.common.data.machines.GTMultiMachines.FUSION_REACTOR; import static com.gregtechceu.gtceu.common.data.models.GTMachineModels.*; @@ -386,20 +385,21 @@ public class CosmicMachines { GTValues.tiersBetween(ULV, HV)); // Enable If needed Inside of Dev - // public static final MultiblockMachineDefinition SOUL_TESTER = REGISTRATE.multiblock("soul_tester", - // PrimitiveWorkableMachine::new) - // .rotationState(RotationState.NON_Y_AXIS) - // .recipeType(CosmicCoreRecipeTypes.SOUL_TESTER_RECIPES) - // .appearanceBlock(GTBlocks.CASING_PRIMITIVE_BRICKS) - // .pattern(definition -> FactoryBlockPattern.start() - // .aisle("S", "C", "I") - // .where("C", controller(blocks(definition.getBlock()))) - // .where("S", abilities(CosmicPartAbility.IMPORT_SOUL).or(abilities(CosmicPartAbility.EXPORT_SOUL))) - // .where("I", abilities(PartAbility.EXPORT_ITEMS).or(abilities(PartAbility.IMPORT_ITEMS))) - // .build()) - // .workableCasingModel(GTCEu.id("block/casings/solid/machine_casing_inert_ptfe"), - // GTCEu.id("block/multiblock/coke_oven")) - // .register(); + public static final MultiblockMachineDefinition SOUL_TESTER = REGISTRATE + .multiblock("soul_tester", PrimitiveWorkableMachine::new) + .rotationState(RotationState.NON_Y_AXIS) + .recipeType(CosmicRecipeTypes.SOUL_TESTER_RECIPES) + .appearanceBlock(GTBlocks.CASING_PRIMITIVE_BRICKS) + .pattern(definition -> FactoryBlockPattern.start() + .aisle("S", "S", "C", "I", "I") + .where("C", controller(blocks(definition.getBlock()))) + .where("S", abilities(CosmicPartAbility.IMPORT_SOUL).or(abilities(CosmicPartAbility.EXPORT_SOUL))) + .where("I", abilities(PartAbility.EXPORT_ITEMS).or(abilities(PartAbility.IMPORT_ITEMS))) + .build()) + .workableCasingModel(GTCEu.id("block/casings/solid/machine_casing_inert_ptfe"), + GTCEu.id("block/multiblock/coke_oven")) + .register(); + /* * public static final MultiblockMachineDefinition EMBER_TESTER = REGISTRATE.multiblock("ember_tester", * PrimitiveWorkableMachine::new) @@ -490,9 +490,6 @@ private static MachineDefinition[] registerSoulHatch(String name, String display if (io == IO.IN) tooltip.add(Component.translatable("tooltip.cosmiccore.soul_hatch.input", SoulHatchPartMachine.getMaxConsumption(tier))); - else - tooltip.add(Component.translatable("tooltip.cosmiccore.soul_hatch.output", - SoulHatchPartMachine.getMaxCapacity(tier))); }).register(), tiers); } diff --git a/src/main/java/com/ghostipedia/cosmiccore/common/item/SoulNetworkReaderItem.java b/src/main/java/com/ghostipedia/cosmiccore/common/item/SoulNetworkReaderItem.java new file mode 100644 index 000000000..34b317c96 --- /dev/null +++ b/src/main/java/com/ghostipedia/cosmiccore/common/item/SoulNetworkReaderItem.java @@ -0,0 +1,48 @@ +package com.ghostipedia.cosmiccore.common.item; + +import com.ghostipedia.cosmiccore.api.data.souls.SoulNetwork; +import com.ghostipedia.cosmiccore.api.data.souls.SoulNetworkSavedData; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulStack; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import java.util.List; + +public class SoulNetworkReaderItem extends Item { + + public SoulNetworkReaderItem(Properties properties) { + super(properties); + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + if (!level.isClientSide() && player instanceof ServerPlayer serverPlayer) { + SoulNetwork soulNetwork = SoulNetworkSavedData.getSoulNetwork((ServerLevel) level, player.getUUID()); + player.sendSystemMessage(displaySoulNetworkInfo(soulNetwork)); + } + return InteractionResultHolder.success(player.getItemInHand(hand)); + } + + public static Component displaySoulNetworkInfo(SoulNetwork network) { + var message = Component.empty(); + List contents = network.getContents(); + if (contents.isEmpty()) { + message.append(Component.translatable("gui.cosmiccore.soul.empty_network").withStyle(ChatFormatting.GRAY)); + } else { + message.append( + Component.translatable("gui.cosmiccore.soul.network_contents").withStyle(ChatFormatting.GOLD)) + .append("\n"); + for (SoulStack stack : contents) message.append("\n").append(stack.type().toComponent(stack.amount())); + } + return message; + } +} diff --git a/src/main/java/com/ghostipedia/cosmiccore/common/machine/multiblock/part/SoulHatchPartMachine.java b/src/main/java/com/ghostipedia/cosmiccore/common/machine/multiblock/part/SoulHatchPartMachine.java index 78a497a32..dca224da2 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/common/machine/multiblock/part/SoulHatchPartMachine.java +++ b/src/main/java/com/ghostipedia/cosmiccore/common/machine/multiblock/part/SoulHatchPartMachine.java @@ -6,8 +6,8 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredIOPartMachine; -import com.gregtechceu.gtceu.utils.FormattingUtil; import com.lowdragmc.lowdraglib.gui.widget.*; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; @@ -17,7 +17,7 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.client.resources.language.I18n; import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.player.Player; +import net.minecraft.server.TickTask; import wayoftime.bloodmagic.util.helper.PlayerHelper; @@ -36,7 +36,24 @@ public class SoulHatchPartMachine extends TieredIOPartMachine { public SoulHatchPartMachine(IMachineBlockEntity holder, int tier, IO io) { super(holder, tier, io); - this.soulContainer = new NotifiableSoulContainer(this, io, getMaxCapacity(tier), getMaxConsumption(tier)); + this.soulContainer = new NotifiableSoulContainer(this, io, getMaxConsumption(tier), getMaxCapacity(tier)); + } + + @Override + public void addedToController(IMultiController controller) { + super.addedToController(controller); + controller.self().getLevel().getServer().tell(new TickTask(0, this::invalidateIfDuplicate)); + } + + private void invalidateIfDuplicate() { + for (var controller : getControllers()) { + for (var part : controller.getParts()) { + if (part == this) continue; + if (part instanceof SoulHatchPartMachine soulHatch && soulHatch.io == this.io) { + controller.onStructureInvalid(); + } + } + } } @Override @@ -47,62 +64,49 @@ public Widget createUIWidget() { group.addWidget(new LabelWidget(8, 8, Component.translatable("gui.cosmiccore.soul_hatch.label." + (this.io == IO.IN ? "import" : "export")))); - if (soulContainer.getOwner() == null) { - group.addWidget( - new LabelWidget(8, 18, I18n.get("gui.cosmiccore.soul_hatch.no_network")).setClientSideWidget()); - } else { - group.addWidget( - new LabelWidget(8, 18, - () -> I18n.get("gui.cosmiccore.soul_hatch.owner", - PlayerHelper.getUsernameFromUUID(this.soulContainer.getOwner()))) - .setClientSideWidget()); - group.addWidget( - new LabelWidget(8, 28, - () -> I18n.get("gui.cosmiccore.soul_hatch.lp", - FormattingUtil.formatNumbers(soulContainer.getCurrentEssence()))) - .setClientSideWidget()); - } + // TODO: Get and display proper player/team Name + group.addWidget( + new LabelWidget(8, 18, + () -> I18n.get("gui.cosmiccore.soul_hatch.owner", + PlayerHelper.getUsernameFromUUID(this.soulContainer.getMachine().getOwnerUUID()))) + .setClientSideWidget()); group.setBackground(GuiTextures.BACKGROUND_INVERSE); return group; } - public static int getMaxCapacity(int tier) { + public static int getMaxConsumption(int tier) { return switch (tier) { - case GTValues.IV -> 5000000; - case GTValues.LuV -> 10000000; - case GTValues.ZPM -> 20000000; - case GTValues.UV -> 30000000; - case GTValues.UHV -> 50000000; - case GTValues.UEV -> 100000000; - case GTValues.UIV -> 250000000; - case GTValues.UXV -> 500000000; - case GTValues.OpV -> 1000000000; + case GTValues.IV -> 10_000; + case GTValues.LuV -> 50_000; + case GTValues.ZPM -> 5_000_000; + case GTValues.UV -> 10_000_000; + case GTValues.UHV -> 25_000_000; + case GTValues.UEV -> 50_000_000; + case GTValues.UIV -> 125_000_000; + case GTValues.UXV -> 250_000_000; + case GTValues.OpV -> 500_000_000; case GTValues.MAX -> Integer.MAX_VALUE; default -> 0; }; } - public static int getMaxConsumption(int tier) { + public static int getMaxCapacity(int tier) { return switch (tier) { - case GTValues.IV -> 10000; - case GTValues.LuV -> 50000; - case GTValues.ZPM -> 5000000; - case GTValues.UV -> 10000000; - case GTValues.UHV -> 25000000; - case GTValues.UEV -> 50000000; - case GTValues.UIV -> 125000000; - case GTValues.UXV -> 250000000; - case GTValues.OpV -> 500000000; + case GTValues.IV -> 1_000_000; + case GTValues.LuV -> 10_000_000; + case GTValues.ZPM -> 50_000_000; + case GTValues.UV -> 100_000_000; + case GTValues.UHV -> 250_000_000; + case GTValues.UEV -> 500_000_000; + case GTValues.UIV -> 1_000_000_000; + case GTValues.UXV -> 1_500_000_000; + case GTValues.OpV -> 2_000_000_000; case GTValues.MAX -> Integer.MAX_VALUE; default -> 0; }; } - public void attachSoulNetwork(Player player) { - this.soulContainer.setOwner(player.getUUID()); - } - @Override public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; diff --git a/src/main/java/com/ghostipedia/cosmiccore/forge/ForgeCommonEventListener.java b/src/main/java/com/ghostipedia/cosmiccore/forge/ForgeCommonEventListener.java index dd7ab1bda..5ca4505b6 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/forge/ForgeCommonEventListener.java +++ b/src/main/java/com/ghostipedia/cosmiccore/forge/ForgeCommonEventListener.java @@ -2,6 +2,7 @@ import com.ghostipedia.cosmiccore.CosmicCore; import com.ghostipedia.cosmiccore.CosmicUtils; +import com.ghostipedia.cosmiccore.common.commands.SoulCommand; import com.ghostipedia.cosmiccore.common.commands.VeinSurveyCommand; import com.ghostipedia.cosmiccore.common.commands.WirelessEnergyCommand; import com.ghostipedia.cosmiccore.common.data.CosmicItems; @@ -12,14 +13,12 @@ import com.ghostipedia.cosmiccore.common.machine.multiblock.multi.SteamAssembler; import com.ghostipedia.cosmiccore.common.machine.multiblock.multi.SteamCaster; import com.ghostipedia.cosmiccore.common.machine.multiblock.multi.SteamMixer; -import com.ghostipedia.cosmiccore.common.machine.multiblock.part.SoulHatchPartMachine; import com.ghostipedia.cosmiccore.common.reflection.ReflectionCommand; import com.ghostipedia.cosmiccore.common.reflection.ReflectionCommands; import com.ghostipedia.cosmiccore.mixin.accessor.LivingEntityAccessor; import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.block.MetaMachineBlock; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.item.armor.ArmorComponentItem; import com.gregtechceu.gtceu.api.machine.MachineDefinition; @@ -42,7 +41,6 @@ import net.minecraftforge.event.entity.living.LivingEquipmentChangeEvent; import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.entity.living.LivingFallEvent; -import net.minecraftforge.event.level.BlockEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @@ -54,15 +52,6 @@ @Mod.EventBusSubscriber(modid = CosmicCore.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE) public class ForgeCommonEventListener { - @SubscribeEvent - public static void entityPlacementEventHandler(BlockEvent.EntityPlaceEvent event) { - if (event.getPlacedBlock().getBlock() instanceof MetaMachineBlock block && - block.getMachine(event.getLevel(), event.getPos()) instanceof SoulHatchPartMachine soulHatch && - event.getEntity() instanceof Player player) { - soulHatch.attachSoulNetwork(player); - } - } - @SubscribeEvent public static void onPlayerTick(final TickEvent.PlayerTickEvent event) { if (event.phase != TickEvent.Phase.END) { @@ -130,6 +119,7 @@ public static void onPlayerDeath(LivingDeathEvent event) { @SubscribeEvent public static void registerCommand(RegisterCommandsEvent event) { WirelessEnergyCommand.register(event.getDispatcher(), event.getBuildContext()); + SoulCommand.register(event.getDispatcher(), event.getBuildContext()); ReflectionCommand.register(event.getDispatcher()); ReflectionCommands.register(event.getDispatcher()); VeinSurveyCommand.register(event.getDispatcher()); diff --git a/src/main/java/com/ghostipedia/cosmiccore/gtbridge/CosmicCoreRecipes.java b/src/main/java/com/ghostipedia/cosmiccore/gtbridge/CosmicCoreRecipes.java index fdbecb421..5e1aa602f 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/gtbridge/CosmicCoreRecipes.java +++ b/src/main/java/com/ghostipedia/cosmiccore/gtbridge/CosmicCoreRecipes.java @@ -1,5 +1,8 @@ package com.ghostipedia.cosmiccore.gtbridge; +import com.ghostipedia.cosmiccore.api.capability.recipe.CosmicRecipeCapabilities; +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulIngredient; import com.ghostipedia.cosmiccore.common.machine.multiblock.multi.logic.LarvaMachine; import com.ghostipedia.cosmiccore.common.recipe.condition.LinkedPartnerCondition; import com.ghostipedia.cosmiccore.common.recipe.condition.LinkedPartnerDimensionCondition; @@ -106,6 +109,20 @@ public static void init(Consumer provider) { * .save(provider); */ + SOUL_TESTER_RECIPES.recipeBuilder("generate_soul") + .notConsumable(Items.DIRT) + .output(CosmicRecipeCapabilities.SOUL, SoulIngredient.of(SoulType.Raw, 10)) + .output(CosmicRecipeCapabilities.SOUL, SoulIngredient.of(SoulType.Temporal, 50)) + .duration(20) + .save(provider); + + SOUL_TESTER_RECIPES.recipeBuilder("generate_soul_2") + .notConsumable(Items.STONE) + .input(CosmicRecipeCapabilities.SOUL, SoulIngredient.of(SoulType.Raw, 10)) + .outputItems(ingot, Steel) + .duration(20) + .save(provider); + // GROVE_RECIPES.recipeBuilder("dirt_movement") // .input(SoulRecipeCapability.CAP, 100) // .notConsumable(CosmicItems.DONK) diff --git a/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/CosmicCoreKubeJSPlugin.java b/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/CosmicCoreKubeJSPlugin.java index 875e2b221..4ebe0fd54 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/CosmicCoreKubeJSPlugin.java +++ b/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/CosmicCoreKubeJSPlugin.java @@ -1,6 +1,7 @@ package com.ghostipedia.cosmiccore.integration.kjs; import com.ghostipedia.cosmiccore.CosmicCore; +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; import com.ghostipedia.cosmiccore.common.data.CosmicBlocks; import com.ghostipedia.cosmiccore.common.data.CosmicItems; import com.ghostipedia.cosmiccore.common.data.CosmicMachines; @@ -63,6 +64,7 @@ public void registerBindings(BindingsEvent event) { event.add("CosmicMachines", CosmicMachines.class); event.add("CosmicItems", CosmicItems.class); event.add("CosmicRecipeTypes", CosmicRecipeTypes.class); + event.add("CosmicSoulTypes", SoulType.class); event.add("CosmicCore", CosmicCore.class); } diff --git a/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/CosmicCoreRecipeSchema.java b/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/CosmicCoreRecipeSchema.java index b6eb267c2..4e34ba7aa 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/CosmicCoreRecipeSchema.java +++ b/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/CosmicCoreRecipeSchema.java @@ -3,6 +3,8 @@ import com.ghostipedia.cosmiccore.api.capability.recipe.EmberRecipeCapability; import com.ghostipedia.cosmiccore.api.capability.recipe.SoulRecipeCapability; import com.ghostipedia.cosmiccore.api.capability.recipe.SterileRecipeCapability; +import com.ghostipedia.cosmiccore.api.capability.souls.SoulType; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulIngredient; import com.ghostipedia.cosmiccore.common.recipe.condition.TitanCondition; import com.gregtechceu.gtceu.integration.kjs.recipe.GTRecipeSchema; @@ -19,12 +21,12 @@ public interface CosmicCoreRecipeSchema { @Accessors(chain = true, fluent = true) class CosmicRecipeJS extends GTRecipeSchema.GTRecipeJS { - public GTRecipeSchema.GTRecipeJS soulInput(int souls) { - return this.input(SoulRecipeCapability.CAP, souls); + public GTRecipeSchema.GTRecipeJS soulInput(SoulType type, int souls) { + return this.input(SoulRecipeCapability.CAP, SoulIngredient.of(type, souls)); } - public GTRecipeSchema.GTRecipeJS soulOutput(int souls) { - return this.output(SoulRecipeCapability.CAP, souls); + public GTRecipeSchema.GTRecipeJS soulOutput(SoulType type, int souls) { + return this.output(SoulRecipeCapability.CAP, SoulIngredient.of(type, souls)); } public GTRecipeSchema.GTRecipeJS titanTier(int tier) { diff --git a/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/components/CosmicRecipeComponent.java b/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/components/CosmicRecipeComponent.java index 22bbcbd2f..b0ad4c814 100644 --- a/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/components/CosmicRecipeComponent.java +++ b/src/main/java/com/ghostipedia/cosmiccore/integration/kjs/recipe/components/CosmicRecipeComponent.java @@ -2,22 +2,59 @@ import com.ghostipedia.cosmiccore.api.capability.recipe.EmberRecipeCapability; import com.ghostipedia.cosmiccore.api.capability.recipe.SoulRecipeCapability; +import com.ghostipedia.cosmiccore.api.recipe.ingredient.SoulIngredient; import com.gregtechceu.gtceu.integration.kjs.recipe.components.ContentJS; +import com.google.gson.JsonElement; +import com.mojang.serialization.JsonOps; +import dev.latvian.mods.kubejs.recipe.RecipeJS; import dev.latvian.mods.kubejs.recipe.component.NumberComponent; +import dev.latvian.mods.kubejs.recipe.component.RecipeComponent; +import dev.latvian.mods.rhino.Wrapper; public class CosmicRecipeComponent { - public static final ContentJS SOUL_IN = new ContentJS<>(NumberComponent.ANY_INT, SoulRecipeCapability.CAP, - false); - public static final ContentJS SOUL_OUT = new ContentJS<>(NumberComponent.ANY_INT, SoulRecipeCapability.CAP, - true); - public static final ContentJS EMBER_IN = new ContentJS<>(NumberComponent.ANY_DOUBLE, EmberRecipeCapability.CAP, false); public static final ContentJS EMBER_OUT = new ContentJS<>(NumberComponent.ANY_DOUBLE, EmberRecipeCapability.CAP, true); + + public static final RecipeComponent SOUL_STACK = new RecipeComponent<>() { + + @Override + public Class componentClass() { + return SoulIngredient.class; + } + + @Override + public String componentType() { + return "soul_stack"; + } + + @Override + public JsonElement write(RecipeJS recipeJS, SoulIngredient soulIngredient) { + return SoulIngredient.CODEC.encodeStart(JsonOps.INSTANCE, soulIngredient).result().orElse(null); + } + + @Override + public SoulIngredient read(RecipeJS recipeJS, Object o) { + if (o instanceof Wrapper w) o = w.unwrap(); + + if (o instanceof SoulIngredient soulIngredient) { + return soulIngredient; + } else { + System.out.println("Shit we have a problem !"); + System.out.println("object type: " + o.getClass().descriptorString()); + return null; + } + } + }; + + public static final ContentJS SOUL_IN = new ContentJS<>(SOUL_STACK, SoulRecipeCapability.CAP, + false); + public static final ContentJS SOUL_OUT = new ContentJS<>(SOUL_STACK, SoulRecipeCapability.CAP, + true); } diff --git a/src/main/resources/assets/cosmiccore/textures/item/soul_reader.png b/src/main/resources/assets/cosmiccore/textures/item/soul_reader.png new file mode 100644 index 000000000..b900d2016 Binary files /dev/null and b/src/main/resources/assets/cosmiccore/textures/item/soul_reader.png differ