diff --git a/.gitignore b/.gitignore index b870e48..bac3312 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ run/logs run/schematics run/server run/downloads/log.json +run/command_history.txt diff --git a/AUDIT_NEOFORGE.md b/AUDIT_NEOFORGE.md new file mode 100644 index 0000000..9fa32ba --- /dev/null +++ b/AUDIT_NEOFORGE.md @@ -0,0 +1,410 @@ +# Audit de code — Migration Forge → NeoForge (CreateNuclearNeoForge) + +Document de suivi vivant. À mettre à jour au fur et à mesure que les points listés sont traités. +Périmètre : intégralité de `src/main/java` (207 fichiers, ~19 500 lignes), à l'exclusion des ressources/datagen JSON. +Rappel du cadre : le mod est **en cours de migration** de Forge vers NeoForge 1.21.1. Les usages de patterns Forge qui fonctionnent correctement et ne sont pas explicitement temporaires ne sont **pas** listés comme problème. Les nouvelles fonctionnalités propres à NeoForge 1.21.1 non encore adoptées ne sont **pas** considérées comme un manque. + +Légende priorité : 🔴 Critique · 🟠 Important · 🟡 Moyen · 🟢 Faible + +--- + +## Sommaire + +1. [Bugs fonctionnels critiques découverts pendant l'audit](#0-bugs-fonctionnels-critiques-découverts-pendant-laudit) +2. [Dead Code](#1-dead-code) +3. [Commentaires et Javadocs](#2-commentaires-et-javadocs) +4. [Duplications](#3-duplications) +5. [Migration Forge → NeoForge](#4-migration-forge--neoforge) +6. [Nettoyage](#5-nettoyage) +7. [Refactorisations](#6-refactorisations) +8. [Tableau de priorités global](#7-tableau-de-priorités-global) + +--- + +## 0. Bugs de logique indépendants de la migration + +Le mod étant encore en migration, une bonne partie du code manquant/commenté relevé dans ce rapport (ex. `ReactorAlarmManager`, `ReactorSummaryDisplaySource`, cf. §4) reflète simplement un travail inachevé et n'a **pas** été classée ici — ces cas sont traités dans la section [4. Migration Forge → NeoForge](#4-migration-forge--neoforge) et [1. Dead Code](#1-dead-code) comme du code à terminer, pas comme des anomalies isolées. + +En revanche, les points ci-dessous sont de **vraies erreurs de logique** (inversion de paramètres, copier-coller fautif, condition inversée) qui ne s'expliquent pas par du code manquant côté migration : ils resteraient des bugs même une fois la migration terminée, et méritent donc un traitement indépendant du planning de migration. + +| # | Fichier:ligne | Problème | Priorité | +|---|---|---|---| +| B1 | `content/multiblock/bluePrintItem/ReactorBluePrintItemScreen.java:56-65,71-73` | Les comptes de barres uranium/graphite sont inversés et un `+3` magique est ajouté lors de l'appel à `sendValueUpdate`, puis `sendValueUpdate` renvoie `countGraphiteRod` deux fois au lieu de `countUraniumRod` pour le dernier paramètre : le comptage des barres envoyé au serveur est corrompu. | 🔴 | +| B2 | `content/radiation/capability/RadiationCapability.java:220-223` | Dans `applyEffects`, la branche `radiationLevel3` et la branche `else` utilisent toutes deux `amplifierLevel2.get()` (copier-coller) : il manque un `amplifierLevel3`, l'amplificateur d'effet plafonne au niveau 2 quel que soit le niveau réel de radiation. | 🟠 | +| B3 | `content/multiblock/controller/manager/ReactorInputFluidManager.java:129-130,148-149` | `int tank = h.getTanks(); h.getFluidInTank(tank);` utilise le *nombre* de tanks comme *index* (valides : `0..getTanks()-1`) → accès systématiquement hors bornes. | 🟠 | +| B4 | `lib/multiblock/manager/MultiBlockCache.java:22-24` | `isCached()` a une sémantique inversée : `return cachedResult == null` renvoie `true` quand **rien n'est en cache**. (Classe par ailleurs non utilisée, cf. §1.) | 🟡 | +| B5 | `content/multiblock/controller/ReactorControllerBlockEntity.java:489-491` | `isEmptyConfiguredPattern()` retourne `!configuredPattern.isEmpty()` — le nom est l'inverse exact du comportement. Fonctionne car l'appelant (`tick()`) compense, mais piège de maintenance. | 🟠 | + +### Code manquant/inachevé lié à un travail en cours (à titre indicatif, non compté comme bug) + +Ces trois cas ont une logique entièrement commentée ou un comportement neutralisé, mais rien n'indique que ce soit lié à la migration Forge→NeoForge spécifiquement (plutôt une fonctionnalité en cours de développement) — ils sont donc listés dans les sections thématiques appropriées plutôt qu'ici : `ReactorAlarmManager.clearInvalid()/getBlocksPosition()` (§1.6, purge/récupération des alarmes non finalisée), `ReactorSummaryDisplaySource` (§1.6, alimentation du résumé du réacteur non finalisée — provoque une exception tant que ce n'est pas terminé), `IrradiatedCatRelaxOnOwnerGoal.canUse()` (§1, comportement du chat jamais terminé). + +--- + +## 1. Dead Code + +### 1.1 Classes inutilisées + +| Fichier | Détail | Priorité | +|---|---|---| +| `lib/multiblock/manager/MultiBlockManager.java` | Jamais référencée hors du fichier ; remplacée de facto par `MultiBlockManagerBeta`. | 🟠 | +| `lib/multiblock/manager/RegisteredMultiBlockPattern.java` | Utilisée uniquement par `MultiBlockManager`, donc morte par transitivité. | 🟡 | +| `lib/multiblock/manager/MultiBlockCache.java` | Jamais instanciée nulle part (voir aussi bug B7). | 🟠 | +| `lib/multiblock/impl/IBetterPattern.java` | Jamais utilisée ; stub cassé (`matches` → toujours `false`, `matchesWithResult` → toujours `null`, `construct` → no-op). | 🟠 | +| `foundation/block/EventTriggerBlock.java` | Seul point d'enregistrement (`CNBlocks.java:636-640`) commenté ; classe jamais instanciée en jeu. | 🟡 | +| `content/contraptions/irradiated/wolf/IrradiatedWoldCollarLayer.java` | Jamais instanciée/enregistrée ; `render()` a un corps vide ; nom mal orthographié (« Wold » au lieu de « Wolf »). | 🟠 | +| `foundation/mixin/client/RadiationHeartMixing.java` | Classe entière encapsulée dans un commentaire bloc, non listée dans `createnuclear.neoforge.mixins.json` (qui ne référence que `BaseFireBlockMixin`) : totalement inactive. | 🟠 | +| `foundation/events/possible code` | Fichier **sans extension `.java`**, donc jamais compilé mais versionné dans le repo. Contient du code Forge legacy (`net.minecraftforge.*`) et un `HudOverlayRegistry` fictif basé sur `ServiceLoader`. Brouillon oublié. | 🔴 | +| `content/multiblock/pattern/ReactorPattern.java` (`VerifyPattern5x5/7x7/9x9`, l.34-92) | Jamais référencées ; le vrai mécanisme passe par `CNMultiblock.REGISTRATE_MULTIBLOCK`. Ancienne implémentation abandonnée. | 🟡 | + +### 1.2 Méthodes inutilisées + +| Fichier:ligne | Détail | Priorité | +|---|---|---| +| `content/multiblock/controller/ReactorControllerBlockEntity.java:618-635` | `convertePattern(CompoundTag)` : variable `list` non utilisée, retourne toujours `null`, jamais appelée. | 🟡 | +| `content/multiblock/controller/ReactorControllerBlockEntity.java:637-654` | `FindController(char)` privée jamais appelée ; duplique le pattern statique de `CNMultiblock`. | 🟡 | +| `content/multiblock/core/ReactorCoreEntity.java:51-68` | Idem : `FindController(char)` jamais appelée. | 🟡 | +| `content/multiblock/input/item/ReactorInputEntity.java:73-90` | Idem : `FindController(char)` jamais appelée. | 🟡 | +| `content/multiblock/ReactorAssembler.java:132-136` | `getPlayersInRadius(...)` annotée `@Deprecated`, jamais appelée. | 🟢 | +| `content/multiblock/alarm/ReactorAlarmEntity.java:98-100` | `setController(...)` jamais appelée. | 🟢 | +| `foundation/utility/CreateNuclearLang.java:67-70` | `temporaryText(String)`, `@Deprecated`, aucun appelant. | 🟢 | +| `foundation/utility/TextUtils.java:45-75,116-119` | `renderMultilineDebugText`, `renderDebugText`, `translateWithFormatting`, `leftPad` : aucun appelant dans le code. | 🟢 | +| `net/nuclearteam/createnuclear/CNDamageTypes.java:10-16` | Méthode privée `key(String)` jamais appelée (`bootstrap()` vide). | 🟢 | + +### 1.3 Champs inutilisés + +| Fichier:ligne | Détail | Priorité | +|---|---|---| +| `content/multiblock/controller/ReactorControllerBlockEntity.java:60` | `public boolean test = true;` — champ de debug jamais lu ailleurs. | 🟡 | +| `content/multiblock/controller/ReactorControllerBlockEntity.java:70-71` | `public State powered = State.OFF;` assigné mais jamais lu ailleurs. | 🟢 | +| `content/multiblock/alarm/ReactorAlarmEntity.java:21` | Champ public `controller` jamais lu (hors `setController` lui-même mort). | 🟢 | +| `content/multiblock/IHeat.java:33-37` | Champ `intColor` et constructeur associé jamais utilisés (tous les enum values utilisent le constructeur `ChatFormatting`). | 🟢 | +| `net/nuclearteam/createnuclear/CNRecipeTypes.java:43,59,69,74` | Champ `isProcessingRecipe` assigné mais jamais lu. | 🟡 | +| `net/nuclearteam/createnuclear/CNAdvancement.java:46` | `public static final CreateNuclearAdvancement START = null,` jamais utilisée ailleurs. | 🟢 | +| `content/effects/VicinityEffect.java:22-27` | Paramètre constructeur `Consumer T build(IPatternBuilder builder){ data.forEach((k,v)-> list.addAll(v)); return builder.make(list, lookup, blockProvider); } - + public BlockPos getDistanceController(char character) { var data = Util.parseBlockPattern(pattern, lookup.keySet()); var coreList = data.get(character); diff --git a/src/main/java/lib/multiblock/SimpleMultiBlockPattern.java b/src/main/java/lib/multiblock/SimpleMultiBlockPattern.java index d67ad5c..e737186 100644 --- a/src/main/java/lib/multiblock/SimpleMultiBlockPattern.java +++ b/src/main/java/lib/multiblock/SimpleMultiBlockPattern.java @@ -18,20 +18,19 @@ import java.util.function.Predicate; import java.util.function.Supplier; -public final class SimpleMultiBlockPattern implements IMultiBlockPattern { - private final List multiBlockOffsetPosList; - private final Map> predicateHashMap; - private final Map> blockProvider; +public record SimpleMultiBlockPattern(List multiBlockOffsetPosList, + Map> predicateHashMap, + Map> blockProvider) implements IMultiBlockPattern { - public SimpleMultiBlockPattern(List multiBlockOffsetPosList, Map> predicateMap, Map> blockSuppliers) { + public SimpleMultiBlockPattern(List multiBlockOffsetPosList, Map> predicateHashMap, Map> blockProvider) { this.multiBlockOffsetPosList = List.copyOf(multiBlockOffsetPosList); - this.predicateHashMap = Map.copyOf(predicateMap); - this.blockProvider = Map.copyOf(blockSuppliers); + this.predicateHashMap = Map.copyOf(predicateHashMap); + this.blockProvider = Map.copyOf(blockProvider); } public boolean matches(Level level, BlockPos blockPos, Rotation rotation) { for (MultiBlockOffsetPos multiBlockOffsetPos : multiBlockOffsetPosList) { - char character = multiBlockOffsetPos.caracter(); + char character = multiBlockOffsetPos.character(); Predicate predicate = predicateHashMap.get(character); BlockInWorld block = new BlockInWorld(level, blockPos.offset(multiBlockOffsetPos.pos().rotate(rotation)), false); @@ -43,7 +42,7 @@ public boolean matches(Level level, BlockPos blockPos, Rotation rotation) { public MultiblockMatchResult matchesWithResult(Level level, BlockPos blockPos, Rotation rotation) { List result = new ArrayList<>(); for (MultiBlockOffsetPos multiBlockOffsetPos : multiBlockOffsetPosList) { - char character = multiBlockOffsetPos.caracter(); + char character = multiBlockOffsetPos.character(); Predicate predicate = predicateHashMap.get(character); BlockInWorld block = new BlockInWorld(level, blockPos.offset(multiBlockOffsetPos.pos().rotate(rotation)), false); if (predicate == null || !predicate.test(block)) return null; @@ -58,22 +57,14 @@ public void construct(Level level, BlockPos blockPos, BiPredicate stateSupplier = blockProvider.get(character); if (stateSupplier != null) { var pos = blockPos.offset(multiBlockOffsetPos.pos().rotate(Rotation.NONE)); var state = stateSupplier.get(); - if (stateBiPredicate.test(character, state)) level.getServer().tell(new TickTask(3, () -> level.setBlock(pos, state, Block.UPDATE_ALL))); + if (stateBiPredicate.test(character, state)) + level.getServer().tell(new TickTask(3, () -> level.setBlock(pos, state, Block.UPDATE_ALL))); } } } - - @Override - public boolean matches(java.util.logging.Level level, BlockPos pos, Rotation rotation) { - return false; - } - - public List test() { - return this.multiBlockOffsetPosList; - } } diff --git a/src/main/java/lib/multiblock/SimpleMultiBlockPatternBuilder.java b/src/main/java/lib/multiblock/SimpleMultiBlockPatternBuilder.java deleted file mode 100644 index e7a56b7..0000000 --- a/src/main/java/lib/multiblock/SimpleMultiBlockPatternBuilder.java +++ /dev/null @@ -1,68 +0,0 @@ -package lib.multiblock; - -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import lib.multiblock.impl.IMultiBlockPatternBuilder; -import lib.multiblock.impl.IMultiBlockPatternBuilder; -import lib.multiblock.impl.IPatternBuilder; -import lib.multiblock.misc.MultiBlockOffsetPos; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.pattern.BlockInWorld; -import lib.multiblock.impl.IMultiBlockPattern; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; -import java.util.function.Supplier; - -public final class SimpleMultiBlockPatternBuilder implements IMultiBlockPatternBuilder { - private static final Joiner COMA_SEPARATOR = Joiner.on(','); - public static SimpleMultiBlockPatternBuilder start() { - return new SimpleMultiBlockPatternBuilder(); - } - - private final Map> multiBlockOffsetPosList = Maps.newHashMap(); - private final Map> predicateHashMap = Maps.newHashMap(); - private final Map> blockProvider = Maps.newHashMap(); - - private SimpleMultiBlockPatternBuilder() {} - - private void ensureProperlyBuilt(List list) { - HashSet characters = new HashSet<>(); - list.forEach(multiBlockOffsetPos -> { - if (!predicateHashMap.containsKey(multiBlockOffsetPos.caracter())) - characters.add(multiBlockOffsetPos.caracter()); - }); - if (!characters.isEmpty()) - throw new IllegalStateException("Missing the following: \"%s\" for MultiBlock Pattern as they have not been defined".formatted(COMA_SEPARATOR.join(characters))); - } - - public SimpleMultiBlockPatternBuilder add(char character, BlockPos relativePos) { - multiBlockOffsetPosList.computeIfAbsent(character, ArrayList::new).add(new MultiBlockOffsetPos(character, relativePos)); - return this; - } - - public SimpleMultiBlockPatternBuilder where(char character, Predicate worldPredicate) { - if (!predicateHashMap.containsKey(character)) predicateHashMap.put(character, worldPredicate); - return this; - } - - public SimpleMultiBlockPatternBuilder block(char pSymbol, Supplier blockStateSupplier) { - blockProvider.put(pSymbol, blockStateSupplier); - return this; - } - - - public T build(IPatternBuilder builder) { - var coreList = multiBlockOffsetPosList.get('*'); - if (coreList == null || coreList.size() != 1) - throw new IllegalArgumentException("Failed to build pattern due to having more or less than one \"*\" defined! Have: %s Expected: 1".formatted(coreList != null ? coreList.size() : 0)); - List list = Lists.newArrayList(); - multiBlockOffsetPosList.forEach((k, v) -> list.addAll(v)); - return builder.make(list, predicateHashMap, blockProvider); - } -} diff --git a/src/main/java/lib/multiblock/impl/IBetterPattern.java b/src/main/java/lib/multiblock/impl/IBetterPattern.java deleted file mode 100644 index f992135..0000000 --- a/src/main/java/lib/multiblock/impl/IBetterPattern.java +++ /dev/null @@ -1,31 +0,0 @@ -package lib.multiblock.impl; - -import lib.multiblock.misc.MultiblockMatchResult; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; - -import java.util.function.BiPredicate; - -public class IBetterPattern implements IMultiBlockPattern { - @Override - public boolean matches(Level level, BlockPos pos, Rotation rotation) { - return false; - } - - @Override - public MultiblockMatchResult matchesWithResult(Level level, BlockPos pos, Rotation rotation) { - return null; - } - - @Override - public void construct(Level level, BlockPos pos, BiPredicate blockStateBiPredicate) { - - } - - @Override - public boolean matches(java.util.logging.Level level, BlockPos pos, Rotation rotation) { - return false; - } -} diff --git a/src/main/java/lib/multiblock/impl/IMultiBlockPattern.java b/src/main/java/lib/multiblock/impl/IMultiBlockPattern.java index 7453566..a33dee0 100644 --- a/src/main/java/lib/multiblock/impl/IMultiBlockPattern.java +++ b/src/main/java/lib/multiblock/impl/IMultiBlockPattern.java @@ -34,7 +34,4 @@ default MultiblockMatchResult matchesWithResult(Level level, BlockPos pos) { default void contruct(Level level, BlockPos pos) { construct(level, pos, (a,b) -> true); } - - - boolean matches(java.util.logging.Level level, BlockPos pos, Rotation rotation); -} +} \ No newline at end of file diff --git a/src/main/java/lib/multiblock/impl/IMultiBlockPatternBuilder.java b/src/main/java/lib/multiblock/impl/IMultiBlockPatternBuilder.java index 17c87dd..b5c34c5 100644 --- a/src/main/java/lib/multiblock/impl/IMultiBlockPatternBuilder.java +++ b/src/main/java/lib/multiblock/impl/IMultiBlockPatternBuilder.java @@ -2,6 +2,7 @@ import lib.multiblock.SimpleMultiBlockPattern; +@SuppressWarnings("unchecked") public interface IMultiBlockPatternBuilder { < T extends IMultiBlockPattern> T build(IPatternBuilder builder); diff --git a/src/main/java/lib/multiblock/impl/IPatternBuilder.java b/src/main/java/lib/multiblock/impl/IPatternBuilder.java index 53c1f00..ca33951 100644 --- a/src/main/java/lib/multiblock/impl/IPatternBuilder.java +++ b/src/main/java/lib/multiblock/impl/IPatternBuilder.java @@ -11,4 +11,4 @@ public interface IPatternBuilder { T make(List multiBlockOffsetPos, Map> predicateMap, Map> blockSuppliers); -} +} \ No newline at end of file diff --git a/src/main/java/lib/multiblock/manager/MultiBlockCache.java b/src/main/java/lib/multiblock/manager/MultiBlockCache.java deleted file mode 100644 index 5a2c97f..0000000 --- a/src/main/java/lib/multiblock/manager/MultiBlockCache.java +++ /dev/null @@ -1,25 +0,0 @@ -package lib.multiblock.manager; - -import lib.multiblock.impl.IMultiBlockPattern; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.block.Rotation; - -import java.util.logging.Level; - -public class MultiBlockCache { - private T cachedResult; - public MultiBlockCache(){} - - public void updateStructure(T blockPattern) { - this.cachedResult = blockPattern; - } - - public boolean isValid(Level level, BlockPos pos, Rotation rotation) { - return cachedResult != null && cachedResult.matches(level, pos, rotation); - } - - public boolean isCached() { - return cachedResult == null; - } -} diff --git a/src/main/java/lib/multiblock/manager/MultiBlockManager.java b/src/main/java/lib/multiblock/manager/MultiBlockManager.java deleted file mode 100644 index dd8dbf4..0000000 --- a/src/main/java/lib/multiblock/manager/MultiBlockManager.java +++ /dev/null @@ -1,27 +0,0 @@ -package lib.multiblock.manager; - -import lib.multiblock.impl.IMultiBlockPattern; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Rotation; - -import java.util.ArrayList; - -public class MultiBlockManager { - private final ArrayList> registeredMultiBlockPatterns = new ArrayList>(); - - public MultiBlockManager(){} - - public E register(String ID, T data, E blockPattern) { - registeredMultiBlockPatterns.add(new RegisteredMultiBlockPattern<>(ID, data, blockPattern)); - return blockPattern; - } - - public RegisteredMultiBlockPattern findStructure(Level level, BlockPos pos, Rotation rotation) { - for (RegisteredMultiBlockPattern registeredMultiBlockPattern : registeredMultiBlockPatterns) { - var result = registeredMultiBlockPattern.pattern().matches(level, pos, rotation); - if(result) return registeredMultiBlockPattern; - } - return null; - } -} diff --git a/src/main/java/lib/multiblock/manager/RegisteredMultiBlockPattern.java b/src/main/java/lib/multiblock/manager/RegisteredMultiBlockPattern.java deleted file mode 100644 index 264af6d..0000000 --- a/src/main/java/lib/multiblock/manager/RegisteredMultiBlockPattern.java +++ /dev/null @@ -1,6 +0,0 @@ -package lib.multiblock.manager; - -import lib.multiblock.impl.IMultiBlockPattern; - -public record RegisteredMultiBlockPattern (String ID, T data, IMultiBlockPattern pattern) { -} diff --git a/src/main/java/lib/multiblock/misc/MultiBlockOffsetPos.java b/src/main/java/lib/multiblock/misc/MultiBlockOffsetPos.java index a388a68..11801a7 100644 --- a/src/main/java/lib/multiblock/misc/MultiBlockOffsetPos.java +++ b/src/main/java/lib/multiblock/misc/MultiBlockOffsetPos.java @@ -2,5 +2,5 @@ import net.minecraft.core.BlockPos; -public record MultiBlockOffsetPos(char caracter, BlockPos pos) { +public record MultiBlockOffsetPos(char character, BlockPos pos) { } diff --git a/src/main/java/lib/multiblock/misc/Util.java b/src/main/java/lib/multiblock/misc/Util.java index 81eb1ef..184f034 100644 --- a/src/main/java/lib/multiblock/misc/Util.java +++ b/src/main/java/lib/multiblock/misc/Util.java @@ -20,14 +20,12 @@ public static Map> parseBlockPattern(List> blockOffsets = new HashMap<>(); - int starX = -1; - int starY = -1; - int starZ = -1; + int starX = -1, starY = -1, starZ = -1; for (int y = 0; y < aisles.size(); y++) { String[] aisle = aisles.get(y); - for (int x=0; x < aisle.length; x++) { + for (int x = 0; x < aisle.length; x++) { for (int z = 0; z < aisle[x].length(); z++) { if (aisle[x].charAt(z) == '*') { starX = x; diff --git a/src/main/java/net/nuclearteam/createnuclear/CNAttachmentTypes.java b/src/main/java/net/nuclearteam/createnuclear/CNAttachmentTypes.java new file mode 100644 index 0000000..f23287d --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/CNAttachmentTypes.java @@ -0,0 +1,25 @@ +package net.nuclearteam.createnuclear; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.attachment.AttachmentType; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.NeoForgeRegistries; +import net.nuclearteam.createnuclear.content.radiation.capability.RadiationCapability; + +import java.util.function.Supplier; + +public class CNAttachmentTypes { + public static final DeferredRegister> ATTACHMENT_TYPES = DeferredRegister.create(NeoForgeRegistries.Keys.ATTACHMENT_TYPES, CreateNuclear.MOD_ID); + + public static final Supplier> RADIATION = ATTACHMENT_TYPES.register("radiation", () -> AttachmentType + .builder(RadiationCapability::new) + .serialize(RadiationCapability.CODEC) + .sync(RadiationCapability.STREAM_CODEC) + .build() + ); + + public static void register(IEventBus modEventBus) { + ATTACHMENT_TYPES.register(modEventBus); + } + +} diff --git a/src/main/java/net/nuclearteam/createnuclear/CNAttributes.java b/src/main/java/net/nuclearteam/createnuclear/CNAttributes.java new file mode 100644 index 0000000..136a500 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/CNAttributes.java @@ -0,0 +1,19 @@ +package net.nuclearteam.createnuclear; + +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.RangedAttribute; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredRegister; + +import net.neoforged.neoforge.registries.DeferredHolder; + +public class CNAttributes { + public static final DeferredRegister ATTRIBUTES = DeferredRegister.create(Registries.ATTRIBUTE, CreateNuclear.MOD_ID); + + public static final DeferredHolder IRRADIATED_RESISTANCE = ATTRIBUTES.register("generic.irradiated_resistance", () -> new RangedAttribute("attribute.name.createnuclear.generic.irradiated_resistance", 0, 0, 1).setSyncable(true)); + + public static void register(IEventBus eventBus) { + ATTRIBUTES.register(eventBus); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/CNBlockEntityTypes.java b/src/main/java/net/nuclearteam/createnuclear/CNBlockEntityTypes.java index f8a469c..ca312d4 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNBlockEntityTypes.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNBlockEntityTypes.java @@ -4,10 +4,12 @@ import com.simibubi.create.content.kinetics.base.OrientedRotatingVisual; import com.tterrag.registrate.util.entry.BlockEntityEntry; import net.nuclearteam.createnuclear.content.enriching.campfire.EnrichingCampfireBlockEntity; +import net.nuclearteam.createnuclear.content.multiblock.alarm.ReactorAlarmEntity; import net.nuclearteam.createnuclear.content.multiblock.casing.ReactorCasingEntity; import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; import net.nuclearteam.createnuclear.content.multiblock.core.ReactorCoreEntity; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.ReactorFluidInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInputEntity; import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutputEntity; import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutputRenderer; @@ -27,9 +29,14 @@ public class CNBlockEntityTypes { .validBlocks(CNBlocks.REACTOR_CORE) .register(); - public static final BlockEntityEntry REACTOR_INPUT = - CreateNuclear.REGISTRATE.blockEntity("reactor_input", ReactorInputEntity::new) - .validBlocks(CNBlocks.REACTOR_INPUT) + public static final BlockEntityEntry REACTOR_INPUT = + CreateNuclear.REGISTRATE.blockEntity("reactor_input", ReactorRodInputEntity::new) + .validBlocks(CNBlocks.REACTOR_ROD_INPUT) + .register(); + + public static final BlockEntityEntry REACTOR_FLUID_INPUT = + CreateNuclear.REGISTRATE.blockEntity("reactor_fluid_input", ReactorFluidInputEntity::new) + .validBlocks(CNBlocks.REACTOR_FLUID_INPUT) .register(); public static final BlockEntityEntry REACTOR_OUTPUT = @@ -44,5 +51,10 @@ public class CNBlockEntityTypes { .validBlocks(CNBlocks.REACTOR_CONTROLLER) .register(); + public static final BlockEntityEntry REACTOR_ALARM = + CreateNuclear.REGISTRATE.blockEntity("reactor_alarm", ReactorAlarmEntity::new) + .validBlocks(CNBlocks.REACTOR_ALARM) + .register(); + public static void register() {} } diff --git a/src/main/java/net/nuclearteam/createnuclear/CNBlocks.java b/src/main/java/net/nuclearteam/createnuclear/CNBlocks.java index 4245ac6..5fa103a 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNBlocks.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNBlocks.java @@ -9,7 +9,6 @@ import com.tterrag.registrate.providers.RegistrateRecipeProvider; import com.tterrag.registrate.providers.loot.RegistrateBlockLootTables; import com.tterrag.registrate.util.entry.BlockEntry; -import net.minecraft.client.renderer.RenderType; import net.minecraft.core.Direction; import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.Registries; @@ -31,11 +30,14 @@ import net.minecraft.world.level.material.MapColor; import net.minecraft.world.level.storage.loot.entries.LootItem; import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount; +import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction; +import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; import net.neoforged.neoforge.client.model.generators.ConfiguredModel; import net.neoforged.neoforge.client.model.generators.ModelFile; import net.neoforged.neoforge.common.Tags; import net.nuclearteam.createnuclear.content.enriching.campfire.EnrichingCampfireBlock; import net.nuclearteam.createnuclear.content.enriching.fire.EnrichingFireBlock; +import net.nuclearteam.createnuclear.content.multiblock.alarm.ReactorAlarm; import net.nuclearteam.createnuclear.content.multiblock.casing.ReactorCasing; import net.nuclearteam.createnuclear.CNTags.CNBlockTags; import net.nuclearteam.createnuclear.CNTags.CNItemTags; @@ -44,11 +46,13 @@ import net.nuclearteam.createnuclear.content.multiblock.core.ReactorCore; import net.nuclearteam.createnuclear.content.multiblock.frame.ReactorFrame; import net.nuclearteam.createnuclear.content.multiblock.frame.ReactorframeItem; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInput; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInputGenerator; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.ReactorFluidInput; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.ReactorFluidInputGenerator; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInput; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInputGenerator; import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutput; import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutputGenerator; -import net.nuclearteam.createnuclear.content.multiblock.reactorCooler.ReactorCooler; +import net.nuclearteam.createnuclear.content.multiblock.cooler.ReactorCooler; import net.nuclearteam.createnuclear.content.multiblock.reinforced.ReinforcedGlassBlock; import net.nuclearteam.createnuclear.content.uraniumOre.UraniumOreBlock; @@ -99,7 +103,6 @@ public class CNBlocks { CreateNuclear.REGISTRATE.block("reactor_frame", ReactorFrame::new) .initialProperties(SharedProperties::stone) .properties(p -> p.explosionResistance(3F).destroyTime(2F)) - .addLayer(() -> RenderType::cutoutMipped) .transform(pickaxeOnly()) .tag(BlockTags.NEEDS_DIAMOND_TOOL) .blockstate((c, p) -> @@ -140,19 +143,30 @@ public class CNBlocks { .register(); - public static final BlockEntry REACTOR_INPUT = - CreateNuclear.REGISTRATE.block("reactor_input", ReactorInput::new) + public static final BlockEntry REACTOR_ROD_INPUT = + CreateNuclear.REGISTRATE.block("reactor_rod_input", ReactorRodInput::new) .initialProperties(SharedProperties::stone) .properties(p -> p.explosionResistance(6F)) .properties(p -> p.destroyTime(2F)) - .addLayer(() -> RenderType::cutoutMipped) .transform(pickaxeOnly()) .tag(BlockTags.NEEDS_DIAMOND_TOOL) - .blockstate(new ReactorInputGenerator()::generate) + .blockstate(new ReactorRodInputGenerator()::generate) .item() - .transform(customItemModel("reactor", "input", "item")) + .transform(customItemModel("reactor", "rod_input", "item")) .register(); + public static final BlockEntry REACTOR_FLUID_INPUT = + CreateNuclear.REGISTRATE.block("reactor_fluid_input", ReactorFluidInput::new) + .initialProperties(SharedProperties::stone) + .properties(p -> p.explosionResistance(6F)) + .properties(p -> p.destroyTime(2F)) + .transform(pickaxeOnly()) + .tag(BlockTags.NEEDS_DIAMOND_TOOL) + .blockstate(new ReactorFluidInputGenerator()::generate) + .item() + .transform(customItemModel("reactor", "fluid_input", "item")) + .register(); + public static final BlockEntry REACTOR_OUTPUT = CreateNuclear.REGISTRATE.block("reactor_output", ReactorOutput::new) .properties(p -> p.explosionResistance(6F).destroyTime(4F)) @@ -178,6 +192,14 @@ public class CNBlocks { .transform(customItemModel("reactor", "controller", "item")) .register(); + public static final BlockEntry REACTOR_ALARM = + CreateNuclear.REGISTRATE.block("reactor_alarm", ReactorAlarm::new) + .initialProperties(SharedProperties::stone) + .transform(pickaxeOnly()) + .item() + .build() + .register(); + public static final BlockEntry REINFORCED_GLASS = CreateNuclear.REGISTRATE .block("reinforced_glass", ReinforcedGlassBlock::new) .initialProperties(() -> Blocks.GLASS) @@ -200,10 +222,10 @@ public class CNBlocks { .withExistingParent("reinforced_glass",ResourceLocation.withDefaultNamespace("block/cube_all")) .texture("all", p.modLoc("block/reactor/reinforced/glass")) .texture("particle", p.modLoc("block/reactor/reinforced/glass")) + .renderType("translucent") ) .build()) ) - .addLayer(() -> RenderType::translucent) .item() .tag(CNTags.forgeItemTag("glass_blocks")) .build() @@ -220,7 +242,6 @@ public class CNBlocks { .properties(EnrichingFireBlock.getLight()) .tag(CNBlockTags.FAN_PROCESSING_CATALYSTS_ENRICHED.tag) .loot((lt, b) -> lt.add(b, BlockLootSubProvider.noDrop())) - .addLayer(() -> RenderType::cutout) .blockstate((c, p) -> { String baseFolder = "block/enriching/fire/"; ModelFile Floor0 = p.models().getExistingFile(p.modLoc(baseFolder + "floor0")); @@ -281,7 +302,6 @@ public class CNBlocks { .lightLevel(EnrichingCampfireBlock::getLight)) .properties(Properties::noOcclusion) .properties(Properties::ignitedByLava) - .addLayer(() -> RenderType::cutoutMipped) .transform(axeOrPickaxe()) .tag(BlockTags.CAMPFIRES) .recipe((c, p) -> ShapedRecipeBuilder.shaped(RecipeCategory.MISC, c.get()) @@ -485,6 +505,124 @@ public class CNBlocks { .build() .register(); + public static final BlockEntry THORIUM_ORE = + CreateNuclear.REGISTRATE.block("thorium_ore", Block::new) + .initialProperties(SharedProperties::stone) + .simpleItem() + .transform(pickaxeOnly()) + .loot((lt, b) -> lt.add(b, + lt.createSilkTouchDispatchTable(b, + lt.applyExplosionDecay(b, LootItem.lootTableItem(CNItems.RAW_THORIUM) + .apply(SetItemCountFunction.setCount(UniformGenerator.between(2.0F, 5.0F))) + .apply(ApplyBonusCount.addOreBonusCount(lt.getRegistries().holderOrThrow(Enchantments.FORTUNE))) )))) + .tag(BlockTags.NEEDS_IRON_TOOL, + CNTags.forgeBlockTag("ores"), + CNTags.forgeBlockTag("ores_in_ground/stone"), + CNTags.forgeBlockTag("ores/thorium") + ) + .item() + .tag(CNTags.forgeItemTag("ores/thorium")) + .build() + .register(); + + public static final BlockEntry DEEPSLATE_THORIUM_ORE = + CreateNuclear.REGISTRATE.block("deepslate_thorium_ore", Block::new) + .initialProperties(() -> Blocks.DIAMOND_ORE) + .simpleItem() + .transform(pickaxeOnly()) + .loot((lt, b) -> lt.add(b, + lt.createSilkTouchDispatchTable(b, + lt.applyExplosionDecay(b, LootItem.lootTableItem(CNItems.RAW_THORIUM) + .apply(SetItemCountFunction.setCount(UniformGenerator.between(2.0F, 5.0F))) + .apply(ApplyBonusCount.addUniformBonusCount(lt.getRegistries().holderOrThrow(Enchantments.FORTUNE), 5)) + )))) + .tag(BlockTags.NEEDS_DIAMOND_TOOL, + CNTags.forgeBlockTag("ores"), + CNTags.forgeBlockTag("ores_in_ground/deepslate"), + CNTags.forgeBlockTag("ores/thorium") + ) + .item() + .tag(CNTags.forgeItemTag("ores/thorium")) + .build() + .register(); + + public static final BlockEntry NITRATE_ORE = + CreateNuclear.REGISTRATE.block("nitrate_ore", Block::new) + .initialProperties(SharedProperties::stone) + .transform(pickaxeOnly()) + .loot((lt, b) -> lt.add(b, + lt.createSilkTouchDispatchTable(b, + lt.applyExplosionDecay(b, LootItem.lootTableItem(CNItems.NITRATE) + .apply(ApplyBonusCount.addOreBonusCount(lt.getRegistries().holderOrThrow(Enchantments.FORTUNE))) + )))) + .tag(BlockTags.NEEDS_IRON_TOOL, + CNTags.forgeBlockTag("ores"), + CNTags.forgeBlockTag("ores_in_ground/stone"), + CNTags.forgeBlockTag("ores/nitrate") + ) + .item() + .tag(CNTags.forgeItemTag("ores/nitrate")) + .build() + .register(); + + public static final BlockEntry DEEPSLATE_NITRATE_ORE = + CreateNuclear.REGISTRATE.block("deepslate_nitrate_ore", Block::new) + .initialProperties(SharedProperties::stone) + .simpleItem() + .transform(pickaxeOnly()) + .loot((lt, b) -> lt.add(b, + lt.createSilkTouchDispatchTable(b, + lt.applyExplosionDecay(b, LootItem.lootTableItem(CNItems.NITRATE) + .apply(ApplyBonusCount.addOreBonusCount(lt.getRegistries().holderOrThrow(Enchantments.FORTUNE))) + )))) + .tag(BlockTags.NEEDS_IRON_TOOL, + CNTags.forgeBlockTag("ores"), + CNTags.forgeBlockTag("ores_in_ground/deepslate"), + CNTags.forgeBlockTag("ores/nitrate") + ) + .item() + .tag(CNTags.forgeItemTag("ores/nitrate")) + .build() + .register(); + + public static final BlockEntry RAW_THORIUM_BLOCK = + CreateNuclear.REGISTRATE.block("raw_thorium_block", Block::new) + .initialProperties(SharedProperties::stone) + .transform(pickaxeOnly()) + .tag(CNTags.forgeBlockTag("storage_blocks/raw_thorium")) + .recipe((c, p) -> ShapedRecipeBuilder.shaped(RecipeCategory.DECORATIONS, c.get()) + .unlockedBy("has_raw_materials_thorium", RegistrateRecipeProvider.has(CNTags.forgeItemTag("raw_materials/thorium"))) + .define('R', CNTags.forgeItemTag("raw_materials/thorium")) + .pattern("RRR") + .pattern("RRR") + .pattern("RRR") + .save(p, CreateNuclear.asResource("crafting/raw/" + c.getName()))) + .item() + .tag(CNTags.forgeItemTag("storage_blocks/raw_thorium")) + .build() + .register(); + + public static final BlockEntry THORIUM_BLOCK = + CreateNuclear.REGISTRATE.block("thorium_block", Block::new) + .initialProperties(SharedProperties::stone) + .transform(pickaxeOnly()) + .loot((lt, b) -> lt.add(b, + lt.createSilkTouchDispatchTable(b, + lt.applyExplosionDecay(b, LootItem.lootTableItem(CNItems.RAW_URANIUM) + .apply(ApplyBonusCount.addOreBonusCount(lt.getRegistries().holderOrThrow(Enchantments.FORTUNE))))))) + .tag(BlockTags.NEEDS_DIAMOND_TOOL, + BlockTags.NEEDS_IRON_TOOL, + CNTags.forgeBlockTag("ores"), + CNTags.forgeBlockTag("ores_in_ground/stone"), + CNTags.forgeBlockTag("ores/thorium"), + CNBlockTags.THORIUM_ORES.tag) + .item() + .tag(CNItemTags.THORIUM_ORES.tag, + CNTags.forgeItemTag("ores/thorium"), + CNTags.forgeItemTag("storage_blocks/thorium")) + .build() + .register(); + public static final BlockEntry STEEL_BLOCK = CreateNuclear.REGISTRATE.block("steel_block", Block::new) .initialProperties(SharedProperties::stone) diff --git a/src/main/java/net/nuclearteam/createnuclear/CNClientProxy.java b/src/main/java/net/nuclearteam/createnuclear/CNClientProxy.java new file mode 100644 index 0000000..c27ae50 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/CNClientProxy.java @@ -0,0 +1,68 @@ +package net.nuclearteam.createnuclear; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; + +@OnlyIn(Dist.CLIENT) +public class CNClientProxy { + + public static final ResourceLocation BOMB_FLASH = CreateNuclear.asResource("textures/misc/bomb_flash.png"); + + public static int muteNonNukeSoundsFor = 0; + public static int renderNukeFlashFor = 0; + public static int renderNukeSkyDarkFor = 0; + public static float prevNukeFlashAmount = 0; + public static float nukeFlashAmount = 0; + + public static int lastTremorTick = -1; + public static float[] randomTremorOffsets = new float[3]; + + public static final Int2ObjectMap ENTITY_SOUND_INSTANCE_MAP = new Int2ObjectOpenHashMap<>(); + + public static float getNukeFlashAmount(float partialTicks) { + return prevNukeFlashAmount + (nukeFlashAmount - prevNukeFlashAmount) * partialTicks; + } + + public static void preScreenRender(float partialTick) { + float screenEffectIntensity = Minecraft.getInstance().options.screenEffectScale().get().floatValue(); + float currentNukeFlash = getNukeFlashAmount(partialTick); + + if (currentNukeFlash > 0 && CNConfigs.client().nuclearBombFlash.get()) { + int screenWidth = Minecraft.getInstance().getWindow().getGuiScaledWidth(); + int screenHeight = Minecraft.getInstance().getWindow().getGuiScaledHeight(); + + RenderSystem.disableDepthTest(); + RenderSystem.depthMask(false); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, currentNukeFlash * screenEffectIntensity); + RenderSystem.setShaderTexture(0, BOMB_FLASH); + + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder bufferbuilder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); + bufferbuilder.addVertex(0.0F, (float)screenHeight, -90.0F).setUv(0.0F, 1.0F); + bufferbuilder.addVertex((float)screenWidth, (float)screenHeight, -90.0F).setUv(1.0F, 1.0F); + bufferbuilder.addVertex((float)screenWidth, 0.0F, -90.0F).setUv(1.0F, 0.0F); + bufferbuilder.addVertex(0.0F, 0.0F, -90.0F).setUv(0.0F, 0.0F); + BufferUploader.drawWithShader(bufferbuilder.buildOrThrow()); + + RenderSystem.depthMask(true); + RenderSystem.enableDepthTest(); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + } + } + + public static boolean isFarFromCamera(double x, double y, double z) { + return Minecraft.getInstance().gameRenderer.getMainCamera().getPosition().distanceToSqr(x, y, z) >= 256.0D; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/CNDamageTypes.java b/src/main/java/net/nuclearteam/createnuclear/CNDamageTypes.java index 91640cf..6cf5252 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNDamageTypes.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNDamageTypes.java @@ -3,7 +3,9 @@ import net.minecraft.core.registries.Registries; import net.minecraft.data.worldgen.BootstrapContext; import net.minecraft.resources.ResourceKey; +import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageType; +import net.minecraft.world.level.Level; @SuppressWarnings("unused") public class CNDamageTypes { @@ -11,7 +13,15 @@ private static ResourceKey key(String name) { return ResourceKey.create(Registries.DAMAGE_TYPE, CreateNuclear.asResource(name)); } - public static void bootstrap(BootstrapContext ctx) { + public static final ResourceKey RADIATION = key("radiation"); + public static final ResourceKey FAN_RADIATION = key("fan_radiation"); + + public static DamageSource radiation(Level level) { + return new DamageSource(level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(RADIATION)); + } + public static void bootstrap(BootstrapContext ctx) { + ctx.register(RADIATION, new DamageType("radiation", 0.1F)); + ctx.register(FAN_RADIATION, new DamageType("fan_radiation", 0.1F)); } } diff --git a/src/main/java/net/nuclearteam/createnuclear/CNDataComponents.java b/src/main/java/net/nuclearteam/createnuclear/CNDataComponents.java index fc95f9e..aa4a936 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNDataComponents.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNDataComponents.java @@ -1,13 +1,20 @@ package net.nuclearteam.createnuclear; -import com.simibubi.create.Create; +import com.mojang.serialization.Codec; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.ExtraCodecs; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; import net.neoforged.neoforge.registries.DeferredRegister; +import net.nuclearteam.createnuclear.content.equipment.cloth.ClothItem.Cloths; import net.nuclearteam.createnuclear.content.multiblock.bluePrintItem.ReactorBluePrintData; import org.jetbrains.annotations.ApiStatus; @@ -51,6 +58,37 @@ public class CNDataComponents { builder -> builder.persistent(ExtraCodecs.NON_NEGATIVE_INT).networkSynchronized(ByteBufCodecs.INT) ); + public static final DataComponentType CLOTH_COLOR = register( + "cloth_color", + b -> b.persistent(StringRepresentable.fromEnum(Cloths::values)).networkSynchronized(NeoForgeStreamCodecs.enumCodec(Cloths.class)) + ); + + public static final DataComponentType CLOTH_ITEM = register( + "cloth_item", + b -> b.persistent(ClothItemStack.CODEC).networkSynchronized(ClothItemStack.STREAM_CODEC) + ); + + /** + * Wraps an ItemStack with content-based equals/hashCode, since ItemStack itself only offers + * identity equality and NeoForge requires data component values to implement both properly. + */ + public record ClothItemStack(ItemStack stack) { + public static final Codec CODEC = ItemStack.CODEC.xmap(ClothItemStack::new, ClothItemStack::stack); + public static final StreamCodec STREAM_CODEC = ItemStack.STREAM_CODEC.map(ClothItemStack::new, ClothItemStack::stack); + + @Override + public boolean equals(Object other) { + return other instanceof ClothItemStack( + ItemStack stack1 + ) && ItemStack.isSameItemSameComponents(this.stack, stack1); + } + + @Override + public int hashCode() { + return ItemStack.hashItemAndComponents(this.stack); + } + } + private static DataComponentType register(String name, UnaryOperator> builder) { DataComponentType type = builder.apply(DataComponentType.builder()).build(); DATA_COMPONENTS.register(name, () -> type); diff --git a/src/main/java/net/nuclearteam/createnuclear/CNEffects.java b/src/main/java/net/nuclearteam/createnuclear/CNEffects.java index 1b81995..444a749 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNEffects.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNEffects.java @@ -2,13 +2,13 @@ import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredRegister; -import net.nuclearteam.createnuclear.content.effects.RadiationEffect; +import net.nuclearteam.createnuclear.content.effects.IodineEffect; +import net.nuclearteam.createnuclear.content.radiation.RadiationEffect; public class CNEffects { public static final DeferredRegister EFFECTS = DeferredRegister.create(BuiltInRegistries.MOB_EFFECT, CreateNuclear.MOD_ID); @@ -18,6 +18,9 @@ public class CNEffects { .addAttributeModifier(Attributes.MOVEMENT_SPEED, CreateNuclear.asResource("radiation"), -0.25f, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)); + + public static final Holder IODINE = EFFECTS.register("iodine", IodineEffect::new); + public static void register(IEventBus eventBus) { EFFECTS.register(eventBus); }} diff --git a/src/main/java/net/nuclearteam/createnuclear/CNEntityType.java b/src/main/java/net/nuclearteam/createnuclear/CNEntityType.java index ba0a69a..a3d2add 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNEntityType.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNEntityType.java @@ -1,6 +1,7 @@ package net.nuclearteam.createnuclear; import com.tterrag.registrate.util.entry.EntityEntry; +import net.minecraft.client.renderer.entity.NoopRenderer; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.storage.loot.LootTable; import net.neoforged.neoforge.client.event.EntityRenderersEvent; @@ -15,9 +16,18 @@ import net.nuclearteam.createnuclear.content.contraptions.irradiated.wolf.IrradiatedWolf; import net.nuclearteam.createnuclear.content.contraptions.irradiated.wolf.IrradiatedWolfModel; import net.nuclearteam.createnuclear.content.contraptions.irradiated.wolf.IrradiatedWolfRenderer; +import net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorModel; +import net.nuclearteam.createnuclear.content.explosion.NuclearExplosionEntity; public class CNEntityType { + public static final EntityEntry NUCLEAR_EXPLOSION = CreateNuclear.REGISTRATE + .entity("nuclear_explosion", NuclearExplosionEntity::new, MobCategory.MISC) + .properties(p -> p.sized(1.0f, 1.0f)) + .lang("Nuclear Explosion") + .renderer(() -> NoopRenderer::new) + .register(); + public static final EntityEntry IRRADIATED_CAT = CreateNuclear.REGISTRATE .entity("irradiated_cat", IrradiatedCat::new, MobCategory.CREATURE) .loot((tb, e) -> tb.add(e, LootTable.lootTable())) @@ -52,6 +62,7 @@ public static void registerModelLayer(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(CNModelLayers.IRRADIATED_CAT, IrradiatedCatModel::createBodyLayer); event.registerLayerDefinition(CNModelLayers.IRRADIATED_CHICKEN, IrradiatedChickenModel::createBodyLayer); event.registerLayerDefinition(CNModelLayers.IRRADIATED_WOLF, IrradiatedWolfModel::createBodyLayer); + event.registerLayerDefinition(CNModelLayers.ANTI_RADIATION_ARMOR, AntiRadiationArmorModel::createBodyLayer); } public static void register() {} diff --git a/src/main/java/net/nuclearteam/createnuclear/CNFluids.java b/src/main/java/net/nuclearteam/createnuclear/CNFluids.java index c5f6ebe..609ded4 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNFluids.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNFluids.java @@ -5,12 +5,24 @@ import com.tterrag.registrate.util.entry.FluidEntry; import net.createmod.catnip.theme.Color; import net.minecraft.core.BlockPos; +import net.minecraft.core.dispenser.BlockSource; +import net.minecraft.core.dispenser.DefaultDispenseItemBehavior; +import net.minecraft.core.dispenser.DispenseItemBehavior; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.item.DispensibleContainerItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.DispenserBlock; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; import net.neoforged.neoforge.common.NeoForgeMod; import net.neoforged.neoforge.common.SoundActions; @@ -21,47 +33,143 @@ import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.fluids.FluidType; import net.nuclearteam.createnuclear.content.decoration.palettes.CNPaletteStoneTypes; +import net.nuclearteam.createnuclear.content.radiation.capability.RadiationCapability; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancement; import org.joml.Vector3f; import net.nuclearteam.createnuclear.CNTags.CNFluidTags; import java.util.List; import java.util.function.Supplier; +import net.nuclearteam.createnuclear.content.radiation.RadiationBucketItem; + public class CNFluids { + private static final double URANIUM_FLUID_DOSE = 5.0D; + public static final FluidEntry URANIUM = - CreateNuclear.REGISTRATE.standardFluid("uranium", SolidRenderedPlaceableFluidity.create(0x38FF08, () -> 1f / 32f)) - .lang("Liquid Uranium") - //.tag(CNFluidTags.URANIUM.tag) - .properties(p -> p.viscosity(2500) - .density(1600) - .canSwim(false) - .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL_LAVA) - .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY_LAVA) - .canDrown(false) - ) - .fluidProperties(f -> f.levelDecreasePerBlock(2) - .tickRate(15) - .slopeFindDistance(6) - .explosionResistance(100f) - ) - .source(BaseFlowingFluid.Source::new) - .bucket() - //.tag(CNTags.forgeItemTag("buckets/uranium")) - .lang("Uranium Bucket") - .build() - .register(); + CreateNuclear.REGISTRATE.standardFluid("uranium", SolidRenderedPlaceableFluidity.create(0x38FF08, () -> 1f / 32f)) + .lang("Liquid Uranium") + .tag(CNFluidTags.URANIUM.tag) + .properties(p -> p.viscosity(2500) + .density(1600) + .canSwim(false) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL_LAVA) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY_LAVA) + .canDrown(false) + ) + .fluidProperties(f -> f.levelDecreasePerBlock(2) + .tickRate(15) + .slopeFindDistance(6) + .explosionResistance(100f) + ) + .source(BaseFlowingFluid.Source::new) + .bucket((s, p) -> new RadiationBucketItem(s instanceof Fluid ? (Fluid) s : ((Supplier) s).get(), p, 20)) + .onRegister(CNFluids::registerFluidDispenseBehavior) + .tag(CNTags.forgeItemTag("buckets/uranium")) + .lang("Uranium Bucket") + .build() + .register(); + + public static final FluidEntry THORIUM = + CreateNuclear.REGISTRATE.standardFluid("thorium", SolidRenderedPlaceableFluidity.create(0x38f9ff, () -> 1f / 32f)) + .lang("Liquid Thorium") + .tag(CNFluidTags.THORIUM.tag) + .properties(p -> p.viscosity(200) + .density(100) + .canSwim(false) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL_LAVA) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY_LAVA) + .canDrown(false) + ) + .fluidProperties(f -> f.levelDecreasePerBlock(2) + .tickRate(15) + .slopeFindDistance(6) + .explosionResistance(100f) + ) + .source(BaseFlowingFluid.Source::new) + .bucket() + .onRegister(CNFluids::registerFluidDispenseBehavior) + .tag(CNTags.forgeItemTag("buckets/thorium")) + .lang("Thorium Bucket") + .build() + .register(); + + public static final FluidEntry LIQUID_NITROGEN = + CreateNuclear.REGISTRATE.standardFluid("nitrogen", SolidRenderedPlaceableFluidity.create(0x23ECD5, () -> 1f / 16f)) + .lang("Liquid Nitrogen") + .tag(CNFluidTags.NITROGEN.tag) + .properties(p -> p.viscosity(1000) + .density(1000) + .canSwim(true) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL_AXOLOTL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY_FISH) + .canDrown(false) + ) + .fluidProperties(f -> f.levelDecreasePerBlock(5) + .tickRate(10) + .slopeFindDistance(6) + .explosionResistance(100f) + ) + .source(BaseFlowingFluid.Source::new) + //.onRegister(ReactorFluidTypesValue.setReactorFluidTypeInfos(8196, 100)) + .bucket() + .onRegister(CNFluids::registerFluidDispenseBehavior) + .lang("Nitrogen Bucket") + .tag(CNTags.forgeItemTag("buckets/nitrogen")) + .build() + .register(); + public static void register() {} - public static void handleFluidEffect(LivingEvent.LivingVisibilityEvent event) { + public static void handleFluidEffect(LivingEvent.LivingVisibilityEvent event) { LivingEntity entity = event.getEntity(); - if (entity.isAlive() && !(entity.isSpectator())) { - if (entity.tickCount % 20 == 0) return; - if (entity.isInFluidType(URANIUM.getType())) { - entity.addEffect(new MobEffectInstance(CNEffects.RADIATION.getDelegate(), 100, 0)); + if (!entity.isAlive() || entity.isSpectator()) return; + + Level level = entity.level(); + + // 1. Uranium: applies radiation contagion + if (entity.isInFluidType(URANIUM.getType())) { + if (entity.tickCount % 20 == 0) { + RadiationCapability.applyContagion(entity, URANIUM_FLUID_DOSE, 100); } } + // 2. Liquid nitrogen: freezes the entity like powder snow + else if (entity.isInFluidType(LIQUID_NITROGEN.getType())) { + // Extinguish fire immediately on contact + if (entity.isOnFire()) { + entity.clearFire(); + } + + if (entity instanceof Player player && player.isSwimming()) { + CNAdvancement.CRYOGENIC_BAPTISM.awardTo(player); + } + + int currentTicks = entity.getTicksFrozen(); + int maxTicks = entity.getTicksRequiredToFreeze(); + int freezeSpeed = 3; + + if (level.isClientSide) { + // Target maxTicks + 1 so vanilla's client-side -1 decay settles exactly at maxTicks + entity.setTicksFrozen(Math.min(maxTicks + 1, currentTicks + freezeSpeed + 1)); + } else { + // Target maxTicks + 2 so vanilla's server-side -2 decay settles exactly at maxTicks + entity.setTicksFrozen(Math.min(maxTicks + 2, currentTicks + freezeSpeed + 2)); + + // Check the freeze state after applying the compensation above + if (entity.getTicksFrozen() >= maxTicks && entity.tickCount % 10 == 0) { + entity.hurt(entity.damageSources().freeze(), 2.0F); + } + + entity.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 40, 1, true, false, false)); + } + } + + // 3. Thorium: burns the entity like lava + else if (entity.isInFluidType(THORIUM.getType())) { + entity.lavaHurt(); + } } public static void registerFluidInteractions() { @@ -73,8 +181,8 @@ public static void registerFluidInteractions() { // List of source FluidTypes we want to register (lava and water) List sourceFluids = List.of( - NeoForgeMod.LAVA_TYPE.value(), - NeoForgeMod.WATER_TYPE.value() + NeoForgeMod.LAVA_TYPE.value(), + NeoForgeMod.WATER_TYPE.value() ); // Loop over each source fluid and register the interaction @@ -86,17 +194,20 @@ public static void registerFluidInteractions() { private static class SolidRenderedPlaceableFluidity extends AllFluids.TintedFluidType { private Vector3f fogColor; + private int fogIntColor; private Supplier fogDistance; public static FluidTypeFactory create(int fogColor, Supplier fogDistance) { return (p, s, f) -> { SolidRenderedPlaceableFluidity fluidtype = new SolidRenderedPlaceableFluidity(p,s,f); fluidtype.fogColor = new Color(fogColor, false).asVectorF(); + fluidtype.fogIntColor = fogColor; fluidtype.fogDistance = fogDistance; return fluidtype; }; } + private SolidRenderedPlaceableFluidity(Properties properties, ResourceLocation stillTecture, ResourceLocation flowingTexture) { super(properties, stillTecture, flowingTexture); } @@ -108,7 +219,7 @@ protected int getTintColor(FluidStack stack) { @Override protected int getTintColor(FluidState state, BlockAndTintGetter getter, BlockPos pos) { - return 0x38FF08; + return fogIntColor; } @Override @@ -121,4 +232,22 @@ protected float getFogDistanceModifier() { return fogDistance.get(); } } + + private static final DispenseItemBehavior DEFAULT = new DefaultDispenseItemBehavior(); + private static final DispenseItemBehavior DISPENSE_FLUID = new DefaultDispenseItemBehavior(){ + @Override + protected ItemStack execute(BlockSource pSource, ItemStack pStack) { + DispensibleContainerItem dispensibleContainerItem = (DispensibleContainerItem) pStack.getItem(); + BlockPos pos = pSource.pos().relative(pSource.state().getValue(DispenserBlock.FACING)); + Level level = pSource.level(); + if (dispensibleContainerItem.emptyContents(null, level, pos, null, pStack)) { + return new ItemStack(Items.BUCKET); + } + return DEFAULT.dispense(pSource, pStack); + } + }; + + private static void registerFluidDispenseBehavior(BucketItem bucket) { + DispenserBlock.registerBehavior(bucket, DISPENSE_FLUID); + } } diff --git a/src/main/java/net/nuclearteam/createnuclear/CNItems.java b/src/main/java/net/nuclearteam/createnuclear/CNItems.java index fb0cbbe..4bf9860 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNItems.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNItems.java @@ -1,14 +1,9 @@ package net.nuclearteam.createnuclear; -import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.Boot; -import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.Chestplate; -import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.Chestplate.getChestplateTag; -import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.Helmet; -import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.Helmet.getHelmetTag; -import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.Leggings; - import static net.nuclearteam.createnuclear.CNTags.CNItemTags; -import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.Leggings.getLeggingsTag; +import static net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem.*; +import static net.nuclearteam.createnuclear.foundation.data.CNBuilderTransformers.biomeRestoreModel; +import static net.nuclearteam.createnuclear.foundation.data.CNBuilderTransformers.coloredArmorModel; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllItems; @@ -17,27 +12,26 @@ import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.data.recipes.ShapedRecipeBuilder; import net.minecraft.data.recipes.ShapelessRecipeBuilder; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; import net.minecraft.world.food.FoodProperties; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; +import net.minecraft.world.item.*; +import net.minecraft.world.item.crafting.Ingredient; import net.neoforged.neoforge.common.DeferredSpawnEggItem; -import net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem; -import net.nuclearteam.createnuclear.content.equipment.armor.CNArmorMaterials; +import net.nuclearteam.createnuclear.api.ItemRodTypesValue; +import net.nuclearteam.createnuclear.api.data.recipe.SmithingClothRecipeBuilder; +import net.nuclearteam.createnuclear.api.multiblock.rods.RodType; +import net.nuclearteam.createnuclear.content.biome.BiomeIrradiationExtractorItem; import net.nuclearteam.createnuclear.content.equipment.cloth.ClothItem; -import net.nuclearteam.createnuclear.content.equipment.cloth.ClothItem.DyeItemList; +import net.nuclearteam.createnuclear.content.equipment.cloth.ClothItem.Cloths; import net.nuclearteam.createnuclear.content.multiblock.bluePrintItem.ReactorBluePrintItem; +import net.nuclearteam.createnuclear.content.radiation.RadiationItem; +import net.nuclearteam.createnuclear.foundation.data.CNBuilderTransformers; import net.nuclearteam.createnuclear.foundation.item.DyedItemsList; import net.nuclearteam.createnuclear.foundation.utility.TextUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.function.Supplier; @SuppressWarnings({"unused", "deprecation"}) public class CNItems { @@ -45,9 +39,9 @@ public class CNItems { CreateNuclear.REGISTRATE.setCreativeTab(CNCreativeModeTabs.MAIN); } - public static final ItemEntry + public static final ItemEntry YELLOWCAKE = CreateNuclear.REGISTRATE - .item("yellowcake", Item::new) + .item("yellowcake", p -> new RadiationItem(p, 4)) .properties(p -> p.food(new FoodProperties.Builder() .nutrition(20) .saturationModifier(0.3F) @@ -57,39 +51,49 @@ public class CNItems { ) .register(), - RAW_LEAD = CreateNuclear.REGISTRATE - .item("raw_lead", Item::new) - .tag(CNTags.forgeItemTag("raw_ores"), CNTags.forgeItemTag("raw_materials"), CNTags.forgeItemTag("raw_materials/lead")) - .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) - .unlockedBy("has_storage_blocks_raw_lead", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/raw_lead"))) - .requires(CNTags.forgeItemTag("storage_blocks/raw_lead")) - .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) - ) + ENRICHED_YELLOWCAKE = CreateNuclear.REGISTRATE + .item("enriched_yellowcake", p -> new RadiationItem(p, 2)) .register(), RAW_URANIUM = CreateNuclear.REGISTRATE - .item("raw_uranium", Item::new) + .item("raw_uranium", p -> new RadiationItem(p, 3)) .tag(CNTags.forgeItemTag("raw_ores"), CNTags.forgeItemTag("raw_materials"), CNTags.forgeItemTag("raw_materials/uranium")) - .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) - .unlockedBy("has_storage_blocks_raw_uranium", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/raw_uranium"))) - .requires(CNTags.forgeItemTag("storage_blocks/raw_uranium")) - .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) - ) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_raw_uranium", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/raw_uranium"))) + .requires(CNTags.forgeItemTag("storage_blocks/raw_uranium")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) .register(), URANIUM_POWDER = CreateNuclear.REGISTRATE - .item("uranium_powder", Item::new) + .item("uranium_powder", p -> new RadiationItem(p, 2)) .tag(CNTags.forgeItemTag("dusts"), CNTags.forgeItemTag("dusts/uranium")) .register(), + URANIUM_ROD = CreateNuclear.REGISTRATE + .item("uranium_rod", p -> new RadiationItem(p, 100)) + .tag(CNTags.forgeItemTag("rods"), CNItemTags.FUEL.tag) + .register(); + + public static final ItemEntry + RAW_LEAD = CreateNuclear.REGISTRATE + .item("raw_lead", Item::new) + .tag(CNTags.forgeItemTag("raw_ores"), CNTags.forgeItemTag("raw_materials"), CNTags.forgeItemTag("raw_materials/lead")) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_raw_lead", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/raw_lead"))) + .requires(CNTags.forgeItemTag("storage_blocks/raw_lead")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) + .register(), + STEEL_INGOT = CreateNuclear.REGISTRATE .item("steel_ingot", Item::new) .tag(CNTags.forgeItemTag("ingots"), CNTags.forgeItemTag("ingots/steel")) - .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) - .unlockedBy("has_storage_blocks_steel", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/steel"))) - .requires(CNTags.forgeItemTag("storage_blocks/steel")) - .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) - ) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_steel", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/steel"))) + .requires(CNTags.forgeItemTag("storage_blocks/steel")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) .register(), COAL_DUST = CreateNuclear.REGISTRATE @@ -105,169 +109,270 @@ public class CNItems { LEAD_INGOT = CreateNuclear.REGISTRATE .item("lead_ingot", Item::new) .tag(CNTags.forgeItemTag("ingots"), CNTags.forgeItemTag("ingots/lead")) - .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(),9) - .unlockedBy("has_storage_blocks_lead", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/lead"))) - .requires(CNTags.forgeItemTag("storage_blocks/lead")) - .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) - ) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(),9) + .unlockedBy("has_storage_blocks_lead", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/lead"))) + .requires(CNTags.forgeItemTag("storage_blocks/lead")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) .register(), STEEL_NUGGET = CreateNuclear.REGISTRATE .item("steel_nugget", Item::new) .tag(CNTags.forgeItemTag("nuggets"), CNTags.forgeItemTag("nuggets/steel")) - .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) - .unlockedBy("has_storage_blocks_steel_nugget", RegistrateRecipeProvider.has(CNTags.forgeItemTag("ingots/steel"))) - .requires(CNTags.forgeItemTag("ingots/steel")) - .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) - ) - .register(), - - URANIUM_ROD = CreateNuclear.REGISTRATE - .item("uranium_rod", Item::new) - .tag(CNTags.forgeItemTag("rods"), CNItemTags.FUEL.tag) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_steel_nugget", RegistrateRecipeProvider.has(CNTags.forgeItemTag("ingots/steel"))) + .requires(CNTags.forgeItemTag("ingots/steel")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) .register(), LEAD_NUGGET = CreateNuclear.REGISTRATE .item("lead_nugget", Item::new) .tag(CNTags.forgeItemTag("nuggets"), CNTags.forgeItemTag("nuggets/lead")) - .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) - .unlockedBy("has_storage_blocks_lead_nugget", RegistrateRecipeProvider.has(CNTags.forgeItemTag("ingots/lead"))) - .requires(CNTags.forgeItemTag("ingots/lead")) - .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) - ) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_lead_nugget", RegistrateRecipeProvider.has(CNTags.forgeItemTag("ingots/lead"))) + .requires(CNTags.forgeItemTag("ingots/lead")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) .register(), GRAPHENE = CreateNuclear.REGISTRATE .item("graphene", Item::new) .register(), - ENRICHED_YELLOWCAKE = CreateNuclear.REGISTRATE - .item("enriched_yellowcake", Item::new) - .register() - ; + RAW_THORIUM = CreateNuclear.REGISTRATE + .item("raw_thorium", Item::new) + .tag(CNTags.forgeItemTag("raw_ores"), CNTags.forgeItemTag("raw_materials"), CNTags.forgeItemTag("raw_materials/thorium")) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_raw_thorium", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/raw_thorium"))) + .requires(CNTags.forgeItemTag("storage_blocks/raw_thorium")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) + .register(), - public static final DyedItemsList ANTI_RADIATION_HELMETS = new DyedItemsList<>(color -> { - String colorName = color.getSerializedName(); - return CreateNuclear.REGISTRATE.item(colorName + "_anti_radiation_helmet", p -> new Helmet(p, color)) - .tag( - CNTags.forgeItemTag("armors/helmets"), - getHelmetTag(colorName), - CNItemTags.ALL_ANTI_RADIATION_ARMORS.tag, - CNItemTags.ANTI_RADIATION_HELMET_FULL_DYE.tag + THORIUM_DUST = CreateNuclear.REGISTRATE + .item("thorium_dust", Item::new) + .tag(CNTags.forgeItemTag("dusts"), CNTags.forgeItemTag("dusts/thorium")) + .register(), + + THORIUM_NUGGET = CreateNuclear.REGISTRATE + .item("thorium_nugget", Item::new) + .model((c, p) -> p.generated(c, CreateNuclear.asResource("item/thorium_nugget"))) + .tag(CNTags.forgeItemTag("nuggets"), CNTags.forgeItemTag("nuggets/thorium")) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_steel_nugget", RegistrateRecipeProvider.has(CNTags.forgeItemTag("ingots/thorium"))) + .requires(CNTags.forgeItemTag("ingots/thorium")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) ) - .recipe((c, p) -> - ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) - .unlockedBy("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) - .define('X', CNTags.forgeItemTag("ingots/lead")) - .define('Y', ClothItem.Cloths.getByColor(color).get()) - .define('Z', CNBlocks.REINFORCED_GLASS.asItem()) - .pattern("YXY") - .pattern("XZX") - .showNotification(true) - .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName()))) - - .properties(p -> p.durability(CNArmorMaterials.durabilityForType(ArmorItem.Type.HELMET))) - .lang(TextUtils.titleCaseConversion(color.getName()) +" Anti Radiation Helmet") - .model((c, p) -> p.generated(c, CreateNuclear.asResource("item/armors/helmets/" + colorName + "_anti_radiation_helmet"))) - .register(); + .register(), - }); + THORIUM_INGOT = CreateNuclear.REGISTRATE + .item("thorium_ingot", Item::new) + .model((c, p) -> p.generated(c, CreateNuclear.asResource("item/thorium_ingot"))) + .tag(CNTags.forgeItemTag("ingots"), CNTags.forgeItemTag("ingots/thorium")) + .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, c.get(), 9) + .unlockedBy("has_storage_blocks_thorium", RegistrateRecipeProvider.has(CNTags.forgeItemTag("storage_blocks/thorium"))) + .requires(CNTags.forgeItemTag("storage_blocks/thorium")) + .save(p, CreateNuclear.asResource("crafting/" + c.getName() + "_from_decompacting")) + ) + .register(), - public static final DyedItemsList ANTI_RADIATION_CHESTPLATES = new DyedItemsList<>(color -> { - String colorName = color.getSerializedName(); + THORIUM_ROD = CreateNuclear.REGISTRATE + .item("thorium_rod", Item::new) + .onRegister(ItemRodTypesValue.setRodTypeInfos(new RodType.Builder() + .rodTimer(3600) + .baseRodHeat(16) + .proximityRodHeat(8) + .fuelRodType())) + .tag(CNTags.forgeItemTag("rods")) + .register(), + + NITRATE = CreateNuclear.REGISTRATE + .item("nitrate", Item::new) + .lang("Nitrate") + .register(), + + NITROGEN_CONCENTRATE = CreateNuclear.REGISTRATE + .item("nitrogen_concentrate", Item::new) + .lang("Nitrogen Concentrate") + .register(), - return CreateNuclear.REGISTRATE.item(colorName + "_anti_radiation_chestplate", p -> new Chestplate(p, color)) + COOLED_NITROGEN_CONCENTRATE = CreateNuclear.REGISTRATE + .item("cooled_nitrogen_concentrate", Item::new) + .lang("Cooled Nitrogen Concentrate") + .register() + ; + + public static final ItemEntry ANTI_RADIATION_HELMETS = CreateNuclear.REGISTRATE + .item("default_anti_radiation_helmet", Helmet::new) + .properties(p -> p.stacksTo(1)) + .tag( + CNTags.forgeItemTag("armors/helmets"), + CNItemTags.ANTI_RADIATION_ARMOR.tag + ) + .transform(setColorComponent(Cloths.DEFAULT)) + .recipe((c, p) -> { + ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) + .unlockedBy("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) + .define('X', CNTags.forgeItemTag("ingots/lead")) + .define('Y', CNTags.forgeItemTag("ingots/brass")) + .define('Z', CNBlocks.REINFORCED_GLASS.asItem()) + .pattern("YXY") + .pattern("XZX") + .showNotification(true) + .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName())); + + for (Cloths cloth : Cloths.values()) { + if (cloth == Cloths.DEFAULT) continue; + SmithingClothRecipeBuilder + .smithingCloth( + Ingredient.EMPTY, + Ingredient.of(c.get()), + Ingredient.of(cloth.getItem()), + RecipeCategory.COMBAT, + new ItemStack(c.get()) + ) + .unlocks("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) + .save(p, CreateNuclear.asResource("smithing/" + c.getName() + "_" + cloth.getSerializedName())); + } + }) + .lang("Anti Radiation Helmet") + .model(coloredArmorModel("helmet", "layer0", "particle")) + .register(); + + public static final ItemEntry ANTI_RADIATION_CHESTPLATES = CreateNuclear.REGISTRATE + .item("default_anti_radiation_chestplate", Chestplate::new) + .properties(p -> p.stacksTo(1)) .tag( CNTags.forgeItemTag("armors/chestplates"), - getChestplateTag(colorName), - CNItemTags.ALL_ANTI_RADIATION_ARMORS.tag, - CNItemTags.ANTI_RADIATION_CHESTPLATE_FULL_DYE.tag + CNTags.CNItemTags.ANTI_RADIATION_ARMOR.tag ) - .recipe((c, p) -> ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) - .unlockedBy("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) - .define('X', CNTags.forgeItemTag("ingots/lead")) - .define('Y', ClothItem.Cloths.getByColor(color).get()) - .define('Z', CNItems.GRAPHITE_ROD) - .pattern("Y Y") - .pattern("XXX") - .pattern("ZXZ") - .showNotification(true) - .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName()))) - - .properties(p -> p.durability(CNArmorMaterials.durabilityForType(ArmorItem.Type.CHESTPLATE))) - .lang(TextUtils.titleCaseConversion(color.getName()) +" Anti Radiation Chestplate") - .model((c, p) -> p.generated(c, CreateNuclear.asResource("item/armors/chestplates/" + colorName + "_anti_radiation_chestplate"))) + .transform(setColorComponent(Cloths.DEFAULT)) + .recipe((c, p) -> { + ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) + .unlockedBy("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) + .define('X', CNTags.forgeItemTag("ingots/lead")) + .define('Y', CNTags.forgeItemTag("ingots/brass")) + .define('Z', CNItems.GRAPHITE_ROD) + .pattern("Y Y") + .pattern("XXX") + .pattern("ZXZ") + .showNotification(true) + .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName())); + for (Cloths cloth : Cloths.values()) { + if (cloth == Cloths.DEFAULT) continue; + + SmithingClothRecipeBuilder + .smithingCloth( + Ingredient.EMPTY, + Ingredient.of(c.get()), + Ingredient.of(cloth.getItem()), + RecipeCategory.COMBAT, + new ItemStack(c.get()) + ) + .unlocks("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) + .save(p, CreateNuclear.asResource("smithing/" + c.getName() + "_" + cloth.getSerializedName())); + } + }) + .lang("Anti Radiation Chestplate") + .model(coloredArmorModel("chestplate", "14")) .register(); - }); - - public static final DyedItemsList ANTI_RADIATION_LEGGINGS = new DyedItemsList<>(color -> { - String colorName = color.getSerializedName(); - return CreateNuclear.REGISTRATE.item(colorName + "_anti_radiation_leggings", p -> new Leggings(p, color)) + public static final ItemEntry ANTI_RADIATION_LEGGINGS = CreateNuclear.REGISTRATE + .item("default_anti_radiation_leggings", Leggings::new) + .properties(p -> p.stacksTo(1)) .tag( CNTags.forgeItemTag("armors/leggings"), - getLeggingsTag(colorName), - CNItemTags.ALL_ANTI_RADIATION_ARMORS.tag, - CNItemTags.ANTI_RADIATION_LEGGINGS_FULL_DYE.tag + CNTags.CNItemTags.ANTI_RADIATION_ARMOR.tag ) - .recipe((c, p) -> ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) - .unlockedBy("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) - .define('X', CNTags.forgeItemTag("ingots/lead")) - .define('Y', ClothItem.Cloths.getByColor(color).get()) - .pattern("YXY") - .pattern("X X") - .pattern("Y Y") - .showNotification(true) - .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName()))) - - .properties(p -> p.durability(CNArmorMaterials.durabilityForType(ArmorItem.Type.LEGGINGS))) - .lang(TextUtils.titleCaseConversion(color.getName()) +" Anti Radiation Leggings") - .model((c, p) -> p.generated(c, CreateNuclear.asResource("item/armors/leggings/" + colorName + "_anti_radiation_leggings"))) + .transform(setColorComponent(Cloths.DEFAULT)) + .recipe((c, p) -> { + ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) + .unlockedBy("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) + .define('X', CNTags.forgeItemTag("ingots/lead")) + .define('Y', CNTags.forgeItemTag("ingots/brass")) + .pattern("YXY") + .pattern("X X") + .pattern("Y Y") + .showNotification(true) + .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName())); + + for (Cloths cloth : Cloths.values()) { + if (cloth == Cloths.DEFAULT) continue; + + SmithingClothRecipeBuilder + .smithingCloth( + Ingredient.EMPTY, + Ingredient.of(c.get()), + Ingredient.of(cloth.getItem()), + RecipeCategory.COMBAT, + new ItemStack(c.get()) + ) + .unlocks("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) + .save(p, CreateNuclear.asResource("smithing/" + c.getName() + "_" + cloth.getSerializedName())); + } + }) + .lang("Anti Radiation Leggings") + .model(coloredArmorModel("leggings", "14")) .register(); - }); - - public static final ItemEntry - ANTI_RADIATION_BOOTS = CreateNuclear.REGISTRATE.item("anti_radiation_boots", Boot::new) + public static final ItemEntry ANTI_RADIATION_BOOTS = CreateNuclear.REGISTRATE + .item("default_anti_radiation_boots", Boot::new) + .properties(p -> p.stacksTo(1)) .tag( CNTags.forgeItemTag("armors/boots"), - CNItemTags.ANTI_RADIATION_BOOTS_DYE.tag, - CNItemTags.ANTI_RADIATION_ARMOR.tag, - CNItemTags.ALL_ANTI_RADIATION_ARMORS.tag + CNTags.CNItemTags.ANTI_RADIATION_ARMOR.tag ) - .lang("Anti Radiation Boots") - .recipe((c, p) -> ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) + .transform(setColorComponent(Cloths.DEFAULT)) + .recipe((c, p) -> { + ShapedRecipeBuilder.shaped(RecipeCategory.COMBAT, c.get()) .unlockedBy("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) .define('X', CNTags.forgeItemTag("ingots/lead")) - .define('Y', ClothItem.Cloths.WHITE_CLOTH.getItem()) + .define('Y', CNTags.forgeItemTag("ingots/brass")) .pattern("Y Y") .pattern("X X") .showNotification(true) - .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName()))) - .properties(p -> p.durability(CNArmorMaterials.durabilityForType(ArmorItem.Type.BOOTS))) - .model((c, p) -> p.generated(c, CreateNuclear.asResource("item/armors/anti_radiation_boots"))) + .save(p, CreateNuclear.asResource("crafting/items/armors/" + c.getName())); + + for (Cloths cloth : Cloths.values()) { + if (cloth == Cloths.DEFAULT) continue; + + SmithingClothRecipeBuilder + .smithingCloth( + Ingredient.EMPTY, + Ingredient.of(c.get()), + Ingredient.of(cloth.getItem()), + RecipeCategory.COMBAT, + new ItemStack(c.get()) + ) + .unlocks("has_cloth", RegistrateRecipeProvider.has(CNItemTags.CLOTH.tag)) + .save(p, CreateNuclear.asResource("smithing/" + c.getName() + "_" + cloth.getSerializedName())); + } + }) + .lang("Anti Radiation Boots") + .model(coloredArmorModel("boots", "14")) .register(); - public static final DyeItemList CLOTHS = new ClothItem.DyeItemList<>(color -> { + public static final DyedItemsList CLOTHS = new DyedItemsList<>(color -> { String colorName = color.getSerializedName(); List ingredients = new ArrayList<>(Arrays.asList(Items.WHITE_DYE, Items.ORANGE_DYE, Items.MAGENTA_DYE, Items.LIGHT_BLUE_DYE, Items.YELLOW_DYE, Items.LIME_DYE, Items.PINK_DYE, Items.GRAY_DYE, Items.LIGHT_GRAY_DYE, Items.CYAN_DYE, Items.PURPLE_DYE, Items.BLUE_DYE, Items.BROWN_DYE, Items.GREEN_DYE, Items.RED_DYE, Items.BLACK_DYE)); return CreateNuclear.REGISTRATE.item(colorName+ "_cloth", p -> new ClothItem(p, color)) .tag(CNItemTags.CLOTH.tag) .recipe((c, p) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.BUILDING_BLOCKS, c.get()) - .unlockedBy("has_white_cloth", RegistrateRecipeProvider.has(ClothItem.Cloths.WHITE_CLOTH.getItem())) - .requires(CNItemTags.CLOTH.tag) - .requires(ingredients.get(color.ordinal())) - .save(p, CreateNuclear.asResource("shapeless/cloth/" + c.getName())) + .unlockedBy("has_white_cloth", RegistrateRecipeProvider.has(ClothItem.Cloths.WHITE_CLOTH.getItem())) + .requires(CNItemTags.CLOTH.tag) + .requires(ingredients.get(color.ordinal())) + .save(p, CreateNuclear.asResource("shapeless/cloth/" + c.getName())) ) .lang(TextUtils.titleCaseConversion(color.getName()) + " Cloth") .model((c, p) -> p.generated(c, CreateNuclear.asResource("item/cloth/" + colorName + "_cloth"))) .register(); }); - public static final ItemEntry SPAWN_WOLF = registerSpawnEgg("wolf_irradiated_spawn_egg", CNEntityType.IRRADIATED_WOLF, 0x42452B,0x4C422B, "Irradiated Wolf Spawn Egg"); - public static final ItemEntry SPAWN_CAT = registerSpawnEgg("cat_irradiated_spawn_egg", CNEntityType.IRRADIATED_CAT, 0x382C19, 0x742728, "Irradiated Cat Spawn Egg"); - public static final ItemEntry SPAWN_CHICKEN = registerSpawnEgg("chicken_irradiated_spawn_egg", CNEntityType.IRRADIATED_CHICKEN, 0x6B9455, 0x95393C, "Irradiated Chicken Spawn Egg"); + public static final ItemEntry SPAWN_WOLF = CNBuilderTransformers.spawnEgg("wolf_irradiated_spawn_egg", CNEntityType.IRRADIATED_WOLF, 0x42452B, 0x4C422B, "Irradiated Wolf Spawn Egg"); + public static final ItemEntry SPAWN_CAT = CNBuilderTransformers.spawnEgg("cat_irradiated_spawn_egg", CNEntityType.IRRADIATED_CAT, 0x382C19, 0x742728, "Irradiated Cat Spawn Egg"); + public static final ItemEntry SPAWN_CHICKEN = CNBuilderTransformers.spawnEgg("chicken_irradiated_spawn_egg", CNEntityType.IRRADIATED_CHICKEN, 0x6B9455, 0x95393C, "Irradiated Chicken Spawn Egg"); public static final ItemEntry REACTOR_BLUEPRINT = CreateNuclear.REGISTRATE .item("reactor_blueprint_item", ReactorBluePrintItem::new) @@ -292,15 +397,32 @@ public class CNItems { .properties(p -> p.stacksTo(1)) .register(); - - private static ItemEntry registerSpawnEgg(String name, Supplier> entity, int backgroundColor, int highlightColor, String nameItems) { - return CreateNuclear.REGISTRATE - .item(name, p -> new DeferredSpawnEggItem(entity, backgroundColor, highlightColor, p)) - .lang(nameItems) - .model((c, p) -> p.withExistingParent(c.getName(), ResourceLocation.withDefaultNamespace("item/template_spawn_egg"))) + public static final ItemEntry REINFORCED_GLASS_BOTTLE = CreateNuclear.REGISTRATE + .item("reinforced_glass_bottle", Item::new) + .lang("Reinforced Glass Bottle") + .recipe((c, p) -> ShapedRecipeBuilder.shaped(RecipeCategory.BREWING, c.get(), 3) + .unlockedBy("has_reinforced_glass", RegistrateRecipeProvider.has(CNBlocks.REINFORCED_GLASS.get())) + .define('G', CNBlocks.REINFORCED_GLASS) + .pattern("G G") + .pattern(" G ") + .save(p, CreateNuclear.asResource("crafting/" + c.getName()))) + .properties(p -> p.stacksTo(16)) .register(); - } + public static final ItemEntry IRRADIATION_BIOME_EXTRACTOR = CreateNuclear.REGISTRATE + .item("biome_irradiation_extractor", BiomeIrradiationExtractorItem::new) + .lang("Biome Irradiation Extractor") + .recipe((c, p) -> ShapedRecipeBuilder.shaped(RecipeCategory.MISC, c.get(), 8) + .unlockedBy("has_reinforced_glass_bottle", RegistrateRecipeProvider.has(CNItems.REINFORCED_GLASS_BOTTLE.get())) + .define('B', CNItems.REINFORCED_GLASS_BOTTLE) + .define('S', Items.NETHER_STAR) + .pattern("BBB") + .pattern("BSB") + .pattern("BBB") + .save(p, CreateNuclear.asResource("crafting/" + c.getName()))) + .properties(p -> p.stacksTo(16).fireResistant().setNoRepair()) + .model(biomeRestoreModel()) + .register(); public static void register() {} } diff --git a/src/main/java/net/nuclearteam/createnuclear/CNMenus.java b/src/main/java/net/nuclearteam/createnuclear/CNMenus.java index 29d2993..ff58565 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNMenus.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNMenus.java @@ -9,12 +9,12 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import net.nuclearteam.createnuclear.content.multiblock.bluePrintItem.ReactorBluePrintItemScreen; import net.nuclearteam.createnuclear.content.multiblock.bluePrintItem.ReactorBluePrintMenu; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInputMenu; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInputScreen; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInputMenu; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInputScreen; public class CNMenus { public static final MenuEntry REACTOR_BLUEPRINT_MENU = menu("reactor_blueprint_menu", ReactorBluePrintMenu::new, () -> ReactorBluePrintItemScreen::new); - public static final MenuEntry SLOT_ITEM_STORAGE = menu("slot_item_menu", ReactorInputMenu::new, () -> ReactorInputScreen::new); + public static final MenuEntry SLOT_ITEM_STORAGE = menu("slot_item_menu", ReactorRodInputMenu::new, () -> ReactorRodInputScreen::new); private static > MenuEntry menu(String name, ForgeMenuFactory factory, NonNullSupplier> screenFactory) { return CreateNuclear.REGISTRATE diff --git a/src/main/java/net/nuclearteam/createnuclear/CNOpenPipeEffectHandlers.java b/src/main/java/net/nuclearteam/createnuclear/CNOpenPipeEffectHandlers.java new file mode 100644 index 0000000..225109a --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/CNOpenPipeEffectHandlers.java @@ -0,0 +1,10 @@ +package net.nuclearteam.createnuclear; + +import com.simibubi.create.api.effect.OpenPipeEffectHandler; +import net.nuclearteam.createnuclear.content.radiation.RadiationEffectHandler; + +public class CNOpenPipeEffectHandlers { + public static void registerDefaults() { + OpenPipeEffectHandler.REGISTRY.register(CNFluids.URANIUM.getSource(), new RadiationEffectHandler()); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/CNParticleRegistry.java b/src/main/java/net/nuclearteam/createnuclear/CNParticleRegistry.java new file mode 100644 index 0000000..20f7c18 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/CNParticleRegistry.java @@ -0,0 +1,26 @@ +package net.nuclearteam.createnuclear; + +import com.mojang.serialization.Codec; +import net.minecraft.core.particles.BlockParticleOption; +import net.minecraft.core.particles.ItemParticleOption; +import net.minecraft.core.particles.ParticleType; +import net.minecraft.core.particles.SimpleParticleType; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.minecraft.core.registries.BuiltInRegistries; +import net.neoforged.neoforge.registries.DeferredHolder; + +public class CNParticleRegistry { + public static final DeferredRegister> DEF_REG; + + public static final DeferredHolder, SimpleParticleType> NUCLEAR_MUSHROOM_CLOUD; + public static final DeferredHolder, SimpleParticleType> NUCLEAR_MUSHROOM_CLOUD_SMOKE; + public static final DeferredHolder, SimpleParticleType> NUCLEAR_MUSHROOM_CLOUD_EXPLOSION; + + static { + DEF_REG = DeferredRegister.create(BuiltInRegistries.PARTICLE_TYPE, CreateNuclear.MOD_ID); + NUCLEAR_MUSHROOM_CLOUD = DEF_REG.register("nuclear_mushroom_cloud", () -> new SimpleParticleType(false)); + NUCLEAR_MUSHROOM_CLOUD_SMOKE = DEF_REG.register("nuclear_mushroom_cloud_smoke", () -> new SimpleParticleType(false)); + NUCLEAR_MUSHROOM_CLOUD_EXPLOSION = DEF_REG.register("nuclear_mushroom_cloud_explosion", () -> new SimpleParticleType(false)); + } + +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/CNParticleTypes.java b/src/main/java/net/nuclearteam/createnuclear/CNParticleTypes.java new file mode 100644 index 0000000..28983f1 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/CNParticleTypes.java @@ -0,0 +1,69 @@ +package net.nuclearteam.createnuclear; + +import com.simibubi.create.foundation.particle.ICustomParticleData; +import net.createmod.catnip.lang.Lang; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.minecraft.core.registries.BuiltInRegistries; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.bus.api.IEventBus; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; +import net.nuclearteam.createnuclear.content.particles.IrradiatedParticlesData; + +import java.util.function.Supplier; + +public enum CNParticleTypes { + + IRRADIATED_PARTICLES(IrradiatedParticlesData::new), + ; + + private final ParticleEntry entry; + + CNParticleTypes(Supplier> typeFactory) { + String name = Lang.asId(name()); + entry = new ParticleEntry<>(name, typeFactory); + } + + public static void register(IEventBus modEventBus) { + ParticleEntry.REGISTER.register(modEventBus); + } + + @OnlyIn(Dist.CLIENT) + public static void registerFactories(RegisterParticleProvidersEvent event) { + for (CNParticleTypes particle : values()) + particle.entry.registerFactory(event); + } + + public ParticleType get() { + return entry.object.get(); + } + + public String parameter() { + return entry.name; + } + + + private static class ParticleEntry { + private static final DeferredRegister> REGISTER = DeferredRegister.create(BuiltInRegistries.PARTICLE_TYPE, CreateNuclear.MOD_ID); + + private final String name; + private final Supplier> typeFactory; + private final DeferredHolder, ParticleType> object; + + public ParticleEntry(String name, Supplier> typeFactory) { + this.name = name; + this.typeFactory = typeFactory; + + object = REGISTER.register(name, () -> this.typeFactory.get().createType()); + } + + @OnlyIn(Dist.CLIENT) + public void registerFactory(RegisterParticleProvidersEvent event) { + typeFactory.get() + .register(object.get(), event); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/CNPotions.java b/src/main/java/net/nuclearteam/createnuclear/CNPotions.java index 98b0ae3..643b20e 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNPotions.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNPotions.java @@ -23,6 +23,13 @@ public class CNPotions { public static final Holder POTION_2 = CN_POTIONS.register("potion_of_radiation_2", () -> new Potion(new MobEffectInstance(CNEffects.RADIATION.getDelegate(), 410, 1))); + public static final Holder POTION_1_IODINE = CN_POTIONS.register("potion_of_iodine", + () -> new Potion(new MobEffectInstance(CNEffects.IODINE.getDelegate(), 900)) + ); + public static final Holder POTION_AUGMENT_1_IODINE = CN_POTIONS.register("potion_of_iodine_augment", + () -> new Potion(new MobEffectInstance(CNEffects.IODINE.getDelegate(), 1800)) + ); + public static void register(IEventBus eventBus) { CN_POTIONS.register(eventBus); } @@ -33,5 +40,8 @@ public static void registerPotionsRecipes(RegisterBrewingRecipesEvent event) { builder.addMix(Potions.AWKWARD, CNItems.ENRICHED_YELLOWCAKE.get(), POTION_1); builder.addMix(POTION_1, Items.REDSTONE, POTION_AUGMENT_1); builder.addMix(POTION_1, Items.GLOWSTONE_DUST, POTION_2); + + builder.addMix(Potions.AWKWARD, Items.DRIED_KELP, POTION_1_IODINE); + builder.addMix(POTION_1_IODINE, Items.REDSTONE, POTION_AUGMENT_1_IODINE); } } diff --git a/src/main/java/net/nuclearteam/createnuclear/CNShapes.java b/src/main/java/net/nuclearteam/createnuclear/CNShapes.java index b32f6d2..9e30709 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNShapes.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNShapes.java @@ -12,11 +12,11 @@ @SuppressWarnings("unused") public class CNShapes { - // Independent Shapers public static final VoxelShaper - REACTOR_OUTPUT = shape(0, 0, 0, 16, 14, 16).forDirectional(), - REACTOR_INPUT = shape(0,0,0,16,16,16).forDirectional() + REACTOR_OUTPUT = shape(0, 0, 0, 16, 14, 16).forDirectional(), + REACTOR_INPUT = shape(0,0,0,16,16,16).forDirectional(), + REACTOR_FLUID_INPUT = shape(0,0,0,16,16,16).forDirectional() ; private static Builder shape(VoxelShape shape) { diff --git a/src/main/java/net/nuclearteam/createnuclear/CNSoundEvents.java b/src/main/java/net/nuclearteam/createnuclear/CNSoundEvents.java new file mode 100644 index 0000000..5ce274e --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/CNSoundEvents.java @@ -0,0 +1,434 @@ +package net.nuclearteam.createnuclear; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.simibubi.create.AllSoundEvents; +import com.simibubi.create.AllSoundEvents.ConfiguredSoundEvent; +import com.simibubi.create.AllSoundEvents.SoundEntry; +import com.simibubi.create.Create; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.CachedOutput; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.RegisterEvent; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +public class CNSoundEvents { + + public static final Map ALL = new HashMap<>(); + + public static final SoundEntry + + REACTOR_ACTIVATION = create("reacteur/activation") + .subtitle("Reactor Activation") + .category(SoundSource.BLOCKS) + .build(), + + REACTOR_RUNNING = create("reacteur/running") + .subtitle("Reactor Running") + .category(SoundSource.BLOCKS) + .build(), + + REACTOR_SHUT_OFF = create("reacteur/shut_off") + .subtitle("Reactor Shut Off") + .category(SoundSource.BLOCKS) + .build(), + + REACTOR_ALARM_ONESHOT = create("alarm/reactor", "alarm") + .subtitle("Reactor Alarm") + .category(SoundSource.BLOCKS) + .build(), + + REACTOR_ALARM_LOOP = create("alarm/alarm") + .subtitle("Reactor Alarm Loop") + .category(SoundSource.BLOCKS) + .build(), + + NUCLEAR_EXPLOSION = create("explosion/nuclear_explosion") + .subtitle("Nuclear Explosion") + .category(SoundSource.AMBIENT) + .build(), + + NUCLEAR_EXPLOSION_LARGE = create("explosion/large_nuclear_explosion") + .subtitle("Large Nuclear Explosion") + .category(SoundSource.AMBIENT) + .build(), + + NUCLEAR_EXPLOSION_RINGING = create("explosion/ringing") + .subtitle("Nuclear Explosion Ringing") + .category(SoundSource.AMBIENT) + .build(), + + NUCLEAR_EXPLOSION_RUMBLE = create("explosion/nuclear_explosion_rumble") + .subtitle("Nuclear Explosion Rumble") + .category(SoundSource.AMBIENT) + .build(), + + MOTOR_ASSEMBLE = create("reacteur/assemble_deassemble/motor_assemble") + .subtitle("Motor Assemble") + .category(SoundSource.BLOCKS) + .build(), + + MOTOR_DISASSEMBLE = create("reacteur/assemble_deassemble/motor_disassemble") + .subtitle("Motor Disassemble") + .category(SoundSource.BLOCKS) + .build(), + + NUCLEAR_EXPLOSION_RUMBLE_2 = create("explosion/rumble") + .subtitle("Nuclear Explosion Rumble") + .category(SoundSource.AMBIENT) + .build(), + + NUCLEAR_EXPLOSION_MAIN = create("explosion/main") + .subtitle("Nuclear Explosion Main") + .category(SoundSource.AMBIENT) + .build(), + + NUCLEAR_EXPLOSION_SHOCKWAVE = create("explosion/shockwave") + .subtitle("Nuclear Explosion Shockwave") + .category(SoundSource.AMBIENT) + .build(), + + GEIGER_HIGH = create("geiger/high") + .subtitle("Geiger High") + .category(SoundSource.AMBIENT) + .build(), + + GEIGER_LOW = create("geiger/low") + .subtitle("Geiger Low") + .category(SoundSource.AMBIENT) + .build(), + + GEIGER_MEDIUM = create("geiger/medium") + .subtitle("Geiger Medium") + .category(SoundSource.AMBIENT) + .build(), + + BIOME_WASTELAND = create("biomes/wasteland") + .subtitle("Wasteland") + .category(SoundSource.AMBIENT) + .build() + ; + + private static SoundEntryBuilder create(String... pathParts) { + String name = String.join("_", pathParts); + return create(CreateNuclear.asResource(name)); + } + + private static SoundEntryBuilder create(ResourceLocation id) { + return new SoundEntryBuilder(id); + } + + public static void prepare() { + for (SoundEntry entry : ALL.values()) entry.prepare(); + } + + public static void register(RegisterEvent event) { + event.register(Registries.SOUND_EVENT, helper -> { + for (SoundEntry entry : ALL.values()) entry.register(helper); + }); + } + + public static void provideLang(BiConsumer consumer) { + for (SoundEntry entry : ALL.values()) + if (entry.hasSubtitle()) + consumer.accept(entry.getSubtitleKey(), entry.getSubtitle()); + } + + public static SoundEntryProvider provider(DataGenerator generator) { + return new SoundEntryProvider(generator); + } + + public static void playItemPickup(Player player) { + player.level().playSound( + null, + player.blockPosition(), + SoundEvents.ITEM_PICKUP, + SoundSource.PLAYERS, + .2f, + 1f + player.level().random.nextFloat() + ); + } + + public static class SoundEntryProvider implements DataProvider { + + private PackOutput output; + + public SoundEntryProvider(DataGenerator generator) { + output = generator.getPackOutput(); + } + + @Override + public CompletableFuture run(CachedOutput cache) { + return generate(output.getOutputFolder(), cache); + } + + @Override + public String getName() { + return "CreateNuclear's Custom Sounds"; + } + + public CompletableFuture generate(Path path, CachedOutput cache) { + path = path.resolve("assets/createnuclear"); + JsonObject json = new JsonObject(); + ALL.entrySet() + .stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(entry -> { + entry.getValue() + .write(json); + }); + return DataProvider.saveStable(cache, json, path.resolve("sounds.json")); + } + + } + + public static class SoundEntryBuilder { + + protected ResourceLocation id; + protected String subtitle = "unregistered"; + protected SoundSource category = SoundSource.BLOCKS; + protected List wrappedEvents; + protected List variants; + protected int attenuationDistance; + + public SoundEntryBuilder(ResourceLocation id) { + wrappedEvents = new ArrayList<>(); + variants = new ArrayList<>(); + this.id = id; + } + + public SoundEntryBuilder subtitle(String subtitle) { + this.subtitle = subtitle; + return this; + } + + public SoundEntryBuilder attenuationDistance(int distance) { + this.attenuationDistance = distance; + return this; + } + + public SoundEntryBuilder noSubtitle() { + this.subtitle = null; + return this; + } + + public SoundEntryBuilder category(SoundSource category) { + this.category = category; + return this; + } + + public SoundEntryBuilder addVariant(String name) { + return addVariant(Create.asResource(name)); + } + + public SoundEntryBuilder addVariant(ResourceLocation id) { + variants.add(id); + return this; + } + + public SoundEntryBuilder playExisting(Supplier event, float volume, float pitch) { + wrappedEvents.add(new ConfiguredSoundEvent(event, volume, pitch)); + return this; + } + + public SoundEntryBuilder playExisting(SoundEvent event, float volume, float pitch) { + return playExisting(() -> event, volume, pitch); + } + + public SoundEntryBuilder playExisting(SoundEvent event) { + return playExisting(event, 1, 1); + } + + public SoundEntryBuilder playExisting(Holder event) { + return playExisting(event::value, 1, 1); + } + + public SoundEntry build() { + SoundEntry entry = + wrappedEvents.isEmpty() ? new CustomSoundEntry(id, variants, subtitle, category, attenuationDistance) + : new WrappedSoundEntry(id, subtitle, wrappedEvents, category, attenuationDistance); + ALL.put(entry.getId(), entry); + return entry; + } + + } + + private static class CustomSoundEntry extends SoundEntry { + + protected List variants; + protected DeferredHolder event; + + public CustomSoundEntry(ResourceLocation id, List variants, String subtitle, + SoundSource category, int attenuationDistance) { + super(id, subtitle, category, attenuationDistance); + this.variants = variants; + } + + @Override + public void prepare() { + event = DeferredHolder.create(Registries.SOUND_EVENT, id); + } + + @Override + public void register(RegisterEvent.RegisterHelper helper) { + ResourceLocation location = event.getId(); + helper.register(location, SoundEvent.createVariableRangeEvent(location)); + } + + @Override + public Holder getMainEventHolder() { + return event; + } + + @Override + public SoundEvent getMainEvent() { + return event.get(); + } + + @Override + public void write(JsonObject json) { + JsonObject entry = new JsonObject(); + JsonArray list = new JsonArray(); + + JsonObject s = new JsonObject(); + s.addProperty("name", id.toString()); + s.addProperty("type", "file"); + if (attenuationDistance != 0) + s.addProperty("attenuation_distance", attenuationDistance); + list.add(s); + + for (ResourceLocation variant : variants) { + s = new JsonObject(); + s.addProperty("name", variant.toString()); + s.addProperty("type", "file"); + if (attenuationDistance != 0) + s.addProperty("attenuation_distance", attenuationDistance); + list.add(s); + } + + entry.add("sounds", list); + if (hasSubtitle()) + entry.addProperty("subtitle", getSubtitleKey()); + json.add(id.getPath(), entry); + } + + @Override + public void play(Level world, Player entity, double x, double y, double z, float volume, float pitch) { + world.playSound(entity, x, y, z, event.get(), category, volume, pitch); + } + + @Override + public void playAt(Level world, double x, double y, double z, float volume, float pitch, boolean fade) { + world.playLocalSound(x, y, z, event.get(), category, volume, pitch, fade); + } + + } + + private static class WrappedSoundEntry extends SoundEntry { + + private List wrappedEvents; + private List compiledEvents; + + public WrappedSoundEntry(ResourceLocation id, String subtitle, + List wrappedEvents, SoundSource category, int attenuationDistance) { + super(id, subtitle, category, attenuationDistance); + this.wrappedEvents = wrappedEvents; + compiledEvents = new ArrayList<>(); + } + + @Override + public void prepare() { + for (int i = 0; i < wrappedEvents.size(); i++) { + ConfiguredSoundEvent wrapped = wrappedEvents.get(i); + ResourceLocation location = getIdOf(i); + DeferredHolder event = DeferredHolder.create(Registries.SOUND_EVENT, location); + compiledEvents.add(new CompiledSoundEvent(event, wrapped.volume(), wrapped.pitch())); + } + } + + @Override + public void register(RegisterEvent.RegisterHelper helper) { + for (CompiledSoundEvent compiledEvent : compiledEvents) { + ResourceLocation location = compiledEvent.event().getId(); + helper.register(location, SoundEvent.createVariableRangeEvent(location)); + } + } + + @Override + public Holder getMainEventHolder() { + return compiledEvents.getFirst().event(); + } + + @Override + public SoundEvent getMainEvent() { + return compiledEvents.get(0) + .event().get(); + } + + protected ResourceLocation getIdOf(int i) { + return ResourceLocation.fromNamespaceAndPath(id.getNamespace(), i == 0 ? id.getPath() : id.getPath() + "_compounded_" + i); + } + + @Override + public void write(JsonObject json) { + for (int i = 0; i < wrappedEvents.size(); i++) { + ConfiguredSoundEvent event = wrappedEvents.get(i); + JsonObject entry = new JsonObject(); + JsonArray list = new JsonArray(); + JsonObject s = new JsonObject(); + s.addProperty("name", event.event() + .get() + .getLocation() + .toString()); + s.addProperty("type", "event"); + if (attenuationDistance != 0) + s.addProperty("attenuation_distance", attenuationDistance); + list.add(s); + entry.add("sounds", list); + if (i == 0 && hasSubtitle()) + entry.addProperty("subtitle", getSubtitleKey()); + json.add(getIdOf(i).getPath(), entry); + } + } + + @Override + public void play(Level world, Player entity, double x, double y, double z, float volume, float pitch) { + for (WrappedSoundEntry.CompiledSoundEvent event : compiledEvents) { + world.playSound(entity, x, y, z, event.event().get(), category, event.volume() * volume, + event.pitch() * pitch); + } + } + + @Override + public void playAt(Level world, double x, double y, double z, float volume, float pitch, boolean fade) { + for (WrappedSoundEntry.CompiledSoundEvent event : compiledEvents) { + world.playLocalSound(x, y, z, event.event().get(), category, event.volume() * volume, + event.pitch() * pitch, fade); + } + } + + private record CompiledSoundEvent(DeferredHolder event, float volume, float pitch) { + } + + } + +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/CNTags.java b/src/main/java/net/nuclearteam/createnuclear/CNTags.java index 5582652..29cbea3 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CNTags.java +++ b/src/main/java/net/nuclearteam/createnuclear/CNTags.java @@ -72,7 +72,7 @@ public enum NameSpace { public enum CNBlockTags { FAN_PROCESSING_CATALYSTS_ENRICHED(MOD, "fan_processing_catalysts/enriched"), ENRICHING_FIRE_BASE_BLOCKS, - ALL_CAMPFIRES(MINECRAFT, "all/campfires"), + THORIUM_ORES, URANIUM_ORES, LEAD_ORES ; @@ -127,15 +127,9 @@ public enum CNItemTags { COOLER, URANIUM_ORES, LEAD_ORES, - ANTI_RADIATION_HELMET_DYE, - ANTI_RADIATION_CHESTPLATE_DYE, - ANTI_RADIATION_LEGGINGS_DYE, - ANTI_RADIATION_BOOTS_DYE, ANTI_RADIATION_ARMOR, - ALL_ANTI_RADIATION_ARMORS, ANTI_RADIATION_HELMET_FULL_DYE, - ANTI_RADIATION_CHESTPLATE_FULL_DYE, - ANTI_RADIATION_LEGGINGS_FULL_DYE, + THORIUM_ORES, ; public final TagKey tag; @@ -179,7 +173,9 @@ private static void init() {} } public enum CNFluidTags { - URANIUM + URANIUM, + THORIUM, + NITROGEN ; public final TagKey tag; diff --git a/src/main/java/net/nuclearteam/createnuclear/CreateNuclear.java b/src/main/java/net/nuclearteam/createnuclear/CreateNuclear.java index cf33877..5e46619 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CreateNuclear.java +++ b/src/main/java/net/nuclearteam/createnuclear/CreateNuclear.java @@ -2,19 +2,16 @@ import com.mojang.logging.LogUtils; -import com.simibubi.create.AllDataComponents; import com.simibubi.create.CreateBuildInfo; import com.simibubi.create.foundation.data.CreateRegistrate; import com.simibubi.create.foundation.item.ItemDescription; import com.simibubi.create.foundation.item.KineticStats; import com.simibubi.create.foundation.item.TooltipModifier; -import com.simibubi.create.infrastructure.data.CreateDatagen; import net.createmod.catnip.lang.FontHelper; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.CreativeModeTab; -import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; @@ -26,10 +23,13 @@ import net.nuclearteam.createnuclear.content.decoration.palettes.CNPaletteBlocks; import net.nuclearteam.createnuclear.content.equipment.armor.CNArmorMaterials; import net.nuclearteam.createnuclear.content.kinetics.fan.processing.CNFanProcessingTypes; +import net.nuclearteam.createnuclear.content.radiation.CNRadiationValues; +import net.nuclearteam.createnuclear.content.radiation.capability.RadiationCapability; import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancement; import net.nuclearteam.createnuclear.foundation.advancement.CNTriggers; import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; import net.nuclearteam.createnuclear.infrastructure.data.CreateNuclearDatagen; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.surfacerule.BiomeTagRule; import org.slf4j.Logger; import com.simibubi.create.api.registrate.CreateRegistrateRegistrationCallback; @@ -65,7 +65,7 @@ public static void onCtor(IEventBus modEventBus, ModContainer modContainer) { REGISTRATE.registerEventListeners(modEventBus); - + CNSoundEvents.prepare(); CNTags.init(); CNBlocks.register(); CNBlockEntityTypes.register(); @@ -78,13 +78,17 @@ public static void onCtor(IEventBus modEventBus, ModContainer modContainer) { CNArmorMaterials.register(modEventBus); CNDataComponents.register(modEventBus); + CNAttachmentTypes.register(modEventBus); CNConfigs.register(modLoadingContext, modContainer); CNCreativeModeTabs.register(modEventBus); CNEffects.register(modEventBus); CNPotions.register(modEventBus); + CNParticleTypes.register(modEventBus); + CNParticleRegistry.DEF_REG.register(modEventBus); CNRecipeTypes.register(modEventBus); + CNAttributes.register(modEventBus); modEventBus.addListener(CreateNuclear::init); modEventBus.addListener(CreateNuclear::onRegister); @@ -98,10 +102,17 @@ public static void onCtor(IEventBus modEventBus, ModContainer modContainer) { public static void init(final FMLCommonSetupEvent event) { CNFluids.registerFluidInteractions(); + // Runs here rather than at mod construction: the item entries it reads (e.g. Create's + // CRUSHED_URANIUM) are only bound once the registry events have finished. + CNRadiationValues.register(); + + event.enqueueWork(CNOpenPipeEffectHandlers::registerDefaults); } public static void onRegister(final RegisterEvent event) { + CNSoundEvents.register(event); CNFanProcessingTypes.register(); + BiomeTagRule.register(event); if (event.getRegistry() == BuiltInRegistries.TRIGGER_TYPES) { CNAdvancement.register(); diff --git a/src/main/java/net/nuclearteam/createnuclear/CreateNuclearClient.java b/src/main/java/net/nuclearteam/createnuclear/CreateNuclearClient.java index a556d60..b8de154 100644 --- a/src/main/java/net/nuclearteam/createnuclear/CreateNuclearClient.java +++ b/src/main/java/net/nuclearteam/createnuclear/CreateNuclearClient.java @@ -20,11 +20,9 @@ public static void onCtorClient(IEventBus modEventBus) { IEventBus neoEventBus = NeoForge.EVENT_BUS; modEventBus.addListener(CreateNuclearClient::clientInit); - } public static void clientInit(final FMLClientSetupEvent event) { PonderIndex.addPlugin(new CreateNuclearPonderPlugin()); - } } diff --git a/src/main/java/net/nuclearteam/createnuclear/api/CreateNuclearRegistries.java b/src/main/java/net/nuclearteam/createnuclear/api/CreateNuclearRegistries.java new file mode 100644 index 0000000..8b2a178 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/CreateNuclearRegistries.java @@ -0,0 +1,17 @@ +package net.nuclearteam.createnuclear.api; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.api.multiblock.fluid.ReactorFluidType; +import net.nuclearteam.createnuclear.api.multiblock.rods.RodType; + + +public class CreateNuclearRegistries { + public static final ResourceKey> ROD_TYPE = key("rods/type"); + public static final ResourceKey> FLUID_TYPE = key("fluids/type"); + + private static ResourceKey> key(String name) { + return ResourceKey.createRegistryKey(CreateNuclear.asResource(name)); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/api/ItemRodTypesValue.java b/src/main/java/net/nuclearteam/createnuclear/api/ItemRodTypesValue.java new file mode 100644 index 0000000..53355a4 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/ItemRodTypesValue.java @@ -0,0 +1,78 @@ +package net.nuclearteam.createnuclear.api; + +import com.simibubi.create.api.registry.SimpleRegistry; +import com.tterrag.registrate.util.nullness.NonNullConsumer; +import net.minecraft.world.item.Item; +import net.nuclearteam.createnuclear.api.multiblock.rods.RodType; +import static net.nuclearteam.createnuclear.api.multiblock.rods.RodType.TypeRod.NONE; + +import java.util.Collections; + +import net.minecraft.core.HolderSet; + +public class ItemRodTypesValue { + public static final SimpleRegistry ROD_TYPE = SimpleRegistry.create(); + /** + * Default RodType returned when no mapping exists in the registry. + * Built once to avoid allocating a new instance on each lookup. + */ + public static final RodType DEFAULT_ROD_TYPE = new RodType(HolderSet.direct(Collections.emptyList()), -1, -1, -1, NONE); + + /** + * Get the {@link RodType} associated with the given {@link Item}. + * If no mapping exists, a default empty {@link RodType} is returned. + * + * @param item the item to look up (must not be null) + * @return the associated {@link RodType} or a default instance if none present + * @throws NullPointerException if {@code item} is null + */ + public static RodType getRodType(Item item) { + RodType type = ROD_TYPE.get(item); + return type == null ? DEFAULT_ROD_TYPE : type; + } + + /** + * Create a consumer that registers the given {@link RodType} for an item. + * + * @param type the rod type to register (must not be null) + * @return a non-null consumer that registers the mapping + * @throws NullPointerException if {@code type} is null + */ + public static NonNullConsumer setRodTypeInfos(RodType.Builder type) { + return item -> ROD_TYPE.register(item, type.addItems(item).build()); + } + + + /** + * Create a consumer that builds a {@link RodType} using the provided numeric + * parameters and registers it for the item passed to the consumer. + * + * The {@code type} parameter controls whether the builder configures the + * rod as a cooler, fuel or keeps the default mixed behaviour. For MIXTE + * we keep the builder default and do not call any explicit setter. + * + * @param baseRodHeat base heat value + * @param proximityRodHeat heat influenced by proximity + * @param rodTimer rod life / timer value + * @param type enum controlling cooler/fuel/mixte behaviour (must not be null) + * @return a non-null consumer that registers the built RodType + * @throws NullPointerException if {@code type} is null + */ + public static NonNullConsumer setRodTypeInfos(int baseRodHeat, int proximityRodHeat, int rodTimer, RodType.TypeRod type) { + RodType.Builder builder = new RodType.Builder() + .baseRodHeat(baseRodHeat) + .proximityRodHeat(proximityRodHeat) + .rodTimer(rodTimer); + + switch (type) { + case COOLER -> builder.coolerRodType(); + case FUEL -> builder.fuelRodType(); + default -> throw new IllegalArgumentException("Unsupported TypeRod: " + type + ". This error occurred during rod stats initialization in the CreateNuclearForge mod."); + } + + return item -> { + RodType built = builder.addItems(item).build(); + ROD_TYPE.register(item, built); + }; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/api/ReactorFluidTypesValue.java b/src/main/java/net/nuclearteam/createnuclear/api/ReactorFluidTypesValue.java new file mode 100644 index 0000000..67a05e4 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/ReactorFluidTypesValue.java @@ -0,0 +1,84 @@ +package net.nuclearteam.createnuclear.api; + +import com.simibubi.create.api.registry.SimpleRegistry; +import com.tterrag.registrate.util.nullness.NonNullConsumer; +import net.minecraft.core.HolderSet; +import net.minecraft.world.level.material.Fluid; +import net.nuclearteam.createnuclear.api.multiblock.fluid.ReactorFluidType; + +import java.util.Collections; + +/** + * Registry-backed accessors for {@link ReactorFluidType} mappings. + *

+ * This class provides a simple registry that maps {@link Fluid} instances to + * their corresponding {@link ReactorFluidType} definitions. It also exposes + * helper methods that produce consumers suitable for registering reactor + * fluid type information during mod initialization. + */ +public class ReactorFluidTypesValue { + /** + * Simple in-memory registry mapping fluids to {@link ReactorFluidType}. + */ + public static final SimpleRegistry REACTOR_FLUID_TYPE = SimpleRegistry.create(); + + /** + * Default {@link ReactorFluidType} returned when no mapping exists in the + * registry. Constructed once to avoid allocating a new instance on each lookup. + */ + public static final ReactorFluidType DEFAULT_REACTOR_FLUID_TYPE = new ReactorFluidType(HolderSet.direct(Collections.emptyList()), -1, -1); + + /** + * Get the {@link ReactorFluidType} associated with the given {@link Fluid}. + * If no mapping exists, a default empty {@link ReactorFluidType} is returned. + * + * @param fluid the fluid to look up (must not be null) + * @return the associated {@link ReactorFluidType} or a default instance if none present + * @throws NullPointerException if {@code fluid} is null + */ + public static ReactorFluidType getReactorFluidType(Fluid fluid) { + ReactorFluidType type = REACTOR_FLUID_TYPE.get(fluid); + return type == null ? DEFAULT_REACTOR_FLUID_TYPE : type; + } + + /** + * Create a consumer that registers the given {@link ReactorFluidType} + * builder for a fluid. + *

+ * The returned consumer will call {@link ReactorFluidType.Builder#fluid(Fluid)} + * with the provided fluid and then build and register the resulting + * {@link ReactorFluidType} into the registry. + * + * @param type the reactor fluid type builder to register (must not be null) + * @return a non-null consumer that registers the mapping + */ + public static NonNullConsumer setReactorFluidTypeInfos(ReactorFluidType.Builder type) { + return fluid -> REACTOR_FLUID_TYPE.register(fluid, type.fluid(fluid).build()); + } + + /** + * Create a consumer that builds a {@link ReactorFluidType} using the + * provided numeric parameters and registers it for the fluid passed to + * the consumer. + *

+ * The returned consumer will construct a builder pre-configured with the + * provided {@code maxHeat} and {@code efficiency} values, then call + * {@link ReactorFluidType.Builder#fluid(Fluid)} and register the built + * instance for the supplied fluid. + * + * @param maxHeat maximum heat value to apply to the built type + * @param efficiency efficiency value to apply to the built type + * @return a non-null consumer that registers the built {@link ReactorFluidType} + */ + public static NonNullConsumer setReactorFluidTypeInfos(int maxHeat, int efficiency) { + ReactorFluidType.Builder builder = new ReactorFluidType.Builder() + .maxHeat(maxHeat) + .efficiency(efficiency); + + return fluid -> { + ReactorFluidType built = builder.fluid(fluid).build(); + REACTOR_FLUID_TYPE.register(fluid, built); + }; + } + +} diff --git a/src/main/java/net/nuclearteam/createnuclear/api/data/recipe/EnrichedRecipeGen.java b/src/main/java/net/nuclearteam/createnuclear/api/data/recipe/EnrichedRecipeGen.java new file mode 100644 index 0000000..73402df --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/data/recipe/EnrichedRecipeGen.java @@ -0,0 +1,21 @@ +package net.nuclearteam.createnuclear.api.data.recipe; + +import com.simibubi.create.api.data.recipe.ProcessingRecipeGen; +import com.simibubi.create.foundation.recipe.IRecipeTypeInfo; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.nuclearteam.createnuclear.CNRecipeTypes; + +import java.util.concurrent.CompletableFuture; + +public abstract class EnrichedRecipeGen extends ProcessingRecipeGen { + + public EnrichedRecipeGen(PackOutput generator, CompletableFuture registries, String defaultNamespace) { + super(generator, registries, defaultNamespace); + } + + @Override + protected IRecipeTypeInfo getRecipeType() { + return CNRecipeTypes.ENRICHED; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/api/data/recipe/SmithingClothRecipeBuilder.java b/src/main/java/net/nuclearteam/createnuclear/api/data/recipe/SmithingClothRecipeBuilder.java new file mode 100644 index 0000000..857323b --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/data/recipe/SmithingClothRecipeBuilder.java @@ -0,0 +1,59 @@ +package net.nuclearteam.createnuclear.api.data.recipe; + +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.AdvancementRewards; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.SmithingTransformRecipe; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class SmithingClothRecipeBuilder { + private final Ingredient template; + private final Ingredient base; + private final Ingredient addition; + private final RecipeCategory category; + private final ItemStack result; + private final Map> criteria = new LinkedHashMap<>(); + + public SmithingClothRecipeBuilder(Ingredient pTemplate, Ingredient pBase, Ingredient pAddition, RecipeCategory pCategory, ItemStack pResult) { + this.category = pCategory; + this.template = pTemplate; + this.base = pBase; + this.addition = pAddition; + this.result = pResult; + } + + public static SmithingClothRecipeBuilder smithingCloth(Ingredient pTemplate, Ingredient pBase, Ingredient pAddition, RecipeCategory pCategory, ItemStack pResult) { + return new SmithingClothRecipeBuilder(pTemplate, pBase, pAddition, pCategory, pResult); + } + + public SmithingClothRecipeBuilder unlocks(String pKey, Criterion pCriterion) { + this.criteria.put(pKey, pCriterion); + return this; + } + + public void save(RecipeOutput pRecipeOutput, ResourceLocation pLocation) { + this.ensureValid(pLocation); + Advancement.Builder advancement = pRecipeOutput.advancement() + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(pLocation)) + .rewards(AdvancementRewards.Builder.recipe(pLocation)) + .requirements(AdvancementRequirements.Strategy.OR); + this.criteria.forEach(advancement::addCriterion); + SmithingTransformRecipe recipe = new SmithingTransformRecipe(this.template, this.base, this.addition, this.result); + pRecipeOutput.accept(pLocation, recipe, advancement.build(pLocation.withPrefix("recipes/" + this.category.getFolderName() + "/"))); + } + + private void ensureValid(ResourceLocation pLocation) { + if (this.criteria.isEmpty()) { + throw new IllegalStateException("No way of obtaining recipe " + pLocation); + } + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/api/multiblock/MultiBlockManagerBeta.java b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/MultiBlockManagerBeta.java index ce79b01..7079cd3 100644 --- a/src/main/java/net/nuclearteam/createnuclear/api/multiblock/MultiBlockManagerBeta.java +++ b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/MultiBlockManagerBeta.java @@ -4,6 +4,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.Level; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; import java.util.ArrayList; import java.util.List; @@ -17,7 +19,7 @@ public void register(String id, T data, IMultiBlockPattern blockPattern) { structures.add(new BlockPattern<>(id, data, blockPattern)); } - public BlockPattern findStructure(Level level, BlockPos pos) { + public BlockPattern findStructure(Level level, BlockPos pos, ReactorControllerBlockEntity entity) { List directions = new ArrayList<>(); directions.add(Direction.NORTH); directions.add(Direction.WEST); @@ -26,8 +28,11 @@ public BlockPattern findStructure(Level level, BlockPos pos) { for (Direction direction : directions) { for (BlockPattern structure : structures) { - var result = structure.structure().matches(level, pos, direction); - if (result) return structure; + boolean result = structure.structure().matches(level, pos, direction); + if (result){ + entity.setMultiblockFacing(direction.getCounterClockWise().getName()); + return structure; + } } } diff --git a/src/main/java/net/nuclearteam/createnuclear/api/multiblock/TypeMultiblock.java b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/TypeMultiblock.java index e22beb1..9f52c6b 100644 --- a/src/main/java/net/nuclearteam/createnuclear/api/multiblock/TypeMultiblock.java +++ b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/TypeMultiblock.java @@ -1,5 +1,26 @@ package net.nuclearteam.createnuclear.api.multiblock; +import net.nuclearteam.createnuclear.foundation.utility.CreateNuclearLang; + public enum TypeMultiblock { - REACTOR + REACTOR_T1(5), + REACTOR_T2(7), + REACTOR_T3(9) + ; + + private final int size; + private final String name; + + TypeMultiblock(int size) { + this.size = size; + this.name = CreateNuclearLang.asId(name()); + } + + public int getSize() { + return this.size; + } + + public String getName() { + return this.name; + } } diff --git a/src/main/java/net/nuclearteam/createnuclear/api/multiblock/fluid/ReactorFluidType.java b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/fluid/ReactorFluidType.java new file mode 100644 index 0000000..97422ea --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/fluid/ReactorFluidType.java @@ -0,0 +1,229 @@ +package net.nuclearteam.createnuclear.api.multiblock.fluid; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.Holder; +import net.minecraft.core.Holder.Reference; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.RegistryCodecs; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; +import net.nuclearteam.createnuclear.api.CreateNuclearRegistries; +import net.nuclearteam.createnuclear.api.ReactorFluidTypesValue; +import net.nuclearteam.createnuclear.content.multiblock.fluid.CNReactorFluidTypes; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Represents a reactor fluid type used by the mod's multiblock. + *

+ * A {@code ReactorFluidType} holds a set of fluids that identify this + * fluid type, heat-related and efficiency values, and a flag indicating + * whether those values should be read from configuration at runtime. + */ +public record ReactorFluidType(HolderSet fluids, int maxHeat, int efficiency, boolean useConfig) { + public ReactorFluidType(HolderSet fluids, int maxHeat, int efficiency) { + this(fluids, maxHeat, efficiency, false); + } + + /** Serialization codec for saving/loading {@link ReactorFluidType} instances. */ + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + RegistryCodecs.homogeneousList(Registries.FLUID).fieldOf("fluids").forGetter(ReactorFluidType::fluids), + Codec.INT.fieldOf("maxHeat").forGetter(ReactorFluidType::maxHeat), + Codec.INT.fieldOf("efficiency").forGetter(ReactorFluidType::efficiency) + ).apply(i, ReactorFluidType::new)); + + /** + * Searches the {@code FLUID_TYPE} registry for a {@link ReactorFluidType} + * matching the provided {@link Fluid}. + * + * @param registryAccess registry access for the world + * @param fluid the fluid to look up + * @return an {@link Optional} containing a {@link Reference} to the matching + * {@code ReactorFluidType} if found, otherwise {@link Optional#empty()} + */ + public static Optional> getTypeForFluid(RegistryAccess registryAccess, Fluid fluid) { + return registryAccess.lookupOrThrow(CreateNuclearRegistries.FLUID_TYPE) + .listElements() + .filter(ref -> ref.value().fluids.stream() + .anyMatch(h -> h.unwrapKey() + .map(k -> Optional.ofNullable(BuiltInRegistries.FLUID.getKey(fluid)).map(k.location()::equals).orElse(false)) + .orElse(false))) + .findFirst(); + } + + /** + * Resolves the {@code ReactorFluidType} for a given {@link Fluid}. + *

+ * The resolution first checks the world's registries, then falls back to + * {@link ReactorFluidTypesValue}, and finally returns the registry fallback + * value if none is found. + * + * @param fluid the fluid whose reactor type should be resolved + * @param world the level used to access registries + * @return the resolved {@code ReactorFluidType} (never {@code null}) + */ + public static ReactorFluidType resolveReactorFluidType(Fluid fluid, Level world) { + return ReactorFluidType.getTypeForFluid(world.registryAccess(), fluid) + .map(Reference::value) + .orElseGet(() -> { + ReactorFluidType fromFluid = ReactorFluidTypesValue.getReactorFluidType(fluid); + return fromFluid.isNotEmptyFluid() + ? fromFluid + : world.registryAccess() + .registryOrThrow(CreateNuclearRegistries.FLUID_TYPE) + .getHolderOrThrow(CNReactorFluidTypes.FALLBACK) + .value(); + }); + } + + + /** + * Returns whether this {@code ReactorFluidType} has at least one associated fluid. + * + * @return {@code true} if one or more fluids are defined, {@code false} otherwise + */ + public boolean isNotEmptyFluid() { + return this.fluids.size() >= 1; + } + + /** + * Fluent builder for creating an immutable {@link ReactorFluidType} instance. + *

+ * Use the configuration methods and class {@link #build()} to obtain the + * resulting instance. + */ + public static class Builder { + private final List> fluids = new ArrayList<>(); + private int maxHeat = 0; + private int efficiency = 0; + private boolean useConfig = false; + + private boolean fluidSet = false; + private boolean maxHeatSet = false; + private boolean efficiencySet = false; + + /** + * Sets the maximum heat value for the built {@link ReactorFluidType}. + * + * @param maxHeat maximum heat value + * @return this builder + */ + public Builder maxHeat(int maxHeat) { + this.maxHeat = maxHeat; + this.maxHeatSet = true; + return this; + } + + /** + * Sets the efficiency value for the built {@link ReactorFluidType}. + * + * @param efficiency efficiency value + * @return this builder + */ + public Builder efficiency(int efficiency) { + this.efficiency = efficiency; + this.efficiencySet = true; + return this; + } + + /** + * Adds a fluid to this type from a {@link FluidStack}. + * + * @param fluidStack the fluid stack providing the fluid + * @return this builder + */ + public Builder fluid(FluidStack fluidStack) { + this.fluids.add(fluidStack.getFluid().builtInRegistryHolder()); + this.fluidSet = true; + return this; + } + + /** + * Adds a fluid to this type directly. + * + * @param fluid the fluid to add + * @return this builder + */ + public Builder fluid(Fluid fluid) { + this.fluids.add(fluid.builtInRegistryHolder()); + this.fluidSet = true; + return this; + } + + /** + * Marks this type to resolve numeric values from configuration at runtime. + * + * @return this builder + */ + public Builder setRodConfig() { + this.useConfig = true; + return this; + } + + /** + * Builds the immutable {@link ReactorFluidType} instance. + * + * @throws IllegalStateException if required fields are missing + * @return the created instance + */ + public ReactorFluidType build() { + List missing = new ArrayList<>(); + if (!fluidSet || fluids.isEmpty()) missing.add("fluids"); + + if (!this.useConfig) { + if (!maxHeatSet) missing.add("maxHeat"); + if (!efficiencySet) missing.add("efficiency"); + } + + if (!missing.isEmpty()) + throw new IllegalStateException("Missing required RodType fields: " + String.join(", ", missing)); + + return new ReactorFluidType(HolderSet.direct(fluids), maxHeat, efficiency, useConfig); + } + } + + @Override + public int maxHeat() { + if (!useConfig) return maxHeat; + try { + return 12; //CNConfigs.server().rods.maxHeat.get(); + } catch (IllegalStateException e) { + return maxHeat; + } + } + + @Override + public int efficiency() { + if (!useConfig) return efficiency; + try { + return 12; //CNConfigs.server().rods.maxHeat.get(); + } catch (IllegalStateException e) { + return efficiency; + } + } + + @Override + public @NotNull String toString() { + String fluidNames = this.fluids.stream() + .map(h -> h.unwrapKey() + .map(k -> k.location().toString()) + .orElseGet(() -> { + ResourceLocation rl = BuiltInRegistries.FLUID.getKey(h.value()); + return rl != null ? rl.toString() : h.value().toString(); + }) + ) + .collect(Collectors.joining(", ")); + + return "ReactorFluidType{fluids=[" + fluidNames + "], maxHeat=" + maxHeat() + ", efficiency=" + efficiency() + "}"; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/api/multiblock/rods/RodType.java b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/rods/RodType.java new file mode 100644 index 0000000..2a17dc3 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/multiblock/rods/RodType.java @@ -0,0 +1,327 @@ +package net.nuclearteam.createnuclear.api.multiblock.rods; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.Holder; +import net.minecraft.core.Holder.Reference; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.RegistryCodecs; +import net.minecraft.core.registries.Registries; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.Level; +import net.nuclearteam.createnuclear.CNTags.CNItemTags; +import net.nuclearteam.createnuclear.api.CreateNuclearRegistries; +import net.nuclearteam.createnuclear.api.ItemRodTypesValue; +import net.nuclearteam.createnuclear.content.rod.CNRodTypes; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; + +/** + * Represents a rod type used by the mod's multiblock. + *

+ * A {@code RodType} holds a set of items that can represent this rod type, + * heat-related values, a timing value, and a {@link TypeRod} indicating the + * category (fuel, cooler, or mixed). + */ +@MethodsReturnNonnullByDefault +public record RodType(HolderSet items, + int baseRodHeat, float proximityRodHeat, + int rodTimer, TypeRod type, boolean useConfig) { + + public RodType(HolderSet items, + int baseRodHeat, float proximityRodHeat, + int rodTimer, TypeRod type) { + this(items, baseRodHeat, proximityRodHeat, rodTimer, type, false); + } + + /** + * Serialization codec for saving/loading {@link RodType} instances. + */ + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + RegistryCodecs.homogeneousList(Registries.ITEM).fieldOf("items").forGetter(RodType::items), + Codec.INT.fieldOf("baseRodHeat").forGetter(RodType::baseRodHeat), + Codec.FLOAT.fieldOf("proximityRodHeat").forGetter(RodType::proximityRodHeat), + Codec.INT.fieldOf("rodTimer").forGetter(RodType::rodTimer), + StringRepresentable.fromEnum(TypeRod::values).fieldOf("type").forGetter(RodType::type) + ).apply(i, RodType::new)); + + /** + * Searches the {@code ROD_TYPE} registry for a {@link RodType} matching the + * provided {@link Item}. + * + * @param registryAccess registry access for the world + * @param item the item to look up + * @return an {@link Optional} containing a {@link Reference} to the matching + * {@code RodType} if found, otherwise {@link Optional#empty()} + */ + public static Optional> getTypeForItem(RegistryAccess registryAccess, Item item) { + return registryAccess.lookupOrThrow(CreateNuclearRegistries.ROD_TYPE) + .listElements() + .filter(ref -> ref.value().items.contains(item.builtInRegistryHolder())) + .findFirst(); + } + + /** + * Resolves the {@code RodType} for a given {@link Item}. The method first + * checks the world's registries, then falls back to {@link ItemRodTypesValue}, + * and lastly returns a fallback registry value if none is found. + * + * @param item the item whose rod type should be resolved + * @param world the world (level) used to access registries + * @return the resolved {@code RodType} (never {@code null}) + */ + public static RodType resolveRodType(Item item, Level world) { + return RodType.getTypeForItem(world.registryAccess(), item) + .map(Reference::value) + .orElseGet(() -> { + RodType fromItem = ItemRodTypesValue.getRodType(item); + return fromItem.isNotEmptyItem() + ? fromItem + : world.registryAccess() + .registryOrThrow(CreateNuclearRegistries.ROD_TYPE) + .getHolderOrThrow(CNRodTypes.FALLBACK) + .value(); + }); + } + + /** + * Returns whether this {@code RodType} has no associated items. + * + * @return {@code true} if no items are defined, {@code false} otherwise + */ + public boolean isNotEmptyItem() { + return this.items.size() >= 1; + } + + /** + * Fluent builder for creating an immutable {@link RodType} instance. + *

+ * Use the configuration methods and call {@link #build()} to obtain the + * resulting instance. + */ + public static class Builder { + private final List> items = new ArrayList<>(); + private int baseRodHeat = 0; + private int proximityRodHeat = 0; + private int rodTimer = 0; + private TypeRod type = TypeRod.FUEL; + private boolean useConfig = false; + + private boolean itemsSet = false; + private boolean baseRodHeatSet = false; + private boolean proximityRodHeatSet = false; + private boolean rodTimerSet = false; + private boolean typeSet = false; + + /** + * Sets the base heat for the rod. + * + * @param baseRodHeat base heat value + * @return this builder + */ + public Builder baseRodHeat(int baseRodHeat) { + this.baseRodHeat = baseRodHeat; + this.baseRodHeatSet = true; + return this; + } + + /** + * Sets the heat contributed by nearby rods. + * + * @param proximityRodHeat proximity heat value + * @return this builder + */ + public Builder proximityRodHeat(int proximityRodHeat) { + this.proximityRodHeat = proximityRodHeat; + this.proximityRodHeatSet = true; + return this; + } + + /** + * Sets the timer (duration) for the rod's behavior. + * + * @param rodTimer duration in ticks or mod-specific units + * @return this builder + */ + public Builder rodTimer(int rodTimer) { + this.rodTimer = rodTimer; + this.rodTimerSet = true; + return this; + } + + /** + * Sets the rod type to {@link TypeRod#COOLER}. + * + * @return this builder + */ + public Builder coolerRodType() { + this.type = TypeRod.COOLER; + this.typeSet = true; + return this; + } + + /** + * Sets the rod type to {@link TypeRod#FUEL}. + * + * @return this builder + */ + public Builder fuelRodType() { + this.type = TypeRod.FUEL; + this.typeSet = true; + return this; + } + + /** + * Adds one or more items that represent this rod type. + * + * @param items array of {@link ItemLike} elements to associate + * @return this builder + */ + public Builder addItems(ItemLike... items) { + for (ItemLike provider : items) + this.items.add(provider.asItem().builtInRegistryHolder()); + if (items.length > 0) this.itemsSet = true; + return this; + } + + public Builder setRodConfig() { + this.useConfig = true; + return this; + } + + /** + * Builds the immutable {@link RodType} instance. + * + * @throws IllegalStateException if required fields are missing + * @return the created instance + */ + public RodType build() { + List missing = new ArrayList<>(); + if (!itemsSet || items.isEmpty()) missing.add("items"); + if (!typeSet) missing.add("type"); + + if (!this.useConfig) { + if (!baseRodHeatSet) missing.add("baseRodHeat"); + if (!proximityRodHeatSet) missing.add("proximityRodHeat"); + if (!rodTimerSet) missing.add("rodTimer"); + } else { + /* Lazy config resolution: don't access CNConfigs here during registration. + * The actual values will be read at runtime via the resolved* accessors. + * Only MIXTE is considered an error for "useConfig" because there are + * no configuration defaults for the mixed type. + */ +// missing.add("Configuration defaults cannot be applied to the rod type"); + } + + if (!missing.isEmpty()) + throw new IllegalStateException("Missing required RodType fields: " + String.join(", ", missing)); + + return new RodType(HolderSet.direct(items), baseRodHeat, proximityRodHeat, rodTimer, type, useConfig); + } + } + + @Override + public int baseRodHeat() { + if (!useConfig) return baseRodHeat; + try { + return switch (type) { + //case FUEL -> CNConfigs.server().rods.baseValueUranium.get(); + //case COOLER -> CNConfigs.server().rods.baseValueGraphite.get(); + default -> baseRodHeat; + }; + } catch (IllegalStateException e) { + return baseRodHeat; + } + } + + @Override + public float proximityRodHeat() { + if (!useConfig) return proximityRodHeat; + try { + return switch (type) { + //case FUEL -> CNConfigs.server().rods.uraniumProxyBonus.get(); + //case COOLER -> CNConfigs.server().rods.graphiteProxyMalus.getF(); + default -> proximityRodHeat; + }; + } catch (IllegalStateException e) { + return proximityRodHeat; + } + } + + @Override + public int rodTimer() { + if (!useConfig) return rodTimer; + try { + return switch (type) { + //case FUEL -> CNConfigs.server().rods.uraniumRodLifetime.get(); + //case COOLER -> CNConfigs.server().rods.graphiteRodLifetime.get(); + default -> rodTimer; + }; + } catch (IllegalStateException e) { + return rodTimer; + } + } + + + public enum TypeRod implements StringRepresentable { + /** + * Fuel rod: generates heat via reaction. + */ + FUEL, + /** + * Cooler rod: reduces or absorbs heat. + */ + COOLER, + /** + * Sentinel value for the fallback/default rod type — no real category. + */ + NONE + ; + + @Override + public String getSerializedName() { + return name(); + } + } + + public static final class TypeRodPredicate { + public static final Predicate IS_NOT_NULL = Objects::nonNull; + + public static final Predicate IS_FUEL = s -> { + TypeRod type = ItemRodTypesValue.getRodType(s.getItem()).type; + return IS_NOT_NULL.test(s) && (s.is(CNItemTags.FUEL.tag) || type == TypeRod.FUEL); + }; + + public static final Predicate IS_COOLED = s -> { + TypeRod type = ItemRodTypesValue.getRodType(s.getItem()).type; + return IS_NOT_NULL.test(s) && (s.is(CNItemTags.COOLER.tag) || type == TypeRod.COOLER); + }; + + public static String tooltipKey(ItemStack stack) { + if (!IS_NOT_NULL.test(stack)) return "unknown"; + if (IS_FUEL.test(stack)) return "fuel"; + if (IS_COOLED.test(stack)) return "cooled"; + return "unknown"; + } + } + + @Override + public String toString() { + return "RodType [items: " + this.items() + + ", baseRodHeat: " + this.baseRodHeat() + + ", proximityRodHeat: " + this.proximityRodHeat() + + ", rodTimer: " + this.rodTimer() + + ", type: " + this.type() + + ", useConfig: " + this.useConfig() + "]"; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/api/radiation/IRadiationSource.java b/src/main/java/net/nuclearteam/createnuclear/api/radiation/IRadiationSource.java new file mode 100644 index 0000000..b55b90b --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/radiation/IRadiationSource.java @@ -0,0 +1,9 @@ +package net.nuclearteam.createnuclear.api.radiation; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; + +public interface IRadiationSource { + double getRadiation(ItemStack stack, LivingEntity entity); +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/api/radiation/RadiationRegistry.java b/src/main/java/net/nuclearteam/createnuclear/api/radiation/RadiationRegistry.java new file mode 100644 index 0000000..261bfcf --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/api/radiation/RadiationRegistry.java @@ -0,0 +1,98 @@ +package net.nuclearteam.createnuclear.api.radiation; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.biome.Biome; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class RadiationRegistry { + private static final Map ITEM_VALUE = new HashMap<>(); + private static final Map, Double> TAG_VALUE = new HashMap<>(); + private static final Map, Double> BIOME_VALUE = new HashMap<>(); + + private RadiationRegistry() {} + + public static Builder register() { + return new Builder(); + } + + public static double get(ItemStack stack) { + Item item = stack.getItem(); + + Double value = ITEM_VALUE.get(item); + if (value != null) return value; + + for (Entry, Double> entry : TAG_VALUE.entrySet()) { + if (stack.is(entry.getKey())) { + return entry.getValue(); + } + } + + return 0D; + } + + public static double get(ResourceKey stack) { + Double value = BIOME_VALUE.get(stack); + if (value != null) return value; + + return 0D; + } + + public static double getRadiation(ItemStack stack, @Nullable LivingEntity entity) { + return get(stack) * stack.getCount(); + } + public static double getRadiation(ResourceKey biome, @Nullable LivingEntity entity) { + return get(biome); + } + + public static class Builder { + private Item item; + private TagKey tag; + private ResourceKey biome; + private double value; + + public Builder item(Item item) { + this.item = item; + return this; + } + + public Builder tag(TagKey tag) { + this.tag = tag; + return this; + } + + public Builder biome(ResourceKey biome) { + this.biome = biome; + return this; + } + + public Builder value(double value) { + this.value = value; + return this; + } + + public void build() { + if (item != null) { + if (item instanceof IRadiationSource) + throw new IllegalStateException("Item " + item + " implements IRadiationSource and should not also be registered in RadiationRegistry (would cause double counting)."); + ITEM_VALUE.put(item, value); + } + + if (tag != null) { + TAG_VALUE.put(tag, value); + } + + if (biome != null) { + BIOME_VALUE.put(biome, value); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/biome/BiomeIrradiationExtractorItem.java b/src/main/java/net/nuclearteam/createnuclear/content/biome/BiomeIrradiationExtractorItem.java new file mode 100644 index 0000000..abc5aa0 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/biome/BiomeIrradiationExtractorItem.java @@ -0,0 +1,119 @@ +package net.nuclearteam.createnuclear.content.biome; + +import net.minecraft.ChatFormatting; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +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.item.TooltipFlag; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.Level; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.foundation.utility.CreateNuclearLang; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.BiomeIrradiationService; + +import java.util.List; + +public class BiomeIrradiationExtractorItem extends Item { + public static final String TAG = "biome_restore"; + private static final int CHARGE_PER_CLICK = 1; + + public BiomeIrradiationExtractorItem(Properties pProperties) { + super(pProperties); + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand interactionHand) { + ItemStack stack = player.getItemInHand(interactionHand); + + if (level.isClientSide || !(level instanceof ServerLevel serverLevel)) { + return InteractionResultHolder.success(stack); + } + + if (getCharge(stack) >= getMaxCharge()) + { + return InteractionResultHolder.pass(stack); + } + + boolean restored = BiomeIrradiationService.restoreArea(serverLevel, player.blockPosition()); + if (!restored) + { + return InteractionResultHolder.pass(stack); + } + + ItemStack charged = stack.copyWithCount(1); + addCharge(charged, CHARGE_PER_CLICK); + + stack.shrink(1); + if (stack.isEmpty()) { + return InteractionResultHolder.success(charged); + } + + if (!player.getInventory().add(charged)) { + player.drop(charged, false); + } + return InteractionResultHolder.success(stack); + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); + if (tooltipFlag.isAdvanced() && getCharge(stack) > 0) { + tooltipComponents.add( + CreateNuclearLang.translateDirect("tooltip.biome_irradiation_extractor." + TAG, getCharge(stack), getMaxCharge()) + .withStyle(ChatFormatting.GRAY) + ); + } + } + + @Override + public int getMaxStackSize(ItemStack stack) { + return getCharge(stack) > 0 ? 1 : this.getDefaultMaxStackSize(); + } + + @Override + public int getBarWidth(ItemStack stack) { + return Math.round(13.0f * getCharge(stack) / getMaxCharge()); + } + + @Override + public int getBarColor(ItemStack stack) { + return 0x4A90D9; + } + + public static int getCharge(ItemStack stack) { + return getChargeTag(stack, 0); + } + + public static int getMaxCharge() { + return CNConfigs.server().biomeRestore.maxCharge.get(); + } + + public static void addCharge(ItemStack stack, int amount) { + int current = getCharge(stack); + int next = Mth.clamp(current + amount, 0, getMaxCharge()); + + // Utilisation de la nouvelle API des composants pour stocker les données NBT + stack.update(DataComponents.CUSTOM_DATA, CustomData.EMPTY, customData -> + customData.update(tag -> tag.putInt(TAG, next)) + ); + } + + public static int getChargeTag(ItemStack stack, int defaultValue) { + if (stack == null || stack.isEmpty()) return defaultValue; + + // Récupération sécurisée via le composant CustomData de la 1.21 + CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + if (customData == null) return defaultValue; + + CompoundTag tag = customData.copyTag(); + return (tag.contains(TAG)) ? tag.getInt(TAG) : defaultValue; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/contraptions/irradiated/CNModelLayers.java b/src/main/java/net/nuclearteam/createnuclear/content/contraptions/irradiated/CNModelLayers.java index e62c785..ab29370 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/contraptions/irradiated/CNModelLayers.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/contraptions/irradiated/CNModelLayers.java @@ -12,6 +12,7 @@ public class CNModelLayers { public static final ModelLayerLocation IRRADIATED_CHICKEN = register("irradiated_chicken"); public static final ModelLayerLocation IRRADIATED_WOLF = register("irradiated_wolf"); public static final ModelLayerLocation IRRADIATED_CAT = register("irradiated_cat"); + public static final ModelLayerLocation ANTI_RADIATION_ARMOR = register("anti_radiation_armor"); private static ModelLayerLocation register(String path) { return register(path, "main"); diff --git a/src/main/java/net/nuclearteam/createnuclear/content/effects/IodineEffect.java b/src/main/java/net/nuclearteam/createnuclear/content/effects/IodineEffect.java new file mode 100644 index 0000000..9862043 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/effects/IodineEffect.java @@ -0,0 +1,33 @@ +package net.nuclearteam.createnuclear.content.effects; + +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.resources.ResourceLocation; +import net.nuclearteam.createnuclear.CNAttributes; +import net.nuclearteam.createnuclear.CreateNuclear; + +public class IodineEffect extends MobEffect { + protected IodineEffect(MobEffectCategory pCategory, int pColor) { + super(pCategory, pColor); + + this.addAttributeModifier( + CNAttributes.IRRADIATED_RESISTANCE, + ResourceLocation.fromNamespaceAndPath( + CreateNuclear.MOD_ID, + "iodine_effect" + ), + 1D, + AttributeModifier.Operation.ADD_VALUE + ); + } + + public IodineEffect() { + this(MobEffectCategory.BENEFICIAL, 7328217); + } + + @Override + public boolean shouldApplyEffectTickThisTick(int pDuration, int pAmplifier) { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/effects/VicinityEffect.java b/src/main/java/net/nuclearteam/createnuclear/content/effects/VicinityEffect.java new file mode 100644 index 0000000..2549b3f --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/effects/VicinityEffect.java @@ -0,0 +1,65 @@ +package net.nuclearteam.createnuclear.content.effects; + +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.*; + +public abstract class VicinityEffect extends MobEffect { + private final UnaryOperator areaSize; + private final Predicate filter; + + // Stores the GameTime tick when the cooldown expires for each entity + private final Map cooldowns = new HashMap<>(); + + protected VicinityEffect(MobEffectCategory category, int color, UnaryOperator areaSize, Predicate filter, Consumer timer) { + super(category, color); + + this.areaSize = areaSize; + this.filter = filter; + } + + @Override + public boolean applyEffectTick(LivingEntity entity, int amplifier) { + long currentTime = entity.level().getGameTime(); + + List nearbyEntities = entity.level().getEntities( + entity, + entity.getBoundingBox().inflate(areaSize.apply(amplifier)), + e -> e instanceof LivingEntity target && filter.test(target) + ); + + for (Entity nearbyEntity : nearbyEntities) { + LivingEntity nearby = (LivingEntity) nearbyEntity; + UUID entityUuid = nearby.getUUID(); + + // Check if the cooldown has expired + if (currentTime >= cooldowns.getOrDefault(entityUuid, 0L)) { + onContaminate(nearby); + + // Set a 100-tick (5 seconds) cooldown before refreshing the effect again + cooldowns.put(entityUuid, currentTime + 100); + } + } + return true; + } + + /** + * Called for each eligible nearby entity once its cooldown expires. Each subclass + * decides how it actually delivers the effect (e.g. RadiationEffect feeds the dose + * into RadiationCapability rather than adding a MobEffect directly). + */ + protected abstract void onContaminate(LivingEntity nearby); + + @Override + public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) { + return duration % 5 == 0; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorClientExtensions.java b/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorClientExtensions.java new file mode 100644 index 0000000..167d1c9 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorClientExtensions.java @@ -0,0 +1,35 @@ +package net.nuclearteam.createnuclear.content.equipment.armor; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ArmorMaterial; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; +import net.nuclearteam.createnuclear.content.contraptions.irradiated.CNModelLayers; +import net.nuclearteam.createnuclear.foundation.utility.ClothTagHelper; +import org.jetbrains.annotations.NotNull; + +public final class AntiRadiationArmorClientExtensions implements IClientItemExtensions { + private AntiRadiationArmorModel model; + + @Override + public @NotNull HumanoidModel getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel original) { + if (this.model == null) { + EntityModelSet entityModelSet = Minecraft.getInstance().getEntityModels(); + ModelPart root = entityModelSet.bakeLayer(CNModelLayers.ANTI_RADIATION_ARMOR); + this.model = new AntiRadiationArmorModel(root); + } + + // Forge copies the body pose + standard part visibility from `original` onto the + // model after this returns, but it can't tell LEGS from FEET (both make the vanilla + // legs visible) and knows nothing about our custom boot parts. We pass the slot so + // renderToBuffer can show legs for leggings vs. only boots for the FEET slot. + this.model.currentSlot = equipmentSlot; + return this.model; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorItem.java b/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorItem.java index 21f9491..9b67964 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorItem.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorItem.java @@ -1,179 +1,91 @@ package net.nuclearteam.createnuclear.content.equipment.armor; -import com.simibubi.create.content.equipment.armor.BaseArmorItem; -import com.tterrag.registrate.util.entry.ItemEntry; +import com.tterrag.registrate.builders.ItemBuilder; +import com.tterrag.registrate.util.nullness.NonNullUnaryOperator; +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Holder; -import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.EquipmentSlotGroup; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.*; +import net.minecraft.world.item.component.ItemAttributeModifiers; +import net.nuclearteam.createnuclear.CNAttributes; +import net.nuclearteam.createnuclear.CNDataComponents; import net.nuclearteam.createnuclear.CNItems; -import net.nuclearteam.createnuclear.CNTags.CNItemTags; import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.equipment.cloth.ClothItem.Cloths; -import java.util.EnumMap; -import java.util.Locale; -import java.util.Map; - -@SuppressWarnings("unused") -public class AntiRadiationArmorItem { - - public static final ArmorItem.Type HELMET = ArmorItem.Type.HELMET; - public static final ArmorItem.Type CHESTPLATE = ArmorItem.Type.CHESTPLATE; - public static final ArmorItem.Type LEGGINGS = ArmorItem.Type.LEGGINGS; - public static final ArmorItem.Type BOOTS = ArmorItem.Type.BOOTS; - public static final Holder ARMOR_MATERIAL = CNArmorMaterials.ANTI_RADIATION_SUIT; +import net.minecraft.world.level.Level; - public static class Helmet extends BaseArmorItem { - protected final DyeColor color; - - public Helmet(Properties properties, DyeColor color) { - super( - CNArmorMaterials.ANTI_RADIATION_SUIT, - HELMET, - properties, - CreateNuclear.asResource(String.format(Locale.ROOT, "%s_anti_radiation_suit", color.getName())) - ); - this.color = color; - } +@SuppressWarnings("unused") +@MethodsReturnNonnullByDefault +public class AntiRadiationArmorItem extends ArmorItem { + public static final double RADIATION_VALUE = 0.25; - public static TagKey getHelmetTag(String key) { - return key.equals("white") - ? CNItemTags.ANTI_RADIATION_ARMOR.tag - : CNItemTags.ANTI_RADIATION_HELMET_DYE.tag; - } + public AntiRadiationArmorItem(Holder material, ArmorItem.Type type, Item.Properties properties) { + super(material, type, properties); } - - public static class Chestplate extends BaseArmorItem { - protected final DyeColor color; - - public Chestplate(Properties properties, DyeColor color) { - super( - CNArmorMaterials.ANTI_RADIATION_SUIT, - CHESTPLATE, - properties, - CreateNuclear.asResource(String.format(Locale.ROOT, "%s_anti_radiation_suit", color.getName())) - ); - this.color = color; - - } - - public static TagKey getChestplateTag(String key) { - return key.equals("white") - ? CNItemTags.ANTI_RADIATION_ARMOR.tag - : CNItemTags.ANTI_RADIATION_CHESTPLATE_DYE.tag; - } + @Override + public ItemAttributeModifiers getDefaultAttributeModifiers() { + return super.getDefaultAttributeModifiers().withModifierAdded( + CNAttributes.IRRADIATED_RESISTANCE, + // The id must be unique per slot: modifiers are keyed by ResourceLocation, so sharing one id + // between the 4 pieces would make them overwrite each other instead of stacking to 1.0. + new AttributeModifier(CreateNuclear.asResource("armor_resistance_irradiation_" + this.getType().getName()), RADIATION_VALUE, AttributeModifier.Operation.ADD_VALUE), + EquipmentSlotGroup.bySlot(this.getType().getSlot()) + ); } - public static class Leggings extends BaseArmorItem { - protected final DyeColor color; - - public Leggings(Properties properties, DyeColor color) { - super( - CNArmorMaterials.ANTI_RADIATION_SUIT, - LEGGINGS, - properties, - CreateNuclear.asResource(String.format(Locale.ROOT, "%s_anti_radiation_suit", color.getName())) - ); - this.color = color; - - } - - public static TagKey getLeggingsTag(String key) { - return key.equals("white") - ? CNItemTags.ANTI_RADIATION_ARMOR.tag - : CNItemTags.ANTI_RADIATION_LEGGINGS_DYE.tag; - } + @Override + public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotId, boolean isSelected) { + super.inventoryTick(stack, level, entity, slotId, isSelected); + if (level.isClientSide || !(entity instanceof Player player)) return; + if (!stack.has(CNDataComponents.CLOTH_COLOR)) return; +// if (CNAdvancement.DYE_ANTI_RADIATION_ARMOR.isAlreadyAwardedTo(player)) return; +// CNAdvancement.DYE_ANTI_RADIATION_ARMOR.awardTo(player); } - public static class Boot extends BaseArmorItem { - public Boot(Properties properties) { - super( - CNArmorMaterials.ANTI_RADIATION_SUIT, - BOOTS, - properties, - CreateNuclear.asResource(String.format(Locale.ROOT, "%s_anti_radiation_suit", DyeColor.WHITE.getName())) + public static NonNullUnaryOperator> setColorComponent(Cloths cloths) { + return b -> b + .properties(p -> p + .component(CNDataComponents.CLOTH_COLOR, Cloths.DEFAULT) ); - } } - - public enum Armor { - WHITE_ARMOR(DyeColor.WHITE), - YELLOW_ARMOR(DyeColor.YELLOW), - RED_ARMOR(DyeColor.RED), - BLUE_ARMOR(DyeColor.BLUE), - GREEN_ARMOR(DyeColor.GREEN), - BLACK_ARMOR(DyeColor.BLACK), - ORANGE_ARMOR(DyeColor.ORANGE), - PURPLE_ARMOR(DyeColor.PURPLE), - BROWN_ARMOR(DyeColor.BROWN), - PINK_ARMOR(DyeColor.PINK), - CYAN_ARMOR(DyeColor.CYAN), - LIGHT_GRAY_ARMOR(DyeColor.LIGHT_GRAY), - GRAY_ARMOR(DyeColor.GRAY), - LIGHT_BLUE_ARMOR(DyeColor.LIGHT_BLUE), - LIME_ARMOR(DyeColor.LIME), - MAGENTA_ARMOR(DyeColor.MAGENTA); - - private static final Map> helmetMap = new EnumMap<>(DyeColor.class); - private static final Map> chestplateMap = new EnumMap<>(DyeColor.class); - private static final Map> leggingsMap = new EnumMap<>(DyeColor.class); - - static { - for (DyeColor color : DyeColor.values()) { - helmetMap.put(color, CNItems.ANTI_RADIATION_HELMETS.get(color)); - chestplateMap.put(color, CNItems.ANTI_RADIATION_CHESTPLATES.get(color)); - leggingsMap.put(color, CNItems.ANTI_RADIATION_LEGGINGS.get(color)); - } - } - - private final DyeColor color; - - Armor(DyeColor dyeColor) { - this.color = dyeColor; - } - - public ItemEntry getHelmetItem() { - return helmetMap.get(this.color); - } - - public static ItemEntry getHelmetByColor(DyeColor color) { - return helmetMap.get(color); - } - - public ItemEntry getChestplateItem() { - return chestplateMap.get(this.color); - } - - public static ItemEntry getChestplateByColor(DyeColor color) { - return chestplateMap.get(color); + public static class Helmet extends AntiRadiationArmorItem implements IGoggleHelmet { + public Helmet(Properties p) { + super(CNArmorMaterials.ANTI_RADIATION_SUIT, Type.HELMET, p); } + } - public ItemEntry getLeggingsItem() { - return leggingsMap.get(this.color); + public static class Chestplate extends AntiRadiationArmorItem { + public Chestplate(Properties p) { + super(CNArmorMaterials.ANTI_RADIATION_SUIT, Type.CHESTPLATE, p); } + } - public static ItemEntry getLeggingsByColor(DyeColor color) { - return leggingsMap.get(color); + public static class Leggings extends AntiRadiationArmorItem { + public Leggings(Properties p) { + super(CNArmorMaterials.ANTI_RADIATION_SUIT, Type.LEGGINGS, p); } + } - public static boolean isArmored(ItemStack item) { - return helmetMap.values().stream().anyMatch(entry -> entry.is(item.getItem())) || - chestplateMap.values().stream().anyMatch(entry -> entry.is(item.getItem())) || - leggingsMap.values().stream().anyMatch(entry -> entry.is(item.getItem())) || - CNItems.ANTI_RADIATION_BOOTS.is(item.getItem()) - ; + public static class Boot extends AntiRadiationArmorItem { + public Boot(Properties p) { + super(CNArmorMaterials.ANTI_RADIATION_SUIT, Type.BOOTS, p); } + } - public static boolean isArmored2(ItemStack item) { - return CNItems.ANTI_RADIATION_HELMETS.contains(item.getItem()) - || CNItems.ANTI_RADIATION_CHESTPLATES.contains(item.getItem()) - || CNItems.ANTI_RADIATION_LEGGINGS.contains(item.getItem()) - || CNItems.ANTI_RADIATION_BOOTS.is(item.getItem()) - ; + public interface IGoggleHelmet { + static boolean isGoggleHelmet(LivingEntity entity) { + ItemStack headSlot = entity.getItemBySlot(EquipmentSlot.HEAD); + return CNItems.ANTI_RADIATION_HELMETS.isIn(headSlot); } } - } diff --git a/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorModel.java b/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorModel.java new file mode 100644 index 0000000..9795960 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/equipment/armor/AntiRadiationArmorModel.java @@ -0,0 +1,99 @@ +package net.nuclearteam.createnuclear.content.equipment.armor; + + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; + +public class AntiRadiationArmorModel extends HumanoidModel { + private final ModelPart head; + private final ModelPart body; + private final ModelPart right_arm; + private final ModelPart left_arm; + private final ModelPart right_leg; + private final ModelPart left_leg; + private final ModelPart left_boot; + private final ModelPart right_boot; + + // Set per render pass by AntiRadiationArmorClientExtensions so renderToBuffer can show + // the legs for LEGS (leggings) but only the boots for FEET (boots) — Forge makes the + // vanilla leg parts visible for BOTH slots, so we disambiguate here. + public EquipmentSlot currentSlot = EquipmentSlot.HEAD; + + public AntiRadiationArmorModel(ModelPart root) { + super(root); + this.head = root.getChild("head"); + this.body = root.getChild("body"); + this.right_arm = root.getChild("right_arm"); + this.left_arm = root.getChild("left_arm"); + this.right_leg = root.getChild("right_leg"); + this.left_leg = root.getChild("left_leg"); + this.left_boot = root.getChild("left_boot"); + this.right_boot = root.getChild("right_boot"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + // HumanoidModel's constructor requires a "hat" child; keep it empty. + partdefinition.addOrReplaceChild("hat", CubeListBuilder.create(), PartPose.ZERO); + + PartDefinition head = partdefinition.addOrReplaceChild("head", CubeListBuilder.create().texOffs(30, 0).addBox(-4.0F, -8.0F, -4.0F, 8.0F, 8.0F, 8.0F, new CubeDeformation(0.2F)) + .texOffs(30, 16).addBox(-4.0F, -8.0F, -4.0F, 8.0F, 8.0F, 8.0F, new CubeDeformation(0.5F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition body = partdefinition.addOrReplaceChild("body", CubeListBuilder.create().texOffs(0, 19).addBox(-3.7F, 0.0F, -2.8F, 8.0F, 12.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(0, 0).addBox(-4.4F, -1.0F, -3.1F, 9.0F, 13.0F, 6.0F, new CubeDeformation(0.0F)) + .texOffs(0, 36).addBox(-4.0F, 0.0F, -2.7F, 8.0F, 12.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(64, 0).addBox(-4.5F, -1.0F, -3.0F, 9.0F, 13.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition right_arm = partdefinition.addOrReplaceChild("right_arm", CubeListBuilder.create().texOffs(45, 32).mirror().addBox(-3.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.45F)).mirror(false) + .texOffs(29, 32).mirror().addBox(-3.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.55F)).mirror(false), PartPose.offset(-5.0F, 2.0F, 0.0F)); + + PartDefinition left_arm = partdefinition.addOrReplaceChild("left_arm", CubeListBuilder.create().texOffs(45, 32).addBox(-1.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.4F)) + .texOffs(29, 32).addBox(-1.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.5F)), PartPose.offset(5.0F, 2.0F, 0.0F)); + + PartDefinition right_leg = partdefinition.addOrReplaceChild("right_leg", CubeListBuilder.create().texOffs(29, 48).mirror().addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(45, 48).mirror().addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)).mirror(false), PartPose.offset(1.9F, 12.0F, 0.0F)); + + PartDefinition left_leg = partdefinition.addOrReplaceChild("left_leg", CubeListBuilder.create().texOffs(29, 48).addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(45, 48).addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offset(-1.9F, 12.0F, 0.0F)); + + partdefinition.addOrReplaceChild("left_boot", CubeListBuilder.create().texOffs(61, 39).addBox(-2.0F, 8.3F, -2.0F, 4.0F, 4.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offset(-1.9F, 12.0F, 0.0F)); + + partdefinition.addOrReplaceChild("right_boot", CubeListBuilder.create().texOffs(61, 39).mirror().addBox(-2.0F, 8.3F, -2.0F, 4.0F, 4.0F, 4.0F, new CubeDeformation(0.25F)).mirror(false), PartPose.offset(1.9F, 12.0F, 0.0F)); + + return LayerDefinition.create(meshdefinition, 96, 96); + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, int color) { + // Make the boots follow the (already animated) legs. + this.right_boot.copyFrom(this.right_leg); + this.left_boot.copyFrom(this.left_leg); + + // Control part visibility based on the slot being rendered + this.head.visible = this.currentSlot == EquipmentSlot.HEAD; + this.body.visible = this.currentSlot == EquipmentSlot.CHEST; + this.right_arm.visible = this.currentSlot == EquipmentSlot.CHEST; + this.left_arm.visible = this.currentSlot == EquipmentSlot.CHEST; + this.right_leg.visible = this.currentSlot == EquipmentSlot.LEGS; + this.left_leg.visible = this.currentSlot == EquipmentSlot.LEGS; + this.right_boot.visible = this.currentSlot == EquipmentSlot.FEET; + this.left_boot.visible = this.currentSlot == EquipmentSlot.FEET; + + if (this.head.visible) head.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + if (this.body.visible) body.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + if (this.right_arm.visible) right_arm.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + if (this.left_arm.visible) left_arm.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + if (this.right_leg.visible) right_leg.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + if (this.left_leg.visible) left_leg.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + if (this.right_boot.visible) right_boot.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + if (this.left_boot.visible) left_boot.render(poseStack, vertexConsumer, packedLight, packedOverlay, color); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/equipment/cloth/ClothItem.java b/src/main/java/net/nuclearteam/createnuclear/content/equipment/cloth/ClothItem.java index e9fbb67..75cc659 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/equipment/cloth/ClothItem.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/equipment/cloth/ClothItem.java @@ -1,18 +1,18 @@ package net.nuclearteam.createnuclear.content.equipment.cloth; -import com.simibubi.create.api.data.recipe.BaseRecipeProvider.GeneratedRecipe; import com.tterrag.registrate.util.entry.ItemEntry; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.util.StringRepresentable; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; import net.nuclearteam.createnuclear.CNItems; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.*; -import java.util.function.Function; + +import javax.annotation.Nullable; +import java.util.EnumMap; +import java.util.Map; @SuppressWarnings("unused") public class ClothItem extends Item { - private final DyeColor color; public ClothItem(Item.Properties properties, DyeColor color) { @@ -20,112 +20,12 @@ public ClothItem(Item.Properties properties, DyeColor color) { this.color = color; } - public static class DyeRecipeList implements Iterable { - - private static final int COLOR_AMOUNT = DyeColor.values().length; - protected final GeneratedRecipe[] recipes = new GeneratedRecipe[getColorCount()]; - - public DyeRecipeList(Function<@NotNull DyeColor, GeneratedRecipe> filler) { - for (DyeColor color : DyeColor.values()) { - recipes[color.ordinal()] = filler.apply(color); - } - } - - protected int getColorCount() { - return COLOR_AMOUNT; - } - - public GeneratedRecipe get(@Nullable DyeColor color) { - return recipes[color.ordinal()]; - } - - public GeneratedRecipe[] toArrays() { - return Arrays.copyOf(recipes, recipes.length); - } - - @NotNull - @Override - public Iterator iterator() { - return new Iterator<>() { - private int index = 0; - @Override - public boolean hasNext() { - return index < recipes.length; - } - @Override - public GeneratedRecipe next() { - if (!hasNext()) throw new NoSuchElementException(); - return recipes[index++]; - } - }; - } - - public static class NullableDyedRecipeList extends DyeRecipeList { - public NullableDyedRecipeList(Function<@Nullable DyeColor, GeneratedRecipe> filter) { - super(filter); - recipes[recipes.length - 1] = filter.apply(null); - } - @Override - protected int getColorCount() { - return COLOR_AMOUNT + 1; - } - @Override - public GeneratedRecipe get(@Nullable DyeColor color) { - return color == null ? recipes[recipes.length - 1] : super.get(color); - } - } - + public DyeColor getColor() { + return color; } - public static class DyeItemList implements Iterable> { - - private static final int COLOR_AMOUNT = DyeColor.values().length; - - private final ItemEntry[] entry = new ItemEntry[COLOR_AMOUNT]; - - public DyeItemList(Function> filler) { - for (DyeColor color : DyeColor.values()) { - entry[color.ordinal()] = filler.apply(color); - } - } - - @SuppressWarnings("unchecked") - public ItemEntry get(DyeColor color) { - return (ItemEntry) entry[color.ordinal()]; - } - - public boolean contains(Item block) { - for (ItemEntry entry : entry) { - if (entry.is(block)) return true; - } - return false; - } - - @SuppressWarnings("unchecked") - public ItemEntry[] toArray() { - return (ItemEntry[]) Arrays.copyOf(entry, entry.length); - } - - @Override - public Iterator> iterator() { - return new Iterator<>() { - private int index = 0; - @Override - public boolean hasNext() { - return index < entry.length; - } - @SuppressWarnings("unchecked") - @Override - public ItemEntry next() { - if (!hasNext()) throw new NoSuchElementException(); - return (ItemEntry) entry[index++]; - } - }; - } - - } - - public enum Cloths { + @MethodsReturnNonnullByDefault + public enum Cloths implements StringRepresentable { WHITE_CLOTH(DyeColor.WHITE), YELLOW_CLOTH(DyeColor.YELLOW), RED_CLOTH(DyeColor.RED), @@ -141,29 +41,59 @@ public enum Cloths { GRAY_CLOTH(DyeColor.GRAY), LIGHT_BLUE_CLOTH(DyeColor.LIGHT_BLUE), LIME_CLOTH(DyeColor.LIME), - MAGENTA_CLOTH(DyeColor.MAGENTA); + MAGENTA_CLOTH(DyeColor.MAGENTA), + DEFAULT(null, "default"); - private static final Map> clothMap = new EnumMap<>(DyeColor.class); + private static Map> clothMap; - static { - for (DyeColor color : DyeColor.values()) { - clothMap.put(color, CNItems.CLOTHS.get(color)); + private static Map> clothMap() { + if (clothMap == null) { + clothMap = new EnumMap<>(DyeColor.class); + for (DyeColor color : DyeColor.values()) { + clothMap.put(color, CNItems.CLOTHS.get(color)); + } } + return clothMap; } + @Nullable private final DyeColor color; + private final String serializedName; Cloths(DyeColor color) { + this(color, color.getSerializedName()); + } + + Cloths(@Nullable DyeColor color, String serializedName) { this.color = color; + this.serializedName = serializedName; } + @Nullable public ItemEntry getItem() { - return clothMap.get(this.color); + return color != null ? clothMap().get(color) : null; } public static ItemEntry getByColor(DyeColor color) { - return clothMap.get(color); + return clothMap().get(color); } + public static Cloths of(@Nullable DyeColor color) { + if (color == null) return DEFAULT; + for (Cloths c : values()) { + if (c.color == color) return c; + } + return DEFAULT; + } + + @Override + public String getSerializedName() { + return serializedName; + } + + @Nullable + public DyeColor getDyeColor() { + return color; + } } } \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNAdvancedEntityModel.java b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNAdvancedEntityModel.java new file mode 100644 index 0000000..6fc682b --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNAdvancedEntityModel.java @@ -0,0 +1,32 @@ +package net.nuclearteam.createnuclear.content.explosion; + +import net.minecraft.world.entity.Entity; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +@OnlyIn(Dist.CLIENT) +public abstract class CNAdvancedEntityModel extends CNBasicEntityModel { + private float movementScale = 1.0F; + public int texWidth = 32; + public int texHeight = 32; + + public void updateDefaultPose() { + this.getAllParts().forEach(CNAdvancedModelBox::updateDefaultPose); + } + + public void resetToDefaultPose() { + this.getAllParts().forEach(CNAdvancedModelBox::resetToDefaultPose); + } + + public float getMovementScale() { + return this.movementScale; + } + + public void setRotateAngle(CNAdvancedModelBox model, float x, float y, float z) { + model.rotateAngleX = x; + model.rotateAngleY = y; + model.rotateAngleZ = z; + } + + public abstract Iterable getAllParts(); +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNAdvancedModelBox.java b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNAdvancedModelBox.java new file mode 100644 index 0000000..5cbfad7 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNAdvancedModelBox.java @@ -0,0 +1,208 @@ +package net.nuclearteam.createnuclear.content.explosion; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Axis; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ObjectListIterator; +import net.minecraft.util.Mth; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; + +@OnlyIn(Dist.CLIENT) +public class CNAdvancedModelBox extends CNBasicModelPart { + public float defaultRotationX; + public float defaultRotationY; + public float defaultRotationZ; + public float defaultPositionX; + public float defaultPositionY; + public float defaultPositionZ; + public float scaleX; + public float scaleY; + public float scaleZ; + public int textureOffsetX; + public int textureOffsetY; + public boolean scaleChildren; + private CNAdvancedEntityModel model; + private CNAdvancedModelBox parent; + public ObjectList cubeList; + public ObjectList childModels; + private float textureWidth; + private float textureHeight; + public String boxName; + + public CNAdvancedModelBox(CNAdvancedEntityModel model, String name) { + super(model); + this.scaleX = 1.0F; + this.scaleY = 1.0F; + this.scaleZ = 1.0F; + this.boxName = ""; + this.textureWidth = (float)model.texWidth; + this.textureHeight = (float)model.texHeight; + this.model = model; + this.cubeList = new ObjectArrayList(); + this.childModels = new ObjectArrayList(); + this.boxName = name; + } + + public CNAdvancedModelBox(CNAdvancedEntityModel model) { + this(model, null); + this.textureWidth = (float)model.texWidth; + this.textureHeight = (float)model.texHeight; + this.cubeList = new ObjectArrayList(); + this.childModels = new ObjectArrayList(); + } + + public CNBasicModelPart addBox(String p_217178_1_, float p_217178_2_, float p_217178_3_, float p_217178_4_, int p_217178_5_, int p_217178_6_, int p_217178_7_, float p_217178_8_, int p_217178_9_, int p_217178_10_) { + this.setTextureOffset(p_217178_9_, p_217178_10_); + this.addBox(this.textureOffsetX, this.textureOffsetY, p_217178_2_, p_217178_3_, p_217178_4_, (float)p_217178_5_, (float)p_217178_6_, (float)p_217178_7_, p_217178_8_, p_217178_8_, p_217178_8_, this.mirror, false); + return this; + } + + public CNBasicModelPart addBox(float p_228300_1_, float p_228300_2_, float p_228300_3_, float p_228300_4_, float p_228300_5_, float p_228300_6_) { + this.addBox(this.textureOffsetX, this.textureOffsetY, p_228300_1_, p_228300_2_, p_228300_3_, p_228300_4_, p_228300_5_, p_228300_6_, 0.0F, 0.0F, 0.0F, this.mirror, false); + return this; + } + + public CNBasicModelPart addBox(float p_228304_1_, float p_228304_2_, float p_228304_3_, float p_228304_4_, float p_228304_5_, float p_228304_6_, boolean p_228304_7_) { + this.addBox(this.textureOffsetX, this.textureOffsetY, p_228304_1_, p_228304_2_, p_228304_3_, p_228304_4_, p_228304_5_, p_228304_6_, 0.0F, 0.0F, 0.0F, p_228304_7_, false); + return this; + } + + public void addBox(float p_228301_1_, float p_228301_2_, float p_228301_3_, float p_228301_4_, float p_228301_5_, float p_228301_6_, float p_228301_7_) { + this.addBox(this.textureOffsetX, this.textureOffsetY, p_228301_1_, p_228301_2_, p_228301_3_, p_228301_4_, p_228301_5_, p_228301_6_, p_228301_7_, p_228301_7_, p_228301_7_, this.mirror, false); + } + + public void addBox(float p_228302_1_, float p_228302_2_, float p_228302_3_, float p_228302_4_, float p_228302_5_, float p_228302_6_, float p_228302_7_, float p_228302_8_, float p_228302_9_) { + this.addBox(this.textureOffsetX, this.textureOffsetY, p_228302_1_, p_228302_2_, p_228302_3_, p_228302_4_, p_228302_5_, p_228302_6_, p_228302_7_, p_228302_8_, p_228302_9_, this.mirror, false); + } + + public void addBox(float p_228303_1_, float p_228303_2_, float p_228303_3_, float p_228303_4_, float p_228303_5_, float p_228303_6_, float p_228303_7_, boolean p_228303_8_) { + this.addBox(this.textureOffsetX, this.textureOffsetY, p_228303_1_, p_228303_2_, p_228303_3_, p_228303_4_, p_228303_5_, p_228303_6_, p_228303_7_, p_228303_7_, p_228303_7_, p_228303_8_, false); + } + + private void addBox(int p_228305_1_, int p_228305_2_, float p_228305_3_, float p_228305_4_, float p_228305_5_, float p_228305_6_, float p_228305_7_, float p_228305_8_, float p_228305_9_, float p_228305_10_, float p_228305_11_, boolean p_228305_12_, boolean p_228305_13_) { + this.cubeList.add(new CNTabulaModelRenderUtils.ModelBox(p_228305_1_, p_228305_2_, p_228305_3_, p_228305_4_, p_228305_5_, p_228305_6_, p_228305_7_, p_228305_8_, p_228305_9_, p_228305_10_, p_228305_11_, p_228305_12_, this.textureWidth, this.textureHeight)); + } + + public void setScale(float scaleX, float scaleY, float scaleZ) { + this.scaleX = scaleX; + this.scaleY = scaleY; + this.scaleZ = scaleZ; + } + + public void updateDefaultPose() { + this.defaultRotationX = this.rotateAngleX; + this.defaultRotationY = this.rotateAngleY; + this.defaultRotationZ = this.rotateAngleZ; + this.defaultPositionX = this.rotationPointX; + this.defaultPositionY = this.rotationPointY; + this.defaultPositionZ = this.rotationPointZ; + } + + public void resetToDefaultPose() { + this.rotateAngleX = this.defaultRotationX; + this.rotateAngleY = this.defaultRotationY; + this.rotateAngleZ = this.defaultRotationZ; + this.rotationPointX = this.defaultPositionX; + this.rotationPointY = this.defaultPositionY; + this.rotationPointZ = this.defaultPositionZ; + } + + public void addChild(CNBasicModelPart child) { + super.addChild(child); + this.childModels.add(child); + if (child instanceof CNAdvancedModelBox advancedChild) { + advancedChild.setParent(this); + } + } + + public CNAdvancedModelBox getParent() { + return this.parent; + } + + public void setParent(CNAdvancedModelBox parent) { + this.parent = parent; + } + + public void translateAndRotate(PoseStack matrixStackIn) { + matrixStackIn.translate(this.rotationPointX / 16.0F, this.rotationPointY / 16.0F, (double)(this.rotationPointZ / 16.0F)); + if (this.rotateAngleZ != 0.0F) { + matrixStackIn.mulPose(Axis.ZP.rotation(this.rotateAngleZ)); + } + + if (this.rotateAngleY != 0.0F) { + matrixStackIn.mulPose(Axis.YP.rotation(this.rotateAngleY)); + } + + if (this.rotateAngleX != 0.0F) { + matrixStackIn.mulPose(Axis.XP.rotation(this.rotateAngleX)); + } + + matrixStackIn.scale(this.scaleX, this.scaleY, this.scaleZ); + } + + public void render(PoseStack p_228309_1_, VertexConsumer p_228309_2_, int p_228309_3_, int p_228309_4_, float p_228309_5_, float p_228309_6_, float p_228309_7_, float p_228309_8_) { + if (this.showModel && (!this.cubeList.isEmpty() || !this.childModels.isEmpty())) { + p_228309_1_.pushPose(); + this.translateAndRotate(p_228309_1_); + this.doRender(p_228309_1_.last(), p_228309_2_, p_228309_3_, p_228309_4_, p_228309_5_, p_228309_6_, p_228309_7_, p_228309_8_); + ObjectListIterator var9 = this.childModels.iterator(); + if (!this.scaleChildren) { + p_228309_1_.scale(1.0F / Math.max(this.scaleX, 1.0E-4F), 1.0F / Math.max(this.scaleY, 1.0E-4F), 1.0F / Math.max(this.scaleZ, 1.0E-4F)); + } + + while(var9.hasNext()) { + CNBasicModelPart lvt_10_1_ = (CNBasicModelPart)var9.next(); + lvt_10_1_.render(p_228309_1_, p_228309_2_, p_228309_3_, p_228309_4_, p_228309_5_, p_228309_6_, p_228309_7_, p_228309_8_); + } + + p_228309_1_.popPose(); + } + + } + + private void doRender(PoseStack.Pose p_228306_1_, VertexConsumer p_228306_2_, int p_228306_3_, int p_228306_4_, float p_228306_5_, float p_228306_6_, float p_228306_7_, float p_228306_8_) { + Matrix4f lvt_9_1_ = p_228306_1_.pose(); + Matrix3f lvt_10_1_ = p_228306_1_.normal(); + ObjectListIterator var11 = this.cubeList.iterator(); + + while(var11.hasNext()) { + CNTabulaModelRenderUtils.ModelBox lvt_12_1_ = (CNTabulaModelRenderUtils.ModelBox)var11.next(); + + for(CNTabulaModelRenderUtils.TexturedQuad lvt_16_1_ : lvt_12_1_.quads) { + Vector3f lvt_17_1_ = new Vector3f(lvt_16_1_.normal); + lvt_17_1_.mul(lvt_10_1_); + float lvt_18_1_ = lvt_17_1_.x(); + float lvt_19_1_ = lvt_17_1_.y(); + float lvt_20_1_ = lvt_17_1_.z(); + + for(int lvt_21_1_ = 0; lvt_21_1_ < 4; ++lvt_21_1_) { + CNTabulaModelRenderUtils.PositionTextureVertex lvt_22_1_ = lvt_16_1_.vertexPositions[lvt_21_1_]; + float lvt_23_1_ = lvt_22_1_.position.x() / 16.0F; + float lvt_24_1_ = lvt_22_1_.position.y() / 16.0F; + float lvt_25_1_ = lvt_22_1_.position.z() / 16.0F; + Vector4f lvt_26_1_ = new Vector4f(lvt_23_1_, lvt_24_1_, lvt_25_1_, 1.0F); + lvt_26_1_.mul(lvt_9_1_); + p_228306_2_.addVertex(lvt_26_1_.x(), lvt_26_1_.y(), lvt_26_1_.z()).setColor(p_228306_5_, p_228306_6_, p_228306_7_, p_228306_8_).setUv(lvt_22_1_.textureU, lvt_22_1_.textureV).setOverlay(p_228306_4_).setLight(p_228306_3_).setNormal(lvt_18_1_, lvt_19_1_, lvt_20_1_); + } + } + } + + } + + public CNAdvancedEntityModel getModel() { + return this.model; + } + + public CNAdvancedModelBox setTextureOffset(int textureOffsetX, int textureOffsetY) { + this.textureOffsetX = textureOffsetX; + this.textureOffsetY = textureOffsetY; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNBasicEntityModel.java b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNBasicEntityModel.java new file mode 100644 index 0000000..53ab4e2 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNBasicEntityModel.java @@ -0,0 +1,40 @@ +package net.nuclearteam.createnuclear.content.explosion; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import java.util.function.Function; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public abstract class CNBasicEntityModel extends EntityModel { + public int textureWidth; + public int textureHeight; + + protected CNBasicEntityModel() { + this(RenderType::entityCutoutNoCull); + } + + protected CNBasicEntityModel(Function p_102613_) { + super(p_102613_); + this.textureWidth = 64; + this.textureHeight = 32; + } + + @Override + public void renderToBuffer(PoseStack p_103013_, VertexConsumer p_103014_, int p_103015_, int p_103016_, int color) { + float p_103017_ = (float) (color >> 16 & 255) / 255.0F; + float p_103018_ = (float) (color >> 8 & 255) / 255.0F; + float p_103019_ = (float) (color & 255) / 255.0F; + float p_103020_ = (float) (color >> 24 & 255) / 255.0F; + this.parts().forEach((p_103030_) -> p_103030_.render(p_103013_, p_103014_, p_103015_, p_103016_, p_103017_, p_103018_, p_103019_, p_103020_)); + } + + public abstract Iterable parts(); + + public abstract void setupAnim(T var1, float var2, float var3, float var4, float var5, float var6); + + public void prepareMobModel(T p_102614_, float p_102615_, float p_102616_, float p_102617_) { + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNBasicModelPart.java b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNBasicModelPart.java new file mode 100644 index 0000000..a26e854 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNBasicModelPart.java @@ -0,0 +1,315 @@ +package net.nuclearteam.createnuclear.content.explosion; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Axis; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ObjectListIterator; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; + +@OnlyIn(Dist.CLIENT) +public class CNBasicModelPart { + public float textureWidth; + public float textureHeight; + public int textureOffsetX; + public int textureOffsetY; + public float rotationPointX; + public float rotationPointY; + public float rotationPointZ; + public float rotateAngleX; + public float rotateAngleY; + public float rotateAngleZ; + public boolean mirror; + public boolean showModel; + private final ObjectList cubeList; + private final ObjectList childModels; + + public CNBasicModelPart(CNBasicEntityModel model) { + this.textureWidth = 64.0F; + this.textureHeight = 32.0F; + this.showModel = true; + this.cubeList = new ObjectArrayList(); + this.childModels = new ObjectArrayList(); + this.setTextureSize(model.textureWidth, model.textureHeight); + } + + public CNBasicModelPart(CNBasicEntityModel model, int texOffX, int texOffY) { + this(model.textureWidth, model.textureHeight, texOffX, texOffY); + } + + public CNBasicModelPart(int textureWidthIn, int textureHeightIn, int textureOffsetXIn, int textureOffsetYIn) { + this.textureWidth = 64.0F; + this.textureHeight = 32.0F; + this.showModel = true; + this.cubeList = new ObjectArrayList(); + this.childModels = new ObjectArrayList(); + this.setTextureSize(textureWidthIn, textureHeightIn); + this.setTextureOffset(textureOffsetXIn, textureOffsetYIn); + } + + private CNBasicModelPart() { + this.textureWidth = 64.0F; + this.textureHeight = 32.0F; + this.showModel = true; + this.cubeList = new ObjectArrayList(); + this.childModels = new ObjectArrayList(); + } + + public CNBasicModelPart getModelAngleCopy() { + CNBasicModelPart CNBasicModelPart = new CNBasicModelPart(); + CNBasicModelPart.copyModelAngles(this); + return CNBasicModelPart; + } + + public void copyModelAngles(CNBasicModelPart CNBasicModelPartIn) { + this.rotateAngleX = CNBasicModelPartIn.rotateAngleX; + this.rotateAngleY = CNBasicModelPartIn.rotateAngleY; + this.rotateAngleZ = CNBasicModelPartIn.rotateAngleZ; + this.rotationPointX = CNBasicModelPartIn.rotationPointX; + this.rotationPointY = CNBasicModelPartIn.rotationPointY; + this.rotationPointZ = CNBasicModelPartIn.rotationPointZ; + } + + public void addChild(CNBasicModelPart renderer) { + this.childModels.add(renderer); + } + + public CNBasicModelPart setTextureOffset(int x, int y) { + this.textureOffsetX = x; + this.textureOffsetY = y; + return this; + } + + public CNBasicModelPart addBox(String partName, float x, float y, float z, int width, int height, int depth, float delta, int texX, int texY) { + this.setTextureOffset(texX, texY); + this.addBox(this.textureOffsetX, this.textureOffsetY, x, y, z, (float)width, (float)height, (float)depth, delta, delta, delta, this.mirror, false); + return this; + } + + public CNBasicModelPart addBox(float x, float y, float z, float width, float height, float depth) { + this.addBox(this.textureOffsetX, this.textureOffsetY, x, y, z, width, height, depth, 0.0F, 0.0F, 0.0F, this.mirror, false); + return this; + } + + public CNBasicModelPart addBox(float x, float y, float z, float width, float height, float depth, boolean mirrorIn) { + this.addBox(this.textureOffsetX, this.textureOffsetY, x, y, z, width, height, depth, 0.0F, 0.0F, 0.0F, mirrorIn, false); + return this; + } + + public void addBox(float x, float y, float z, float width, float height, float depth, float delta) { + this.addBox(this.textureOffsetX, this.textureOffsetY, x, y, z, width, height, depth, delta, delta, delta, this.mirror, false); + } + + public void addBox(float x, float y, float z, float width, float height, float depth, float deltaX, float deltaY, float deltaZ) { + this.addBox(this.textureOffsetX, this.textureOffsetY, x, y, z, width, height, depth, deltaX, deltaY, deltaZ, this.mirror, false); + } + + public void addBox(float x, float y, float z, float width, float height, float depth, float delta, boolean mirrorIn) { + this.addBox(this.textureOffsetX, this.textureOffsetY, x, y, z, width, height, depth, delta, delta, delta, mirrorIn, false); + } + + private void addBox(int texOffX, int texOffY, float x, float y, float z, float width, float height, float depth, float deltaX, float deltaY, float deltaZ, boolean mirorIn, boolean p_228305_13_) { + this.cubeList.add(new CNBasicModelPart.ModelBox(texOffX, texOffY, x, y, z, width, height, depth, deltaX, deltaY, deltaZ, mirorIn, this.textureWidth, this.textureHeight)); + } + + public void setRotationPoint(float rotationPointXIn, float rotationPointYIn, float rotationPointZIn) { + this.rotationPointX = rotationPointXIn; + this.rotationPointY = rotationPointYIn; + this.rotationPointZ = rotationPointZIn; + } + + public void render(PoseStack matrixStackIn, VertexConsumer bufferIn, int packedLightIn, int packedOverlayIn) { + this.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + } + + public void render(PoseStack matrixStackIn, VertexConsumer bufferIn, int packedLightIn, int packedOverlayIn, float red, float green, float blue, float alpha) { + if (this.showModel && (!this.cubeList.isEmpty() || !this.childModels.isEmpty())) { + matrixStackIn.pushPose(); + this.translateRotate(matrixStackIn); + this.doRender(matrixStackIn.last(), bufferIn, packedLightIn, packedOverlayIn, red, green, blue, alpha); + ObjectListIterator var9 = this.childModels.iterator(); + + while(var9.hasNext()) { + CNBasicModelPart CNBasicModelPart = (CNBasicModelPart)var9.next(); + CNBasicModelPart.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn, red, green, blue, alpha); + } + + matrixStackIn.popPose(); + } + + } + + public void translateRotate(PoseStack matrixStackIn) { + matrixStackIn.translate((double)(this.rotationPointX / 16.0F), (double)(this.rotationPointY / 16.0F), (double)(this.rotationPointZ / 16.0F)); + if (this.rotateAngleZ != 0.0F) { + matrixStackIn.mulPose(Axis.ZP.rotation(this.rotateAngleZ)); + } + + if (this.rotateAngleY != 0.0F) { + matrixStackIn.mulPose(Axis.YP.rotation(this.rotateAngleY)); + } + + if (this.rotateAngleX != 0.0F) { + matrixStackIn.mulPose(Axis.XP.rotation(this.rotateAngleX)); + } + + } + + private void doRender(PoseStack.Pose matrixEntryIn, VertexConsumer bufferIn, int packedLightIn, int packedOverlayIn, float red, float green, float blue, float alpha) { + Matrix4f matrix4f = matrixEntryIn.pose(); + Matrix3f matrix3f = matrixEntryIn.normal(); + ObjectListIterator var11 = this.cubeList.iterator(); + + while(var11.hasNext()) { + CNBasicModelPart.ModelBox CNBasicModelPart$modelbox = (CNBasicModelPart.ModelBox)var11.next(); + + for(CNBasicModelPart.TexturedQuad CNBasicModelPart$texturedquad : CNBasicModelPart$modelbox.quads) { + Vector3f vector3f = new Vector3f(CNBasicModelPart$texturedquad.normal); + vector3f.mul(matrix3f); + float f = vector3f.x(); + float f1 = vector3f.y(); + float f2 = vector3f.z(); + + for(int i = 0; i < 4; ++i) { + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex = CNBasicModelPart$texturedquad.vertexPositions[i]; + float f3 = CNBasicModelPart$positiontexturevertex.position.x() / 16.0F; + float f4 = CNBasicModelPart$positiontexturevertex.position.y() / 16.0F; + float f5 = CNBasicModelPart$positiontexturevertex.position.z() / 16.0F; + Vector4f vector4f = new Vector4f(f3, f4, f5, 1.0F); + vector4f.mul(matrix4f); + bufferIn.addVertex(vector4f.x(), vector4f.y(), vector4f.z()).setColor(red, green, blue, alpha).setUv(CNBasicModelPart$positiontexturevertex.textureU, CNBasicModelPart$positiontexturevertex.textureV).setOverlay(packedOverlayIn).setLight(packedLightIn).setNormal(f, f1, f2); + } + } + } + + } + + public CNBasicModelPart setTextureSize(int textureWidthIn, int textureHeightIn) { + this.textureWidth = (float)textureWidthIn; + this.textureHeight = (float)textureHeightIn; + return this; + } + + public CNBasicModelPart.ModelBox getRandomCube(RandomSource randomIn) { + return this.cubeList.size() > 0 ? (CNBasicModelPart.ModelBox)this.cubeList.get(randomIn.nextInt(this.cubeList.size())) : null; + } + + @OnlyIn(Dist.CLIENT) + public static class ModelBox { + private final CNBasicModelPart.TexturedQuad[] quads; + public final float posX1; + public final float posY1; + public final float posZ1; + public final float posX2; + public final float posY2; + public final float posZ2; + + public ModelBox(int texOffX, int texOffY, float x, float y, float z, float width, float height, float depth, float deltaX, float deltaY, float deltaZ, boolean mirorIn, float texWidth, float texHeight) { + this.posX1 = x; + this.posY1 = y; + this.posZ1 = z; + this.posX2 = x + width; + this.posY2 = y + height; + this.posZ2 = z + depth; + this.quads = new CNBasicModelPart.TexturedQuad[6]; + float f = x + width; + float f1 = y + height; + float f2 = z + depth; + x -= deltaX; + y -= deltaY; + z -= deltaZ; + f += deltaX; + f1 += deltaY; + f2 += deltaZ; + if (mirorIn) { + float f3 = f; + f = x; + x = f3; + } + + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex7 = new CNBasicModelPart.PositionTextureVertex(x, y, z, 0.0F, 0.0F); + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex = new CNBasicModelPart.PositionTextureVertex(f, y, z, 0.0F, 8.0F); + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex1 = new CNBasicModelPart.PositionTextureVertex(f, f1, z, 8.0F, 8.0F); + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex2 = new CNBasicModelPart.PositionTextureVertex(x, f1, z, 8.0F, 0.0F); + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex3 = new CNBasicModelPart.PositionTextureVertex(x, y, f2, 0.0F, 0.0F); + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex4 = new CNBasicModelPart.PositionTextureVertex(f, y, f2, 0.0F, 8.0F); + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex5 = new CNBasicModelPart.PositionTextureVertex(f, f1, f2, 8.0F, 8.0F); + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex6 = new CNBasicModelPart.PositionTextureVertex(x, f1, f2, 8.0F, 0.0F); + float f4 = (float)texOffX; + float f5 = (float)texOffX + depth; + float f6 = (float)texOffX + depth + width; + float f7 = (float)texOffX + depth + width + width; + float f8 = (float)texOffX + depth + width + depth; + float f9 = (float)texOffX + depth + width + depth + width; + float f10 = (float)texOffY; + float f11 = (float)texOffY + depth; + float f12 = (float)texOffY + depth + height; + this.quads[2] = new CNBasicModelPart.TexturedQuad(new CNBasicModelPart.PositionTextureVertex[]{CNBasicModelPart$positiontexturevertex4, CNBasicModelPart$positiontexturevertex3, CNBasicModelPart$positiontexturevertex7, CNBasicModelPart$positiontexturevertex}, f5, f10, f6, f11, texWidth, texHeight, mirorIn, Direction.DOWN); + this.quads[3] = new CNBasicModelPart.TexturedQuad(new CNBasicModelPart.PositionTextureVertex[]{CNBasicModelPart$positiontexturevertex1, CNBasicModelPart$positiontexturevertex2, CNBasicModelPart$positiontexturevertex6, CNBasicModelPart$positiontexturevertex5}, f6, f11, f7, f10, texWidth, texHeight, mirorIn, Direction.UP); + this.quads[1] = new CNBasicModelPart.TexturedQuad(new CNBasicModelPart.PositionTextureVertex[]{CNBasicModelPart$positiontexturevertex7, CNBasicModelPart$positiontexturevertex3, CNBasicModelPart$positiontexturevertex6, CNBasicModelPart$positiontexturevertex2}, f4, f11, f5, f12, texWidth, texHeight, mirorIn, Direction.WEST); + this.quads[4] = new CNBasicModelPart.TexturedQuad(new CNBasicModelPart.PositionTextureVertex[]{CNBasicModelPart$positiontexturevertex, CNBasicModelPart$positiontexturevertex7, CNBasicModelPart$positiontexturevertex2, CNBasicModelPart$positiontexturevertex1}, f5, f11, f6, f12, texWidth, texHeight, mirorIn, Direction.NORTH); + this.quads[0] = new CNBasicModelPart.TexturedQuad(new CNBasicModelPart.PositionTextureVertex[]{CNBasicModelPart$positiontexturevertex4, CNBasicModelPart$positiontexturevertex, CNBasicModelPart$positiontexturevertex1, CNBasicModelPart$positiontexturevertex5}, f6, f11, f8, f12, texWidth, texHeight, mirorIn, Direction.EAST); + this.quads[5] = new CNBasicModelPart.TexturedQuad(new CNBasicModelPart.PositionTextureVertex[]{CNBasicModelPart$positiontexturevertex3, CNBasicModelPart$positiontexturevertex4, CNBasicModelPart$positiontexturevertex5, CNBasicModelPart$positiontexturevertex6}, f8, f11, f9, f12, texWidth, texHeight, mirorIn, Direction.SOUTH); + } + } + + @OnlyIn(Dist.CLIENT) + static class PositionTextureVertex { + public final Vector3f position; + public final float textureU; + public final float textureV; + + public PositionTextureVertex(float x, float y, float z, float texU, float texV) { + this(new Vector3f(x, y, z), texU, texV); + } + + public CNBasicModelPart.PositionTextureVertex setTextureUV(float texU, float texV) { + return new CNBasicModelPart.PositionTextureVertex(this.position, texU, texV); + } + + public PositionTextureVertex(Vector3f posIn, float texU, float texV) { + this.position = posIn; + this.textureU = texU; + this.textureV = texV; + } + } + + @OnlyIn(Dist.CLIENT) + static class TexturedQuad { + public final CNBasicModelPart.PositionTextureVertex[] vertexPositions; + public final Vector3f normal; + + public TexturedQuad(CNBasicModelPart.PositionTextureVertex[] positionsIn, float u1, float v1, float u2, float v2, float texWidth, float texHeight, boolean mirrorIn, Direction directionIn) { + this.vertexPositions = positionsIn; + float f = 0.0F / texWidth; + float f1 = 0.0F / texHeight; + positionsIn[0] = positionsIn[0].setTextureUV(u2 / texWidth - f, v1 / texHeight + f1); + positionsIn[1] = positionsIn[1].setTextureUV(u1 / texWidth + f, v1 / texHeight + f1); + positionsIn[2] = positionsIn[2].setTextureUV(u1 / texWidth + f, v2 / texHeight - f1); + positionsIn[3] = positionsIn[3].setTextureUV(u2 / texWidth - f, v2 / texHeight - f1); + if (mirrorIn) { + int i = positionsIn.length; + + for(int j = 0; j < i / 2; ++j) { + CNBasicModelPart.PositionTextureVertex CNBasicModelPart$positiontexturevertex = positionsIn[j]; + positionsIn[j] = positionsIn[i - 1 - j]; + positionsIn[i - 1 - j] = CNBasicModelPart$positiontexturevertex; + } + } + + this.normal = directionIn.step(); + if (mirrorIn) { + this.normal.mul(-1.0F, 1.0F, 1.0F); + } + + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNNuclearExplosionSound.java b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNNuclearExplosionSound.java new file mode 100644 index 0000000..39094a1 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNNuclearExplosionSound.java @@ -0,0 +1,46 @@ +package net.nuclearteam.createnuclear.content.explosion; + +import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance; +import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; + +public class CNNuclearExplosionSound extends AbstractTickableSoundInstance { + + private int tickCount = 0; + private final int duration; + private final int fadesAt; + private final float fadeInBy; + + public CNNuclearExplosionSound(SoundEvent soundEvent, double x, double y, double z, int duration, int fadesAt, float fadeInBy, boolean looping) { + super(soundEvent, SoundSource.NEUTRAL, SoundInstance.createUnseededRandom()); + this.attenuation = Attenuation.LINEAR; + this.looping = looping; + this.x = x; + this.y = y; + this.z = z; + this.duration = duration; + this.fadesAt = fadesAt; + this.fadeInBy = fadeInBy; + this.delay = 0; + this.volume = 20; + } + + public void tick() { + if(tickCount < this.duration){ + if(tickCount >= fadesAt){ + float shrinkVolumeFor = 1F / Math.max(this.duration - fadesAt, 1F); + volume = Math.max(0, volume - shrinkVolumeFor); + }else if(volume < 1F){ + volume = Math.min(1F, volume + fadeInBy); + } + tickCount++; + }else{ + stop(); + } + } + + public boolean canStartSilent() { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNTabulaModelRenderUtils.java b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNTabulaModelRenderUtils.java new file mode 100644 index 0000000..53dea7f --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/explosion/CNTabulaModelRenderUtils.java @@ -0,0 +1,119 @@ +package net.nuclearteam.createnuclear.content.explosion; + +import net.minecraft.core.Direction; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.joml.Vector3f; + +public class CNTabulaModelRenderUtils { + @OnlyIn(Dist.CLIENT) + static class PositionTextureVertex { + public final Vector3f position; + public final float textureU; + public final float textureV; + + public PositionTextureVertex(float p_i1158_1_, float p_i1158_2_, float p_i1158_3_, float p_i1158_4_, float p_i1158_5_) { + this(new Vector3f(p_i1158_1_, p_i1158_2_, p_i1158_3_), p_i1158_4_, p_i1158_5_); + } + + public PositionTextureVertex setTextureUV(float p_78240_1_, float p_78240_2_) { + return new PositionTextureVertex(this.position, p_78240_1_, p_78240_2_); + } + + public PositionTextureVertex(Vector3f p_i225952_1_, float p_i225952_2_, float p_i225952_3_) { + this.position = p_i225952_1_; + this.textureU = p_i225952_2_; + this.textureV = p_i225952_3_; + } + } + + @OnlyIn(Dist.CLIENT) + static class TexturedQuad { + public final PositionTextureVertex[] vertexPositions; + public final Vector3f normal; + + public TexturedQuad(PositionTextureVertex[] p_i225951_1_, float p_i225951_2_, float p_i225951_3_, float p_i225951_4_, float p_i225951_5_, float p_i225951_6_, float p_i225951_7_, boolean p_i225951_8_, Direction p_i225951_9_) { + this.vertexPositions = p_i225951_1_; + float lvt_10_1_ = 0.0F / p_i225951_6_; + float lvt_11_1_ = 0.0F / p_i225951_7_; + p_i225951_1_[0] = p_i225951_1_[0].setTextureUV(p_i225951_4_ / p_i225951_6_ - lvt_10_1_, p_i225951_3_ / p_i225951_7_ + lvt_11_1_); + p_i225951_1_[1] = p_i225951_1_[1].setTextureUV(p_i225951_2_ / p_i225951_6_ + lvt_10_1_, p_i225951_3_ / p_i225951_7_ + lvt_11_1_); + p_i225951_1_[2] = p_i225951_1_[2].setTextureUV(p_i225951_2_ / p_i225951_6_ + lvt_10_1_, p_i225951_5_ / p_i225951_7_ - lvt_11_1_); + p_i225951_1_[3] = p_i225951_1_[3].setTextureUV(p_i225951_4_ / p_i225951_6_ - lvt_10_1_, p_i225951_5_ / p_i225951_7_ - lvt_11_1_); + if (p_i225951_8_) { + int lvt_12_1_ = p_i225951_1_.length; + + for(int lvt_13_1_ = 0; lvt_13_1_ < lvt_12_1_ / 2; ++lvt_13_1_) { + PositionTextureVertex lvt_14_1_ = p_i225951_1_[lvt_13_1_]; + p_i225951_1_[lvt_13_1_] = p_i225951_1_[lvt_12_1_ - 1 - lvt_13_1_]; + p_i225951_1_[lvt_12_1_ - 1 - lvt_13_1_] = lvt_14_1_; + } + } + + this.normal = p_i225951_9_.step(); + if (p_i225951_8_) { + this.normal.mul(-1.0F, 1.0F, 1.0F); + } + + } + } + + @OnlyIn(Dist.CLIENT) + public static class ModelBox { + public final TexturedQuad[] quads; + public final float posX1; + public final float posY1; + public final float posZ1; + public final float posX2; + public final float posY2; + public final float posZ2; + + public ModelBox(int p_i225950_1_, int p_i225950_2_, float p_i225950_3_, float p_i225950_4_, float p_i225950_5_, float p_i225950_6_, float p_i225950_7_, float p_i225950_8_, float p_i225950_9_, float p_i225950_10_, float p_i225950_11_, boolean p_i225950_12_, float p_i225950_13_, float p_i225950_14_) { + this.posX1 = p_i225950_3_; + this.posY1 = p_i225950_4_; + this.posZ1 = p_i225950_5_; + this.posX2 = p_i225950_3_ + p_i225950_6_; + this.posY2 = p_i225950_4_ + p_i225950_7_; + this.posZ2 = p_i225950_5_ + p_i225950_8_; + this.quads = new TexturedQuad[6]; + float lvt_15_1_ = p_i225950_3_ + p_i225950_6_; + float lvt_16_1_ = p_i225950_4_ + p_i225950_7_; + float lvt_17_1_ = p_i225950_5_ + p_i225950_8_; + p_i225950_3_ -= p_i225950_9_; + p_i225950_4_ -= p_i225950_10_; + p_i225950_5_ -= p_i225950_11_; + lvt_15_1_ += p_i225950_9_; + lvt_16_1_ += p_i225950_10_; + lvt_17_1_ += p_i225950_11_; + if (p_i225950_12_) { + float lvt_18_1_ = lvt_15_1_; + lvt_15_1_ = p_i225950_3_; + p_i225950_3_ = lvt_18_1_; + } + + PositionTextureVertex lvt_18_2_ = new PositionTextureVertex(p_i225950_3_, p_i225950_4_, p_i225950_5_, 0.0F, 0.0F); + PositionTextureVertex lvt_19_1_ = new PositionTextureVertex(lvt_15_1_, p_i225950_4_, p_i225950_5_, 0.0F, 8.0F); + PositionTextureVertex lvt_20_1_ = new PositionTextureVertex(lvt_15_1_, lvt_16_1_, p_i225950_5_, 8.0F, 8.0F); + PositionTextureVertex lvt_21_1_ = new PositionTextureVertex(p_i225950_3_, lvt_16_1_, p_i225950_5_, 8.0F, 0.0F); + PositionTextureVertex lvt_22_1_ = new PositionTextureVertex(p_i225950_3_, p_i225950_4_, lvt_17_1_, 0.0F, 0.0F); + PositionTextureVertex lvt_23_1_ = new PositionTextureVertex(lvt_15_1_, p_i225950_4_, lvt_17_1_, 0.0F, 8.0F); + PositionTextureVertex lvt_24_1_ = new PositionTextureVertex(lvt_15_1_, lvt_16_1_, lvt_17_1_, 8.0F, 8.0F); + PositionTextureVertex lvt_25_1_ = new PositionTextureVertex(p_i225950_3_, lvt_16_1_, lvt_17_1_, 8.0F, 0.0F); + float lvt_26_1_ = (float)p_i225950_1_; + float lvt_27_1_ = (float)p_i225950_1_ + p_i225950_8_; + float lvt_28_1_ = (float)p_i225950_1_ + p_i225950_8_ + p_i225950_6_; + float lvt_29_1_ = (float)p_i225950_1_ + p_i225950_8_ + p_i225950_6_ + p_i225950_6_; + float lvt_30_1_ = (float)p_i225950_1_ + p_i225950_8_ + p_i225950_6_ + p_i225950_8_; + float lvt_31_1_ = (float)p_i225950_1_ + p_i225950_8_ + p_i225950_6_ + p_i225950_8_ + p_i225950_6_; + float lvt_32_1_ = (float)p_i225950_2_; + float lvt_33_1_ = (float)p_i225950_2_ + p_i225950_8_; + float lvt_34_1_ = (float)p_i225950_2_ + p_i225950_8_ + p_i225950_7_; + this.quads[2] = new TexturedQuad(new PositionTextureVertex[]{lvt_23_1_, lvt_22_1_, lvt_18_2_, lvt_19_1_}, lvt_27_1_, lvt_32_1_, lvt_28_1_, lvt_33_1_, p_i225950_13_, p_i225950_14_, p_i225950_12_, Direction.DOWN); + this.quads[3] = new TexturedQuad(new PositionTextureVertex[]{lvt_20_1_, lvt_21_1_, lvt_25_1_, lvt_24_1_}, lvt_28_1_, lvt_33_1_, lvt_29_1_, lvt_32_1_, p_i225950_13_, p_i225950_14_, p_i225950_12_, Direction.UP); + this.quads[1] = new TexturedQuad(new PositionTextureVertex[]{lvt_18_2_, lvt_22_1_, lvt_25_1_, lvt_21_1_}, lvt_26_1_, lvt_33_1_, lvt_27_1_, lvt_34_1_, p_i225950_13_, p_i225950_14_, p_i225950_12_, Direction.WEST); + this.quads[4] = new TexturedQuad(new PositionTextureVertex[]{lvt_19_1_, lvt_18_2_, lvt_21_1_, lvt_20_1_}, lvt_27_1_, lvt_33_1_, lvt_28_1_, lvt_34_1_, p_i225950_13_, p_i225950_14_, p_i225950_12_, Direction.NORTH); + this.quads[0] = new TexturedQuad(new PositionTextureVertex[]{lvt_23_1_, lvt_19_1_, lvt_20_1_, lvt_24_1_}, lvt_28_1_, lvt_33_1_, lvt_30_1_, lvt_34_1_, p_i225950_13_, p_i225950_14_, p_i225950_12_, Direction.EAST); + this.quads[5] = new TexturedQuad(new PositionTextureVertex[]{lvt_22_1_, lvt_23_1_, lvt_24_1_, lvt_25_1_}, lvt_30_1_, lvt_33_1_, lvt_31_1_, lvt_34_1_, p_i225950_13_, p_i225950_14_, p_i225950_12_, Direction.SOUTH); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/explosion/NuclearExplosionEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/explosion/NuclearExplosionEntity.java new file mode 100644 index 0000000..bd141a4 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/explosion/NuclearExplosionEntity.java @@ -0,0 +1,236 @@ +package net.nuclearteam.createnuclear.content.explosion; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.nuclearteam.createnuclear.*; +import net.nuclearteam.createnuclear.foundation.utility.Maths; +import net.nuclearteam.createnuclear.CNDamageTypes; + +import java.util.List; +import java.util.Stack; + +public class NuclearExplosionEntity extends Entity { + + private boolean spawnedParticle = false; + private Stack destroyingChunks = new Stack<>(); + private static final EntityDataAccessor SIZE = SynchedEntityData.defineId(NuclearExplosionEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor NO_GRIEFING = SynchedEntityData.defineId(NuclearExplosionEntity.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor INTENTIONAL_GAME_DESIGN = SynchedEntityData.defineId(NuclearExplosionEntity.class, EntityDataSerializers.BOOLEAN); + private boolean loadingChunks = false; + + + public NuclearExplosionEntity(EntityType entityType, Level level) { + super(entityType, level); + } + + + + public void tick() { + super.tick(); + int chunksAffected = getChunksAffected(); + int radius = chunksAffected * 15; + if (!spawnedParticle) { + spawnedParticle = true; + int particleY = (int) Math.ceil(this.getY()); + while (particleY > level().getMinBuildHeight() && particleY > this.getY() - radius / 2F && isDestroyable(level().getBlockState(BlockPos.containing(this.getX(), particleY, this.getZ())))) { + particleY--; + } + level().addAlwaysVisibleParticle(CNParticleRegistry.NUCLEAR_MUSHROOM_CLOUD.get(), true, this.getX(), particleY + 2, this.getZ(), this.getSize(), isIntentionalGameDesign() ? 1.0F : 0.0F, 0); + } + if (tickCount > 40 && destroyingChunks.isEmpty()) { + this.remove(RemovalReason.DISCARDED); + } else { + if (!level().isClientSide && !isNoGriefing()) { + if (!loadingChunks && !this.isRemoved()) { + loadingChunks = true; + loadChunksAround(true); + } + if (destroyingChunks.isEmpty()) { + BlockPos center = this.blockPosition(); + int chunks = chunksAffected; + for (int i = -chunks; i <= chunks; i++) { + for (int j = -chunks; j <= chunks; j++) { + for (int k = -chunks; k <= chunks; k++) { + destroyingChunks.push(center.offset(i * 16, j * 16, k * 16)); + } + } + } + destroyingChunks.sort((blockPos1, blockPos2) -> Double.compare(blockPos2.distManhattan(this.blockPosition()), blockPos1.distManhattan(this.blockPosition()))); + } else { + int tickChunkCount = Math.min(destroyingChunks.size(), 3); + for (int i = 0; i < tickChunkCount; i++) { + removeChunk(radius); + } + } + } + AABB killBox = this.getBoundingBox().inflate(radius + radius * 0.5F, radius * 0.6, radius + radius * 0.5F); + float flingStrength = getSize() * 0.33F; + float maximumDistance = radius + radius * 0.5F + 1; + + for (LivingEntity entity : this.level().getEntitiesOfClass(LivingEntity.class, killBox)) { + float dist = entity.distanceTo(this); + float damage = calculateDamage(dist, maximumDistance); + Vec3 vec3 = entity.position().subtract(this.position()).add(0, 0.3, 0).normalize(); + float playerFling = entity instanceof Player ? 0.5F * flingStrength : flingStrength; + + if (damage > 0) { + if (entity.getType().is(CNTags.CNEntityTags.IRRADIATED_IMMUNE.tag)) { + damage *= 0.25F; + playerFling *= 0.1F; + + } + + if (damage > 0) { + entity.hurt(CNDamageTypes.radiation(entity.level()), damage); + } + } + entity.setDeltaMovement(vec3.scale(damage * 0.1F * playerFling)); + } + } + } + + @Override + public void remove(Entity.RemovalReason removalReason) { + if (!level().isClientSide && loadingChunks) { + loadingChunks = false; + loadChunksAround(false); + } + super.remove(removalReason); + } + + + private int getChunksAffected() { + return (int) Math.ceil(this.getSize()); + } + + private void loadChunksAround(boolean load) { + if (this.level() instanceof ServerLevel serverLevel) { + ChunkPos chunkPos = new ChunkPos(this.blockPosition()); + int dist = Math.max(getChunksAffected(), serverLevel.getServer().getPlayerList().getViewDistance() / 2); + for (int i = -dist; i <= dist; i++) { + for (int j = -dist; j <= dist; j++) { + serverLevel.setChunkForced(chunkPos.x + i, chunkPos.z + j, load); + } + } + } + } + + private float calculateDamage(float dist, float max) { + float revert = (max - dist) / max; + float baseDmg = this.getSize() <= 1.5F ? 100 : 100 + (this.getSize() - 1.5F) * 400; + return revert * baseDmg; + } + + private void removeChunk(int radius) { + BlockPos chunkCorner = destroyingChunks.pop(); + BlockPos.MutableBlockPos carve = new BlockPos.MutableBlockPos(); + BlockPos.MutableBlockPos carveBelow = new BlockPos.MutableBlockPos(); + carve.set(chunkCorner); + carveBelow.set(chunkCorner); + float itemDropModifier = 0.025F / Math.min(1, this.getSize()); + + + Explosion dummyExplosion = new Explosion(level(), this, this.getX(), this.getY(), this.getZ(), 10.0F, false, Explosion.BlockInteraction.DESTROY); + + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 15; y >= 0; y--) { + boolean canSetToFire = false; + carve.set(chunkCorner.getX() + x, Mth.clamp(chunkCorner.getY() + y, level().getMinBuildHeight(), level().getMaxBuildHeight()), chunkCorner.getZ() + z); + float widthSimplexNoise1 = (Maths.sampleNoise3D((float)carve.getX(), (float)carve.getY(), (float)carve.getZ(), (float)radius) - 0.5F) * 0.45F + 0.55F; + double yDist = Maths.smin(0.6F - Math.abs(this.blockPosition().getY() - carve.getY()) / (float) radius, 0.6F, 0.2F); + double distToCenter = carve.distToLowCornerSqr(this.blockPosition().getX(), carve.getY() - 1, this.blockPosition().getZ()); + double targetRadius = yDist * (radius + widthSimplexNoise1 * radius) * radius; + + if (distToCenter <= targetRadius) { + BlockState state = level().getBlockState(carve); + if ((!state.isAir() || !state.getFluidState().isEmpty()) && isDestroyable(state)) { + carveBelow.set(carve.getX(), carve.getY() - 1, carve.getZ()); + canSetToFire = true; + + + + // 1. Create an immutable copy of the position + BlockPos immutablePos = carve.immutable(); + + // 2. Call the explosion behavior + state.onBlockExploded(level(), immutablePos, dummyExplosion); + + // 3. onBlockExploded() doesn't remove plain vanilla blocks (e.g. stone/dirt) by + // itself, so force removal here and let items drop normally + if (level().getBlockState(immutablePos).is(state.getBlock())) { + level().destroyBlock(immutablePos, true); + } + } + } + if (canSetToFire && random.nextFloat() < 0.15 && !level().getBlockState(carveBelow).isAir()) { + level().setBlockAndUpdate(carveBelow.above(), CNBlocks.ENRICHING_FIRE.get().defaultBlockState()); + } + } + } + } + } + + private boolean isDestroyable(BlockState state) { + return !(state.getBlock().getExplosionResistance() >= 3600000); + } + + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + builder.define(SIZE, 1.0F); + builder.define(NO_GRIEFING, false); + builder.define(INTENTIONAL_GAME_DESIGN, false); + } + + public float getSize() { + return this.entityData.get(SIZE); + } + + public void setSize(float f) { + this.entityData.set(SIZE, f); + } + + public boolean isNoGriefing() { + return this.entityData.get(NO_GRIEFING); + } + + public void setNoGriefing(boolean noGriefing) { + this.entityData.set(NO_GRIEFING, noGriefing); + } + + public boolean isIntentionalGameDesign() { + return this.entityData.get(INTENTIONAL_GAME_DESIGN); + } + + public void setIntentionalGameDesign(boolean intentionalGameDesign) { + this.entityData.set(INTENTIONAL_GAME_DESIGN, intentionalGameDesign); + } + + + @Override + protected void readAdditionalSaveData(CompoundTag compoundTag) { + loadingChunks = compoundTag.getBoolean("WasLoadingChunks"); + } + + @Override + protected void addAdditionalSaveData(CompoundTag compoundTag) { + compoundTag.putBoolean("WasLoadingChunks", loadingChunks); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/logistics/BigFluidStack.java b/src/main/java/net/nuclearteam/createnuclear/content/logistics/BigFluidStack.java new file mode 100644 index 0000000..eb32ef4 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/logistics/BigFluidStack.java @@ -0,0 +1,93 @@ +package net.nuclearteam.createnuclear.content.logistics; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.util.ExtraCodecs; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.fluids.FluidStack; +import net.nuclearteam.createnuclear.api.ReactorFluidTypesValue; +import net.nuclearteam.createnuclear.api.multiblock.fluid.ReactorFluidType; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class BigFluidStack { + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + FluidStack.OPTIONAL_CODEC.fieldOf("fluid_stack").forGetter(s -> s.stack), + ExtraCodecs.NON_NEGATIVE_INT.fieldOf("amount").forGetter(s -> s.amount) + ).apply(i, BigFluidStack::new)); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + FluidStack.OPTIONAL_STREAM_CODEC, s -> s.stack, + ByteBufCodecs.VAR_INT, s -> s.amount, + BigFluidStack::new + ); + + public static final int INF = 1_000_000_000; + + public FluidStack stack; + public int amount; + + public BigFluidStack(FluidStack stack) { + this(stack, 1); + } + + public BigFluidStack(FluidStack stack, int amount) { + this.stack = stack; + this.amount = amount; + } + + public boolean isInfinite() { + return amount >= INF; + } + + public static BigFluidStack receive(RegistryFriendlyByteBuf buffer) { + return new BigFluidStack(FluidStack.STREAM_CODEC.decode(buffer), buffer.readVarInt()); + } + + public static Comparator comparator() { + return (i1, i2) -> Integer.compare(i2.amount, i1.amount); + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) + return true; + if (obj instanceof BigFluidStack other) + return FluidStack.isSameFluidSameComponents(stack, other.stack) && amount == other.amount; + return false; + } + + @Override + public int hashCode() { + return (nullHash(stack) * 31) ^ Integer.hashCode(amount); + } + + int nullHash(Object o) { + return o == null ? 0 : o.hashCode(); + } + + @Override + public String toString() { + return "(" + stack.getHoverName().getString() + " x" + amount + ")"; + } + + public static List duplicateWrappers(List list) { + List copy = new ArrayList<>(); + for (BigFluidStack bigFluidStack : list) + copy.add(new BigFluidStack(bigFluidStack.stack, bigFluidStack.amount)); + return copy; + } + + public ReactorFluidType getFluidtype(@Nullable Level level) { + if (level == null) { + return ReactorFluidTypesValue.getReactorFluidType(this.stack.getFluid()); + } + return ReactorFluidType.resolveReactorFluidType(this.stack.getFluid(), level); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/CNMultiblock.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/CNMultiblock.java index e0dd7b6..60164c4 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/CNMultiblock.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/CNMultiblock.java @@ -22,7 +22,7 @@ public class CNMultiblock { static { REGISTRATE_MULTIBLOCK.register("createnuclear:reactor", - TypeMultiblock.REACTOR, + TypeMultiblock.REACTOR_T1, SimpleMultiBlockAislePatternBuilder.start() .aisle(AAAAA, AAAAA, AAAAA, AAAAA, AAAAA) .aisle(AABAA, ADADA, BACAB, ADADA, AABAA) @@ -37,7 +37,7 @@ public class CNMultiblock { .where('D', stateIs(CNBlocks.REACTOR_COOLER.get())) .where('*', stateIs(CNBlocks.REACTOR_CONTROLLER.get())) .where('O', stateIs(CNBlocks.REACTOR_OUTPUT.get())) - .where('I', stateIs(CNBlocks.REACTOR_INPUT.get())) + .where('I', stateIs(CNBlocks.REACTOR_ROD_INPUT.get())) .build() ); } diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/MultiblockHelpers.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/MultiblockHelpers.java new file mode 100644 index 0000000..08e05d1 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/MultiblockHelpers.java @@ -0,0 +1,75 @@ +package net.nuclearteam.createnuclear.content.multiblock; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import net.nuclearteam.createnuclear.content.multiblock.pattern.ReactorPattern; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancementBehaviour; + +import javax.annotation.Nullable; +import java.util.function.BiConsumer; + +public class MultiblockHelpers { + private static final ReactorPattern pattern = new ReactorPattern(); + + /** + * Utility helpers for handling multiblock placement and removal events. + * These helpers locate the controller for a given part position and + * invoke provided callbacks to register or remove parts. + */ + public static void handleOnPlace(BlockPos pos, Level level, BiConsumer register) { + handleOnPlace(pos, level, true); + + BlockPos controllerPos = pattern.findControllerPos(pos, level); + if (controllerPos != null) { + ReactorControllerBlockEntity controllerBlockEntity = (ReactorControllerBlockEntity) level.getBlockEntity(controllerPos); + if (controllerBlockEntity != null) { + register.accept(controllerBlockEntity, pos); + } + } + } + + /** + * Called when a multiblock part is placed. Locates the controller and + * invokes {@code register} with the controller and placed part position. + */ + public static void handleOnPlace(BlockPos pos, Level level, boolean first) { + pattern.findController(pos, level, first); + } + + public static void handleRemoval(BlockPos pos, Level level, BiConsumer remover) { + handleOnPlace(pos, level, false); + + BlockPos controllerPos = pattern.findControllerPos(pos, level); + if (controllerPos != null) { + ReactorControllerBlockEntity controllerBlockEntity = (ReactorControllerBlockEntity) level.getBlockEntity(controllerPos); + if (controllerBlockEntity != null) { + remover.accept(controllerBlockEntity, pos); + } + } + } + + public static ReactorControllerBlockEntity getControllerForPart(Level level, BlockPos pos) { + /** + * Returns the controller entity for the given part position, or null + * if no valid controller is found. + */ + BlockPos controllerPos = pattern.findControllerPos(pos, level); + if (controllerPos != null) { + return (ReactorControllerBlockEntity) level.getBlockEntity(controllerPos); + } + + return null; + } + + public static void handleAdvancedPlacedBy(BlockPos pos, Level level, @Nullable LivingEntity entity) { + if (entity == null) return; + + ReactorControllerBlockEntity controller = getControllerForPart(level, pos); + + if (controller != null) { + CNAdvancementBehaviour.setPlacedBy(level, controller.getBlockPos(), entity); + } + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/ReactorAssembler.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/ReactorAssembler.java new file mode 100644 index 0000000..38bb598 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/ReactorAssembler.java @@ -0,0 +1,146 @@ +package net.nuclearteam.createnuclear.content.multiblock; + +import net.createmod.catnip.lang.LangBuilder; +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.nuclearteam.createnuclear.CNBlocks; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.api.multiblock.BlockPattern; +import net.nuclearteam.createnuclear.api.multiblock.TypeMultiblock; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import net.nuclearteam.createnuclear.content.multiblock.frame.ReactorFrameEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.ReactorFluidInputEntity; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancement; +import net.nuclearteam.createnuclear.foundation.utility.CreateNuclearLang; +import net.nuclearteam.createnuclear.foundation.utility.NotifyUtil; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; + +import java.util.List; + +public final class ReactorAssembler { + + public static final int configRadius = CNConfigs.server().notify.warningDistance.get(); + public static final boolean configWarnAll = CNConfigs.server().notify.warnAllPlayers.get(); + + private ReactorAssembler() {} + + public static void assemble(BlockPos pos, Level level) { + ReactorControllerBlockEntity entity = getBlockEntity(level, pos); + if (entity == null) return; + + BlockPattern result = CNMultiblock.REGISTRATE_MULTIBLOCK.findStructure(level, pos, entity); + if (result == null) return; + + CreateNuclear.LOGGER.warn("ReactorAssembler#assemble id: {}, size: {}, name: {}", + result.id(), result.data().getSize(), result.data().getName()); + + sendMessageToPlayer(level, pos, CreateNuclearLang.translate("notification.reactor.assembled"), !entity.isAssembled()); + + switch (result.data()) { + case REACTOR_T1 -> entity.getAdvancement().awardPlayer(CNAdvancement.T1_REACTOR); + case REACTOR_T2 -> entity.getAdvancement().awardPlayer(CNAdvancement.T2_REACTOR); + case REACTOR_T3 -> entity.getAdvancement().awardPlayer(CNAdvancement.T3_REACTOR); + } + + entity.setMultiblockSize(result.data().getSize()); + entity.setAssembled(true); + entity.setMultiblockStructure(entity.getStructureBounds(pos, entity.getMultiblockSize(), entity.getMultiblockFacing())); + + findAndRegisterSpecialBlocks(entity.getMultiblockPos(), entity, level); + } + + public static void disassemble(BlockPos pos, Level level) { + ReactorControllerBlockEntity entity = getBlockEntity(level, pos); + if (entity == null || !entity.isAssembled()) return; + + BlockPattern result = CNMultiblock.REGISTRATE_MULTIBLOCK.findStructure(level, pos, entity); + if (result != null) return; + + sendMessageToPlayer(level, pos, CreateNuclearLang.translate("notification.reactor.disassembled"), true); + + entity.setAssembled(false); + entity.removeIOAll(); + } + + public static void findAndRegisterSpecialBlocks(int[] reactorPos, ReactorControllerBlockEntity entity, Level level) { + int xMin = reactorPos[0], xMax = reactorPos[1]; + int yMin = reactorPos[2], yMax = reactorPos[3]; + int zMin = reactorPos[4], zMax = reactorPos[5]; + + final Block reactorOutputBlock = CNBlocks.REACTOR_OUTPUT.get(); + final Block reactorInputBlock = CNBlocks.REACTOR_ROD_INPUT.get(); + final Block reactorInputFluidBlock = CNBlocks.REACTOR_FLUID_INPUT.get(); + //final Block reactorAlarmBlock = CNBlocks.REACTOR_ALARM.get(); + final Block reactorFrameBlock = CNBlocks.REACTOR_FRAME.get(); + + BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); + + int frameMinY = Integer.MAX_VALUE; + int frameMaxY = Integer.MIN_VALUE; + + for (int y = yMin; y <= yMax; y++) { + boolean isYBoundary = (y == yMin || y == yMax); + for (int x = xMin; x <= xMax; x++) { + boolean isXBoundary = (x == xMin || x == xMax); + for (int z = zMin; z <= zMax; z++) { + boolean isZBoundary = (z == zMin || z == zMax); + + if (!(isYBoundary || isXBoundary || isZBoundary)) continue; + + mutablePos.set(x, y, z); + BlockState blockState = level.getBlockState(mutablePos); + + if (blockState.is(reactorOutputBlock)) { + entity.addOutput(mutablePos.immutable()); + } else if (blockState.is(reactorInputBlock)) { + entity.addInput(mutablePos.immutable()); + } else if (blockState.is(reactorInputFluidBlock)) { + entity.addInputFluid(mutablePos.immutable()); + if (level.getBlockEntity(mutablePos) instanceof ReactorFluidInputEntity fluidInput) { + fluidInput.applyReactorTierCapacity(entity.getMultiblockSize()); + } + //} else if (blockState.is(reactorAlarmBlock)) { + // entity.addAlarm(mutablePos.immutable()); + } else if (blockState.is(reactorFrameBlock)) { + if (level.getBlockEntity(mutablePos) instanceof ReactorFrameEntity frame) { + frame.setController(entity.getBlockPos()); + } + frameMinY = Math.min(frameMinY, y); + frameMaxY = Math.max(frameMaxY, y); + } + } + } + } + + if (frameMinY != Integer.MAX_VALUE) { + entity.setFrameColumn(frameMinY, frameMaxY); + } + } + + private static ReactorControllerBlockEntity getBlockEntity(Level level, BlockPos pos) { + if (level.getBlockEntity(pos) instanceof ReactorControllerBlockEntity entity) { + return entity; + } + return null; + } + + @Deprecated + private static List getPlayersInRadius(Level level, BlockPos center, double radius) { + AABB box = new AABB(center).inflate(radius); + return level.getEntitiesOfClass(Player.class, box); + } + + private static void sendMessageToPlayer(Level level, BlockPos pos, LangBuilder component, boolean condition) { + if (!condition) return; + + NotifyUtil.sendActionBar(level, pos, + component, + ChatFormatting.GOLD, configRadius, configWarnAll + ); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarm.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarm.java new file mode 100644 index 0000000..a8d9eed --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarm.java @@ -0,0 +1,84 @@ +package net.nuclearteam.createnuclear.content.multiblock.alarm; + +import com.simibubi.create.foundation.block.IBE; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.nuclearteam.createnuclear.CNBlockEntityTypes; +import net.nuclearteam.createnuclear.content.multiblock.MultiblockHelpers; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import net.nuclearteam.createnuclear.content.multiblock.pattern.ReactorPattern; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancementBehaviour; + +import javax.annotation.Nullable; + +public class ReactorAlarm extends Block implements IBE { + public static final BooleanProperty POWERED = BlockStateProperties.POWERED; + protected ReactorPattern pattern = new ReactorPattern(); + + public ReactorAlarm(Properties props) { + super(props); + this.registerDefaultState(this.defaultBlockState().setValue(POWERED, false)); + } + + // Registers this alarm with the multiblock controller when placed + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { + super.onPlace(state, level, pos, oldState, isMoving); + MultiblockHelpers.handleOnPlace(pos, level, ReactorControllerBlockEntity::addAlarm); + } + + @Override + public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity pPlacer, ItemStack stack) { + super.setPlacedBy(level, pos, state, pPlacer, stack); + MultiblockHelpers.handleAdvancedPlacedBy(pos, level, pPlacer); + } + + // Unregisters this alarm from the multiblock controller on removal (piston, explosion, etc.) + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { + if (!state.is(newState.getBlock())) { + MultiblockHelpers.handleRemoval(pos, level, ReactorControllerBlockEntity::removeAlarm); + super.onRemove(state, level, pos, newState, isMoving); + } + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + return this.defaultBlockState().setValue(POWERED, ctx.getLevel().hasNeighborSignal(ctx.getClickedPos())); + } + + @Override + public void neighborChanged(BlockState state, Level level, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) { + if (level.isClientSide) return; + boolean poweredNow = level.hasNeighborSignal(pos); + if (state.getValue(POWERED) != poweredNow) { + level.setBlock(pos, state.setValue(POWERED, poweredNow), 3); + } + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(POWERED); + } + + @Override + public Class getBlockEntityClass() { + return ReactorAlarmEntity.class; + } + + @Override + public BlockEntityType getBlockEntityType() { + return CNBlockEntityTypes.REACTOR_ALARM.get(); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarmEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarmEntity.java new file mode 100644 index 0000000..3348e40 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarmEntity.java @@ -0,0 +1,101 @@ +package net.nuclearteam.createnuclear.content.multiblock.alarm; + +import com.simibubi.create.foundation.blockEntity.SmartBlockEntity; +import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.nuclearteam.createnuclear.CNSoundEvents; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancement; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancementBehaviour; + +import java.util.List; + +public class ReactorAlarmEntity extends SmartBlockEntity { + + public ReactorControllerBlockEntity controller = null; + private CNAdvancementBehaviour advancement; + private boolean advancementAwarded = false; // Prevents repeatedly awarding the advancement on every tick + + @OnlyIn(Dist.CLIENT) + protected ReactorAlarmSoundInstance soundInstance; + + public ReactorAlarmEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Override + public void tick() { + super.tick(); + if (level == null || !(getBlockState().getBlock() instanceof ReactorAlarm)) return; + + if (level.isClientSide) { + tickAudio(); // DistExecutor is unnecessary here; the level-side check is enough and avoids thread overhead + } else { + tickServer(); + } + } + + @OnlyIn(Dist.CLIENT) + protected void tickAudio() { + BlockState state = getBlockState(); + boolean powered = state.hasProperty(ReactorAlarm.POWERED) && state.getValue(ReactorAlarm.POWERED); + + if (!powered) { + stopSound(); + return; + } + + if (soundInstance == null || soundInstance.isStopped()) { + Minecraft minecraft = Minecraft.getInstance(); + try { + soundInstance = new ReactorAlarmSoundInstance(level, worldPosition, CNSoundEvents.REACTOR_ALARM_LOOP.getMainEvent()); + minecraft.getSoundManager().play(soundInstance); + } catch (Exception e) { + CreateNuclear.LOGGER.warn("Failed to start alarm sound: " + e.getMessage()); + } + } + } + + @OnlyIn(Dist.CLIENT) + private void stopSound() { + if (soundInstance != null) { + try { + soundInstance.fadeOut(); + } catch (Exception e) { + CreateNuclear.LOGGER.warn("Error while stopping the sound: " + e.getMessage()); + } + soundInstance = null; + } + } + + private void tickServer() { + BlockState state = getBlockState(); + if (!(state.getBlock() instanceof ReactorAlarm)) return; + + boolean powered = state.getValue(ReactorAlarm.POWERED); + + if (powered) { + if (!advancementAwarded) { // Award it only once, on the rising edge + this.advancement.awardPlayer(CNAdvancement.SILENCE_THE_CORE); + advancementAwarded = true; + } + } else { + advancementAwarded = false; // Reset when the alarm turns off + } + } + + @Override + public void addBehaviours(List behaviours) { + behaviours.add(advancement = new CNAdvancementBehaviour(this, CNAdvancement.SILENCE_THE_CORE)); + } + + public void setController(ReactorControllerBlockEntity controller) { + this.controller = controller; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarmSoundInstance.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarmSoundInstance.java new file mode 100644 index 0000000..04793cd --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/alarm/ReactorAlarmSoundInstance.java @@ -0,0 +1,50 @@ +package net.nuclearteam.createnuclear.content.multiblock.alarm; + +import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance; +import net.minecraft.core.BlockPos; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.nuclearteam.createnuclear.CreateNuclear; + +public class ReactorAlarmSoundInstance extends AbstractTickableSoundInstance { + private final BlockPos pos; + private final Level level; + + public ReactorAlarmSoundInstance(Level level, BlockPos pos, SoundEvent sound) { + super(sound, SoundSource.BLOCKS, level != null ? level.random : null); + this.level = level; + this.pos = pos; + if (pos != null) { + this.x = pos.getX(); + this.y = pos.getY(); + this.z = pos.getZ(); + } + this.looping = true; + this.delay = 0; + this.volume = 10F; + } + + @Override + public void tick() { + try { + if (level == null || pos == null) { + stop(); + return; + } + + BlockState state = level.getBlockState(pos); + if (!(state.getBlock() instanceof ReactorAlarm)) { + stop(); + } + } catch (Exception e) { + stop(); + CreateNuclear.LOGGER.warn(e.getMessage()); + } + } + + public void fadeOut() { + stop(); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/casing/ReactorCasing.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/casing/ReactorCasing.java index cbdee86..460a1b1 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/casing/ReactorCasing.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/casing/ReactorCasing.java @@ -22,7 +22,7 @@ import net.nuclearteam.createnuclear.CNBlocks; import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlock; import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInput; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInput; import net.nuclearteam.createnuclear.foundation.utility.CreateNuclearLang; import javax.annotation.Nullable; @@ -69,7 +69,7 @@ public InteractionResult onWrenched(BlockState state, UseOnContext context) { Player player = context.getPlayer(); if (player.getItemInHand(InteractionHand.OFF_HAND).is(Blocks.HOPPER.asItem())) { - level.setBlock(pos, CNBlocks.REACTOR_INPUT.getDefaultState().setValue(ReactorInput.FACING, context.getClickedFace()), 2); + level.setBlock(pos, CNBlocks.REACTOR_ROD_INPUT.getDefaultState().setValue(ReactorRodInput.FACING, context.getClickedFace()), 2); player.sendSystemMessage(Component.translatable("reactor.update.casing.input")); } return InteractionResult.SUCCESS; diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java index d4f4e9d..0569b00 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java @@ -148,7 +148,7 @@ public void playerDestroy(Level level, Player player, BlockPos pos, BlockState s public void Verify(BlockState state, BlockPos pos, Level level, List players, boolean create){ ReactorControllerBlock controller = (ReactorControllerBlock) level.getBlockState(pos).getBlock(); ReactorControllerBlockEntity entity = controller.getBlockEntity(level, pos); - var result = CNMultiblock.REGISTRATE_MULTIBLOCK.findStructure(level, pos); // control the pattern + var result = CNMultiblock.REGISTRATE_MULTIBLOCK.findStructure(level, pos, this.getBlockEntity(level, pos)); // control the pattern if (result != null) { // the pattern is correct for (Player player : players) { diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java index 021e4a1..c5ceb7e 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java @@ -1,46 +1,56 @@ package net.nuclearteam.createnuclear.content.multiblock.controller; -import com.simibubi.create.AllDataComponents; import com.simibubi.create.api.equipment.goggles.IHaveGoggleInformation; +import com.simibubi.create.content.logistics.BigItemStack; import com.simibubi.create.foundation.blockEntity.SmartBlockEntity; import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; import com.simibubi.create.foundation.utility.CreateLang; import com.simibubi.create.foundation.utility.IInteractionChecker; import lib.multiblock.SimpleMultiBlockAislePatternBuilder; -import net.createmod.catnip.data.Iterate; import net.createmod.catnip.platform.CatnipServices; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.HolderLookup; +import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Explosion; +import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.wrapper.EmptyItemHandler; import net.nuclearteam.createnuclear.*; +import net.nuclearteam.createnuclear.content.logistics.BigFluidStack; +import net.nuclearteam.createnuclear.content.multiblock.CNMultiblock; import net.nuclearteam.createnuclear.content.multiblock.IHeat; import net.nuclearteam.createnuclear.content.multiblock.bluePrintItem.PatternData; import net.nuclearteam.createnuclear.content.multiblock.bluePrintItem.ReactorBluePrintData; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.controller.manager.*; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.FluidLockManager; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.PersistentFluidLocks; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.ReactorFluidInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInputEntity; import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutput; import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutputEntity; +import net.nuclearteam.createnuclear.content.multiblock.pattern.ReactorPattern; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancementBehaviour; -import java.util.LinkedHashSet; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static net.nuclearteam.createnuclear.content.multiblock.CNMultiblock.*; import static net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlock.ASSEMBLED; @@ -99,12 +109,221 @@ public class ReactorControllerBlockEntity extends SmartBlockEntity implements II }; private final int[][] offsets = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; + /* + FORGE ARGUMENTS PART + */ + + private final ReactorPattern pattern = new ReactorPattern(); + private int explosionCountdown = 0; + private boolean isExploding = false; + + //private final ConsumptionCycleManager cycleManager = new ConsumptionCycleManager(); + private double liquidLife; + + private BigItemStack bigFuelItem; + private BigItemStack bigCoolerItem; + private List bigFluidStack; + + private int reactorSize = 0; + private String reactorFacing = "null"; + // les pos sont [xMin, xMax, yMin, yMax, zMin, zMax] + private int[] reactorPos; + // Vertical span (block Y) of the reactor frame columns, used to map the + // fluid fill ratio onto the liquid actually drawn in the frame windows. + private int frameColumnMinY = Integer.MAX_VALUE; + private int frameColumnMaxY = Integer.MIN_VALUE; + // Per-tick cache for the fluid drawn in the frame windows. Recomputed from the + // live (synced) input tanks so the displayed fluid and its fill ratio always + // come from the same source and stay consistent for every fluid type. + private long frameFluidCacheTick = -1; + private FluidStack frameFluidCache = FluidStack.EMPTY; + private float frameFluidFillRatioCache = 0f; + private boolean needsToResolveEntities = false; + private double fluidBuffer = 0.0; + + private CNAdvancementBehaviour advancement; + + private final ReactorInputManagerI inputManager; + private final ReactorOutputManagerI outputManager; + private final ReactorInputFluidManagerI inputFluidManager; + private final ReactorAlarmManagerI alarmManager; + + // Client Display Data (Synced via NBT) + private Map clientDisplayItems = new HashMap<>(); + private List clientDisplayFluids = new ArrayList<>(); + private long clientMaxFluidCapacity = 0; + + // services (dependencies) - abstracted behind interfaces to follow DIP + //private final IHeatService heatService; + //private final IPersistenceService persistenceService; + + // service fields are injected; implementations live in separate classes + + // --- Accessors used by external services (persistence) --- + public ReactorControllerInventory getInventoryObject() { + return this.inventory; + } + + /*public void deserializeInventory(CompoundTag tag) { + this.inventory.deserializeNBT(tag); + } + + public CompoundTag serializeInventory() { + return this.inventory.serializeNBT(); + }*/ + + public ItemStack getConfiguredPattern() { + return this.configuredPattern; + } + + public void setConfiguredPattern(ItemStack stack) { + this.configuredPattern = stack; + } + public CompoundTag getConfiguredPatternTag() { + return this.configuredPattern.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + } + + public int[] getMultiblockPos() { + return this.reactorPos; + } + + public BigItemStack getBigFuelItem() { + return this.bigFuelItem; + } + + public void setBigFuelItem(BigItemStack b) { + this.bigFuelItem = b; + } + public BigItemStack getBigCoolerItem() { + return this.bigCoolerItem; + } + + public void setBigCoolerItem(BigItemStack b) { + this.bigCoolerItem = b; + } + + public List getBigFluidStack() { + return this.bigFluidStack; + } + + public void setBigFluidStack(List b) { + this.bigFluidStack = b; + } + + private void refreshFrameFluidCache() { + if (level == null) return; + long now = level.getGameTime(); + if (now == frameFluidCacheTick) return; + frameFluidCacheTick = now; + + long amount = 0; + long capacity = 0; + FluidStack fluid = FluidStack.EMPTY; + for (IFluidHandler handler : inputFluidManager.getFuildHandlers(level)) { + int tanks = handler.getTanks(); + for (int t = 0; t < tanks; t++) { + FluidStack stack = handler.getFluidInTank(t); + capacity += handler.getTankCapacity(t); + amount += stack.getAmount(); + if (fluid.isEmpty() && !stack.isEmpty()) fluid = stack.copy(); + } + } + + frameFluidCache = fluid; + frameFluidFillRatioCache = capacity > 0 ? Math.min(1f, (float) amount / (float) capacity) : 0f; + } + + public FluidStack getDisplayedFluid() { + refreshFrameFluidCache(); + return frameFluidCache; + } + + /** + * How full the reactor's fluid input is, in the range {@code [0, 1]}, used by + * the frame renderer to size the visible liquid column. + */ + public float getDisplayedFluidFillRatio() { + refreshFrameFluidCache(); + return frameFluidFillRatioCache; + } + + /** Records the lowest and highest frame block-Y of the assembled reactor. */ + public void setFrameColumn(int minY, int maxY) { + if (this.frameColumnMinY == minY && this.frameColumnMaxY == maxY) return; + this.frameColumnMinY = minY; + this.frameColumnMaxY = maxY; + notifyUpdate(); + } + + /** @return the lowest frame block-Y, or {@link Integer#MAX_VALUE} if unknown. */ + public int getFrameColumnMinY() { + return frameColumnMinY; + } + + /** @return the highest frame block-Y, or {@link Integer#MIN_VALUE} if unknown. */ + public int getFrameColumnMaxY() { + return frameColumnMaxY; + } + + public boolean hasFrameColumn() { + return frameColumnMinY != Integer.MAX_VALUE && frameColumnMaxY != Integer.MIN_VALUE + && frameColumnMaxY >= frameColumnMinY; + } + + public int getMultiblockSize() { + return this.reactorSize; + } + + public void setMultiblockSize(int s) { + this.reactorSize = s; + } + + public String getMultiblockFacing() { + return this.reactorFacing; + } + + public void setMultiblockFacing(String f) { + this.reactorFacing = f; + } + + public CNAdvancementBehaviour getAdvancement() { + return this.advancement; + } + + public void setMultiblockStructure(int[] p) { + this.reactorPos = p; + } + + public double getLiquidLife() { + return this.liquidLife; + } + + public void setLiquidLife(double l) { + this.liquidLife = l; + } + + /** + * Main constructor allowing dependency injection for testability and DIP + * compliance. + */ public ReactorControllerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); - inventory = new ReactorControllerInventory(this); - configuredPattern = ItemStack.EMPTY; + this.inventory = new ReactorControllerInventory(this); + this.configuredPattern = ItemStack.EMPTY; + + this.inputManager = new ReactorInputManager(); + this.outputManager = new ReactorOutputManager(); + this.inputFluidManager = new ReactorInputFluidManager(); + this.alarmManager = new ReactorAlarmManager(); + + this.bigFuelItem = new BigItemStack(ItemStack.EMPTY); + this.bigCoolerItem = new BigItemStack(ItemStack.EMPTY); + this.bigFluidStack = new ArrayList<>(); + + //this.heatService = new DefaultHeatService(new HeatManager()); + //this.persistenceService = new DefaultPersistenceService(); } @Override @@ -184,6 +403,23 @@ protected void write(CompoundTag compound, HolderLookup.Provider registries, boo super.write(compound, registries, clientPacket); } + public boolean isAssembled() { + if (level == null) + return false; + try { + return level.getBlockState(worldPosition).getValue(ASSEMBLED); + } catch (Exception e) { + return false; + } + } + + public void setAssembled(boolean assembled) { + if (level == null) + return; + level.setBlockAndUpdate(worldPosition, getBlockState().setValue(ASSEMBLED, assembled)); + this.setChanged(); + } + public enum State { ON, OFF } @@ -205,7 +441,7 @@ public void tick() { } BlockEntity blockEntity = level.getBlockEntity(getBlockPosForReactor('I')); - if (blockEntity instanceof ReactorInputEntity be) { + if (blockEntity instanceof ReactorRodInputEntity be) { fuelItem = be.inventory.getStackInSlot(0); coolerItem = be.inventory.getStackInSlot(1); @@ -372,7 +608,7 @@ private BlockPos getBlockPosForReactor(char character) { for (int[] direction : directions) { BlockPos newPos = posController.offset(direction[0], direction[1], direction[2]); - if (level.getBlockState(newPos).is(CNBlocks.REACTOR_INPUT.get())) { + if (level.getBlockState(newPos).is(CNBlocks.REACTOR_ROD_INPUT.get())) { posInput = newPos; break; } @@ -415,7 +651,7 @@ private static BlockPos FindController(char character) { .where('D', a -> a.getState().is(CNBlocks.REACTOR_COOLER.get())) .where('*', a -> a.getState().is(CNBlocks.REACTOR_CONTROLLER.get())) .where('O', a -> a.getState().is(CNBlocks.REACTOR_OUTPUT.get())) - .where('I', a -> a.getState().is(CNBlocks.REACTOR_INPUT.get())) + .where('I', a -> a.getState().is(CNBlocks.REACTOR_ROD_INPUT.get())) .getDistanceController(character); } @@ -447,4 +683,188 @@ public void rotate(BlockState state, BlockPos pos, Level level, int rotation, bo } } } + + @Deprecated + public int[] getStructureBounds(BlockPos startPos, int structureSize, String facing) { + int[] northOffsets5x5 = new int[] { -2, 2, -3, 3, 0, 4 }; + int[] northOffsets7x7 = new int[] { -3, 3, -4, 4, 0, 6 }; + int[] northOffsets9x9 = new int[] { -4, 4, -5, 5, 0, 8 }; + + int[] eastOffsets5x5 = new int[] { -4, 0, -3, 3, -2, 2 }; + int[] eastOffsets7x7 = new int[] { -6, 0, -4, 4, -3, 3 }; + int[] eastOffsets9x9 = new int[] { -8, 0, -5, 5, -4, 4 }; + + int[] southOffsets5x5 = new int[] { -2, 2, -3, 3, -4, 0 }; + int[] southOffsets7x7 = new int[] { -3, 3, -4, 4, -6, 0 }; + int[] southOffsets9x9 = new int[] { -4, 4, -5, 5, -8, 0 }; + + int[] westOffsets5x5 = new int[] { 0, 4, -3, 3, -2, 2 }; + int[] westOffsets7x7 = new int[] { 0, 6, -4, 4, -3, 3 }; + int[] westOffsets9x9 = new int[] { 0, 8, -5, 5, -4, 4 }; + + switch (facing) { + case "north": + switch (structureSize) { + case 5: + return applyOffset(startPos, northOffsets5x5); + case 7: + return applyOffset(startPos, northOffsets7x7); + case 9: + return applyOffset(startPos, northOffsets9x9); + } + case "east": + switch (structureSize) { + case 5: + return applyOffset(startPos, eastOffsets5x5); + case 7: + return applyOffset(startPos, eastOffsets7x7); + case 9: + return applyOffset(startPos, eastOffsets9x9); + } + case "south": + switch (structureSize) { + case 5: + return applyOffset(startPos, southOffsets5x5); + case 7: + return applyOffset(startPos, southOffsets7x7); + case 9: + return applyOffset(startPos, southOffsets9x9); + } + case "west": + switch (structureSize) { + case 5: + return applyOffset(startPos, westOffsets5x5); + case 7: + return applyOffset(startPos, westOffsets7x7); + case 9: + return applyOffset(startPos, westOffsets9x9); + } + default: + return new int[] { 0, 0, 0, 0, 0, 0 }; + } + } + + @Deprecated + private int[] applyOffset(BlockPos pos, int[] offset) { + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + + return new int[] { x + offset[0], x + offset[1], y + offset[2], y + offset[3], z + offset[4], z + offset[5] }; + } + + public void addInput(BlockPos inputPos) { + this.inputManager.addBlock(inputPos); + this.setChanged(); + } + + public void removeInput(BlockPos inputPos) { + this.inputManager.removeBlock(inputPos); + this.setChanged(); + } + + public void addOutput(BlockPos outputPos) { + this.outputManager.addBlock(outputPos); + this.setChanged(); + } + + public void removeOutput(BlockPos outputPos) { + this.outputManager.removeBlock(outputPos); + this.setChanged(); + } + + public void addInputFluid(BlockPos outputPos) { + this.inputFluidManager.addBlock(outputPos); + this.setChanged(); + } + + public void removeInputFluid(BlockPos outputPos) { + this.inputFluidManager.removeBlock(outputPos); + this.setChanged(); + } + + public void addAlarm(BlockPos alarmPos) { + this.alarmManager.addBlock(alarmPos); + this.setChanged(); + } + + public void removeAlarm(BlockPos alarmPos) { + this.alarmManager.removeBlock(alarmPos); + this.setChanged(); + } + + public void removeIOAll() { + this.inputManager.clearInvalid(level); + this.outputManager.clearInvalid(level); + this.inputFluidManager.clearInvalid(level); + this.alarmManager.clearInvalid(level); + this.setChanged(); + } + + /** Try to lock this controller to the given Fluid. Returns true if allowed. */ + public boolean tryLockFluid(Fluid fluid) { + // server-persistent approach (preferred): use PersistentFluidLocks when on + // server + if (level != null && !level.isClientSide && level instanceof ServerLevel serverLevel) { + return PersistentFluidLocks.get(serverLevel).tryLock(getBlockPos(), fluid); + } + // fallback to in-memory manager (single-server-run) + return FluidLockManager.tryLock(getBlockPos(), fluid); + } + + /** Returns whether the given FluidStack is acceptable for this controller. */ + public boolean canAcceptFluid(FluidStack stack) { + if (stack == null || stack.isEmpty()) + return true; + if (level != null && !level.isClientSide && level instanceof ServerLevel serverLevel) { + return PersistentFluidLocks.get(serverLevel).canAccept(getBlockPos(), stack.getFluid()); + } + return FluidLockManager.canAccept(getBlockPos(), stack); + } + + /** Force-clear the lock on this controller. */ + public void clearLock() { + if (level != null && !level.isClientSide && level instanceof ServerLevel serverLevel) { + PersistentFluidLocks.get(serverLevel).clearLock(getBlockPos()); + } else { + FluidLockManager.clearLock(getBlockPos()); + } + setChanged(); + sendData(); + } + + public void clearLockIfAllInputsEmpty() { + if (level == null || level.isClientSide) + return; + + final int SCAN_RADIUS = CNMultiblock.REGISTRATE_MULTIBLOCK.findStructure(level, getBlockPos(), this).data() + .getSize(); // adapte selon la taille max du multiblock + BlockPos center = getBlockPos(); + boolean anyNonEmpty = false; + + for (int dx = -SCAN_RADIUS; dx <= SCAN_RADIUS && !anyNonEmpty; dx++) { + for (int dy = -SCAN_RADIUS; dy <= SCAN_RADIUS && !anyNonEmpty; dy++) { + for (int dz = -SCAN_RADIUS; dz <= SCAN_RADIUS && !anyNonEmpty; dz++) { + BlockPos p = center.offset(dx, dy, dz); + BlockEntity be = level.getBlockEntity(p); + if (!(be instanceof ReactorFluidInputEntity)) + continue; + + IFluidHandler handler = level.getCapability(Capabilities.FluidHandler.BLOCK, p, null); + if (handler == null) + continue; + + for (int t = 0; t < handler.getTanks(); t++) { + if (!handler.getFluidInTank(t).isEmpty()) { + anyNonEmpty = true; + break; + } + } + } + } + } + + if (!anyNonEmpty) + clearLock(); + } } \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/AbstractReactorIOManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/AbstractReactorIOManager.java new file mode 100644 index 0000000..b1333cb --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/AbstractReactorIOManager.java @@ -0,0 +1,89 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.Level; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** + * Abstract base providing common implementation for managing + * positions used by IO managers (inputs / outputs). + * + * This class holds a collection of `BlockPos` instances and exposes + * basic operations (add, remove, contains, iteration via `resolveBlock`). + * Serialization (`read`/`write`) and validation (`clearInvalid`) are + * left abstract for concrete implementations to provide. + */ +public abstract class AbstractReactorIOManager implements ReactorIOManager { + /** List of `BlockPos` instances tracked by this manager. */ + protected final List positions = new ArrayList<>(); + + /** + * Adds `pos` if non-null and not already present; returns true when added. + */ + @Override + public void addBlock(BlockPos pos) { + if (pos == null) return; + if (!contains(pos)) { + positions.add(pos); + } + } + + /** + * Removes `pos` if present. + */ + @Override + public void removeBlock(BlockPos pos) { + positions.remove(pos); + } + + /** Tests whether `pos` is present. */ + @Override + public boolean contains(BlockPos pos) { + return positions.contains(pos); + } + + /** Returns the current number of tracked positions. */ + @Override + public int size() { + return positions.size(); + } + + /** Returns an immutable copy of the tracked positions. */ + @Override + public List getBlocksPosition() { + return List.copyOf(positions); + } + + /** + * Resolves each position with `resolver` and returns a list of non-null results. + * Useful to convert positions to block entities or handlers. + */ + @Override + public List resolveBlock(Level level, Function resolver) { + List result = new ArrayList<>(); + for (BlockPos p: new ArrayList<>(positions)) { + T r = resolver.apply(p); + if (r != null) result.add(r); + } + return result; + } + + /** Serialization: implementation provided by subclasses. */ + @Override + public abstract void read(CompoundTag compound); + + /** Deserialization: implementation provided by subclasses. */ + @Override + public abstract void write(CompoundTag compound); + + /** + * Removes invalid positions (e.g. unloaded chunk, missing block entity). + * Subclasses must implement manager-specific validation logic. + */ + @Override + public abstract void clearInvalid(Level level); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorAlarmManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorAlarmManager.java new file mode 100644 index 0000000..afefd1d --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorAlarmManager.java @@ -0,0 +1,71 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +//import net.nuclearteam.createnuclear.content.multiblock.alarm.ReactorAlarmEntity; + +import java.util.ArrayList; +import java.util.List; + +public class ReactorAlarmManager extends AbstractReactorIOManager implements ReactorAlarmManagerI { + private static final String NBT_KEY = "ReactorAlarms"; + + @Override + public void write(CompoundTag compound) { + ListTag list = new ListTag(); + for (BlockPos pos : positions) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", pos.getX()); + tag.putInt("y", pos.getY()); + tag.putInt("z", pos.getZ()); + list.add(tag); + } + compound.put(NBT_KEY, list); + } + + @Override + public void read(CompoundTag compound) { + positions.clear(); + if (!compound.contains(NBT_KEY)) return; + ListTag list = compound.getList(NBT_KEY, Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); ++i) { + CompoundTag tag = list.getCompound(i); + positions.add(new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"))); + } + } + + @Override + public void clearInvalid(Level level) { + if (level == null) return; + List toRemove = new ArrayList<>(); + + for (BlockPos p : positions) { + if (!level.isLoaded(p)) continue; // On ne supprime pas si le chunk est juste déchargé + + BlockEntity be = level.getBlockEntity(p); + // Si le bloc n'existe plus ou n'est plus une alarme, on marque pour suppression + //if (be == null || !(be instanceof ReactorAlarmEntity)) { + // toRemove.add(p); + //} + } + positions.removeAll(toRemove); + } + + @Override + public List getBlocksPosition(Level level) { + if (level == null) return List.of(); + + List validPositions = new ArrayList<>(); + for (BlockPos p : this.positions) { + // On vérifie que l'entité est bien chargée et du bon type + //if (level.isLoaded(p) && level.getBlockEntity(p) instanceof ReactorAlarmEntity) { + // validPositions.add(p); + //} + } + return List.copyOf(validPositions); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorAlarmManagerI.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorAlarmManagerI.java new file mode 100644 index 0000000..e1e154a --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorAlarmManagerI.java @@ -0,0 +1,12 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; + +import java.util.List; + +public interface ReactorAlarmManagerI extends ReactorIOManager { + /** * Retourne une copie immuable des positions d'alarmes valides dans le monde actuel. + */ + List getBlocksPosition(Level level); +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorIOManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorIOManager.java new file mode 100644 index 0000000..e51f8a9 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorIOManager.java @@ -0,0 +1,49 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.Level; + +import java.util.List; +import java.util.function.Function; + +/** + * Generic manager interface for reactor IO positions (inputs or outputs). + * Defines expected operations to manage a collection of `BlockPos` instances: + * serialization, validation, and resolution into concrete objects via a `resolver`. + */ +public interface ReactorIOManager { + /** Returns true when the position is known. */ + boolean contains(BlockPos pos); + + /** Current number of tracked positions. */ + int size(); + + /** Reads state from an NBT tag (deserialization). */ + void read(CompoundTag compound); + + /** Writes state to an NBT tag (serialization). */ + void write(CompoundTag compound); + + /** + * Adds a position; returns true if added. + */ + void addBlock(BlockPos pos); + + /** + * Removes a position; returns true if it was present and removed. + */ + void removeBlock(BlockPos pos); + + /** Returns an immutable copy of tracked positions. */ + List getBlocksPosition(); + + /** Removes invalid positions for the provided `level` (e.g. missing block entity). */ + void clearInvalid(Level level); + + /** + * Resolves each `BlockPos` into an instance using `resolver` and returns + * the list of non-null results. + */ + List resolveBlock(Level level, Function resolver); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputFluidManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputFluidManager.java new file mode 100644 index 0000000..9963fa3 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputFluidManager.java @@ -0,0 +1,161 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import net.neoforged.neoforge.fluids.capability.IFluidHandler.FluidAction; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.ReactorFluidInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.VirtualReactorInputFluid; + +import java.util.ArrayList; +import java.util.List; + +public class ReactorInputFluidManager extends AbstractReactorIOManager implements ReactorInputFluidManagerI { + private static final String NBT_KEY = "ReactorInputFluid"; + + /** + * Manager that tracks fluid input blocks for a reactor. + * Responsible for serializing tracked positions, validating + * handlers, reporting aggregated inventory and extracting fluids. + */ + + @Override + /** + * Read tracked positions from NBT data. + * Existing positions are cleared before reading. + */ + public void read(CompoundTag compound) { + positions.clear(); + if (!compound.contains(NBT_KEY)) return; + ListTag list = compound.getList(NBT_KEY, Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); ++i) { + CompoundTag tag = list.getCompound(i); + positions.add(new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"))); + } + } + + @Override + /** + * Write tracked positions to the provided NBT compound. + */ + public void write(CompoundTag compound) { + ListTag list = new ListTag(); + for (BlockPos pos : positions) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", pos.getX()); + tag.putInt("y", pos.getY()); + tag.putInt("z", pos.getZ()); + list.add(tag); + } + compound.put(NBT_KEY, list); + } + + @Override + /** + * Remove any tracked positions that are no longer valid in the given level. + */ + public void clearInvalid(Level level) { + List toRemove = new ArrayList<>(); + for (BlockPos p: positions) { + if (level == null || !level.isLoaded(p)) { + toRemove.add(p); + continue; + } + + BlockEntity be = level.getBlockEntity(p); + if (be == null) { + toRemove.add(p); + continue; + } + + IFluidHandler cap = level.getCapability(Capabilities.FluidHandler.BLOCK, p, null); + if (cap == null) toRemove.add(p); + } + + positions.removeAll(toRemove); + } + + @Override + /** + * Return an immutable list of tracked block positions that correspond + * to fluid input entities in the given level. + */ + public List getBlocksPosition(Level level) { + List positions = new ArrayList<>(); + + for (BlockPos p : this.getBlocksPosition()) { + CreateNuclear.LOGGER.warn("getBlocksPosition: {} {}", level.getBlockEntity(p), level.getBlockEntity(p) instanceof ReactorFluidInputEntity); + if (level.getBlockEntity(p) instanceof ReactorFluidInputEntity) positions.add(p); + } + return List.copyOf(positions); + } + + @Override + /** + * Collect and return fluid handler capabilities for all tracked positions. + */ + public List getFuildHandlers(Level level) { + List handlers = new ArrayList<>(); + for (BlockPos p : new ArrayList<>(positions)) { + if (level == null || !level.isLoaded(p)) continue; + BlockEntity be = level.getBlockEntity(p); + if (be == null) continue; + IFluidHandler cap = level.getCapability(Capabilities.FluidHandler.BLOCK, p, null); + if (cap != null) { + handlers.add(cap); + } + } + + return handlers; + } + + @Override + /** + * Build and return a virtual aggregated inventory of all input fluids. + */ + public VirtualReactorInputFluid getInventory(Level level) { + VirtualReactorInputFluid virtualReactorInputFluid = new VirtualReactorInputFluid(); + List handlers = this.getFuildHandlers(level); + if (handlers.isEmpty()) return new VirtualReactorInputFluid(); + + for (IFluidHandler h : handlers) { + int tank = h.getTanks(); + virtualReactorInputFluid.addFluid(h.getFluidInTank(tank)); + } + + return virtualReactorInputFluid; + } + + @Override + /** + * Attempt to extract up to {@code fluidNeeded} units of fluid from tracked handlers. + * Returns true if the full amount was extracted. + */ + public boolean extractFluids(Level level, int fluidNeeded) { + if (level == null) return false; + boolean isExtracted = false; + List handlers = getFuildHandlers(level); + if (handlers.isEmpty()) return false; + + for (IFluidHandler handler : handlers) { + int tank = handler.getTanks(); + FluidStack stack = handler.getFluidInTank(tank); + if (stack.isEmpty()) continue; + int toExtract = Math.min(fluidNeeded, stack.getAmount()); + if (toExtract > 1) { + handler.drain(toExtract, FluidAction.EXECUTE); + isExtracted = true; + } + if (fluidNeeded <= 0) break; + } + + return isExtracted; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputFluidManagerI.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputFluidManagerI.java new file mode 100644 index 0000000..6001c7e --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputFluidManagerI.java @@ -0,0 +1,38 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.VirtualReactorInputFluid; + +import java.util.List; + +public interface ReactorInputFluidManagerI extends ReactorIOManager { + /** + * Interface for managing reactor input fluids. + * Implementations track input fluid sources and provide + * accessors and extraction methods for reactor consumption. + */ + /** Returns an immutable copy of tracked positions. */ + List getBlocksPosition(Level level); + + /** + * Returns a list of fluid handler capabilities for the tracked positions. + * @param level the current world level + */ + List getFuildHandlers(Level level); + + /** + * Returns a virtual aggregated inventory representing all tracked input fluids. + * @param level the current world level + */ + VirtualReactorInputFluid getInventory(Level level); + + /** + * Attempt to extract the requested amount of fluid from tracked sources. + * @param level current world level + * @param fluidNeeded amount of fluid required + * @return true if the requested amount was successfully extracted + */ + boolean extractFluids(Level level, int fluidNeeded); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputManager.java new file mode 100644 index 0000000..653340f --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputManager.java @@ -0,0 +1,195 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.Container; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.items.IItemHandler; +import net.nuclearteam.createnuclear.api.multiblock.rods.RodType.TypeRodPredicate; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.item.VirtualReactorInputsItem; + +import java.util.ArrayList; +import java.util.List; + +/** + * Manager for reactor input positions (`ReactorInput`). + * + * Serializes positions as x/y/z triplets and provides utilities to + * obtain valid `IItemHandler` instances present at those positions. + */ +public class ReactorInputManager extends AbstractReactorIOManager implements ReactorInputManagerI { + private static final String NBT_KEY = "ReactorInput"; + + @Override + public void write(CompoundTag compound) { + ListTag list = new ListTag(); + for (BlockPos pos : positions) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", pos.getX()); + tag.putInt("y", pos.getY()); + tag.putInt("z", pos.getZ()); + list.add(tag); + } + compound.put(NBT_KEY, list); + } + + @Override + public void read(CompoundTag compound) { + positions.clear(); + if (!compound.contains(NBT_KEY)) return; + ListTag list = compound.getList(NBT_KEY, Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); ++i) { + CompoundTag tag = list.getCompound(i); + positions.add(new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"))); + } + } + + /** + * Retrieves all `IItemHandler` instances located at the input positions. + * Returns an empty list when no handlers are found. + */ + @Override + public List getItemHandlers(Level level) { + List handlers = new ArrayList<>(); + for (BlockPos p: new ArrayList<>(positions)) { + if (level == null || !level.isLoaded(p)) continue; + BlockEntity be = level.getBlockEntity(p); + if (be == null) continue; + IItemHandler cap = level.getCapability(Capabilities.ItemHandler.BLOCK, p, null); + if (cap != null) { + handlers.add(cap); + } + } + + return handlers; + } + + @Override + public VirtualReactorInputsItem getInventory(Level level) { + List handlers = this.getItemHandlers(level); + if (handlers.isEmpty()) return new VirtualReactorInputsItem(); + + int totalFuel = 0; + int totalCooler = 0; + for (IItemHandler h : handlers) { + int slots = h.getSlots(); + for (int s = 0; s < slots; s++) { + ItemStack st = h.getStackInSlot(s); + + if (TypeRodPredicate.IS_FUEL.test(st)) totalFuel += st.getCount(); + else if (TypeRodPredicate.IS_COOLED.test(st)) totalCooler += st.getCount(); + } + } + + return new VirtualReactorInputsItem(totalFuel, totalCooler); + } + + @Override + public boolean extractItems(Level level, int fuelNeeded, int coolerNeeded) { + if (level == null) return false; + List handlers = getItemHandlers(level); + if (handlers.isEmpty()) return false; + + int fuelRemaining = fuelNeeded; + int coolerRemaining = coolerNeeded; + + for (IItemHandler handler : handlers) { + int slots = handler.getSlots(); + for (int s = 0; s < slots && (fuelRemaining > 0 || coolerRemaining > 0); s++) { + ItemStack stack = handler.getStackInSlot(s); + if (stack.isEmpty()) continue; + + + if (fuelRemaining > 0 && TypeRodPredicate.IS_FUEL.test(stack)) { + int toExtract = Math.min(fuelRemaining, stack.getCount()); + handler.extractItem(s, toExtract, false); + fuelRemaining -= toExtract; + } else if (coolerRemaining > 0 && TypeRodPredicate.IS_COOLED.test(stack)) { + int toExtract = Math.min(coolerRemaining, stack.getCount()); + handler.extractItem(s, toExtract, false); + coolerRemaining -= toExtract; + } + } + if (fuelRemaining <= 0 && coolerRemaining <= 0) break; + } + + return fuelRemaining <= 0 && coolerRemaining <= 0; + } + + public boolean extractItemByName(Level level, String itemName) { + if (level == null || itemName == null) return false; + + List handlers = getItemHandlers(level); + if (handlers.isEmpty()) return false; + + for (IItemHandler handler : handlers) { + int slots = handler.getSlots(); + for (int s = 0; s < slots; s++) { + ItemStack stack = handler.getStackInSlot(s); + if (stack.isEmpty()) continue; + + // On récupère le nom de l'item (ex: "uranium_rod") + String registryPath = BuiltInRegistries.ITEM.getKey(stack.getItem()).getPath(); + + // Comparaison intelligente : on ignore la casse et les underscores (_) + if (isMatching(registryPath, itemName)) { + // On tente d'extraire 1 unité + ItemStack extracted = handler.extractItem(s, 1, false); + + // Si l'extraction a réussi, on s'arrête là et on renvoie true + if (!extracted.isEmpty()) { + return true; + } + } + } + } + + return false; // On n'a pas trouvé l'item demandé + } + + /** + * Helper pour comparer "GraphiteRod" avec "graphite_rod" + */ + private boolean isMatching(String registryPath, String configName) { + String cleanPath = registryPath.replace("_", "").toLowerCase(); + String cleanConfig = configName.replace("_", "").toLowerCase(); + return cleanPath.equals(cleanConfig); + } + + /** + * Cleans up invalid positions (unloaded chunk, missing block entity, + * or not a `Container`). + */ + @Override + public void clearInvalid(Level level) { + List toRemove = new ArrayList<>(); + for (BlockPos p: positions) { + if (level == null || !level.isLoaded(p)) { + toRemove.add(p); + continue; + } + + BlockEntity be = level.getBlockEntity(p); + if (be == null || !(be instanceof Container)) toRemove.add(p); + } + + positions.removeAll(toRemove); + } + + @Override + public List getBlocksPosition(Level level) { + List positions = new ArrayList<>(); + + for (BlockPos p : this.getBlocksPosition()) { + if (level.getBlockEntity(p) instanceof ReactorRodInputEntity) positions.add(p); + } + return List.copyOf(positions); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputManagerI.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputManagerI.java new file mode 100644 index 0000000..5f1a975 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorInputManagerI.java @@ -0,0 +1,28 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.items.IItemHandler; +import net.nuclearteam.createnuclear.content.multiblock.input.item.VirtualReactorInputsItem; + +import java.util.List; + +/** + * Interface exposing operations specific to reactor inputs. + * Currently provides access to `IItemHandler` instances at resolved input positions. + */ +public interface ReactorInputManagerI extends ReactorIOManager { + /** + * Retrieves valid item handlers for the given `level`. + * May return an empty list if no valid positions exist. + */ + List getItemHandlers(Level level); + + /** Returns an immutable copy of tracked positions. */ + List getBlocksPosition(Level level); + + VirtualReactorInputsItem getInventory(Level level); + + boolean extractItems(Level level, int fuelNeeded, int coolerNeeded); + boolean extractItemByName(Level level, String itemName); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorOutputManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorOutputManager.java new file mode 100644 index 0000000..34e9f03 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorOutputManager.java @@ -0,0 +1,141 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutputEntity; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +/** + * Manager for a reactor's outputs (`ReactorOutput`). + * Handles serialization of output positions and distribution + * of energy via `distributeSU`. + */ +public class ReactorOutputManager extends AbstractReactorIOManager implements ReactorOutputManagerI { + private static final String NBT_KEY = "ReactorOutputs"; + + @Override + public void write(CompoundTag compound) { + ListTag list = new ListTag(); + for (BlockPos p : positions) { + CompoundTag t = new CompoundTag(); + t.putLong("p", p.asLong()); + list.add(t); + } + compound.put(NBT_KEY, list); + } + + @Override + public void read(CompoundTag compound) { + positions.clear(); + if (!compound.contains(NBT_KEY)) return; + ListTag list = compound.getList(NBT_KEY, 10); + for (int i = 0; i < list.size(); i++) { + BlockPos p = BlockPos.of(list.getCompound(i).getLong("p")); + positions.add(p); + } + } + + /** + * Example usage: + * double totalSUToDistribute = this.reactorPower; // or another value depending on logic + * if (!reactorOutputEntityList.isEmpty() && totalSUToDistribute > 0) { + * double remaining = outputManager.distributeSU(totalSUToDistribute, reactorOutputEntityList, (outEntity, amount) -> { + * // Inserter: apply `amount` to the output and return the amount not accepted. + * // Here we accept everything and apply the corresponding speed (rounded) + * int speed = (int) Math.round(amount); + * try { + * outEntity.setSpeed(speed); + * outEntity.heat = speed; // adjust heat if needed + * outEntity.updateSpeed = true; + * outEntity.updateGeneratedRotation(); + * return 0.0; // all accepted + * } catch (Exception e) { + * return amount; // nothing accepted on error + * } + * }); + * // `remaining` contains the SU that couldn't be inserted — you can store or drop it + * this.reactorPower = (float) remaining; // example: keep the remainder + * } + * @param totalSU total SU to distribute + * @param level world level (may be null) + * @param inserter function that applies an amount to an output and returns not accepted amount + * @return amount of SU not inserted + */ + public double distributeSU(double totalSU, Level level, BiFunction inserter) { + if (totalSU <= 0 || inserter == null) return totalSU; + List validOutputs = new ArrayList<>(); + for (BlockPos p : new ArrayList<>(positions)) { + if (level == null || !level.isLoaded(p)) continue; + BlockEntity be = level.getBlockEntity(p); + if (be instanceof ReactorOutputEntity out) validOutputs.add(out); + } + if (validOutputs.isEmpty()) return totalSU; + + double remaining = totalSU; + List active = new ArrayList<>(validOutputs); + final double MIN_ACCEPTED_SU = 1e-6; + + // Distribute evenly and reallocate the remainder among outputs still able to accept + boolean progress = true; + while (remaining > MIN_ACCEPTED_SU && !active.isEmpty() && progress) { + progress = false; + double share = remaining / active.size(); + List stillActive = new ArrayList<>(); + + for (ReactorOutputEntity out : active) { + double toInsert = share; + double notAccepted; + try { + notAccepted = inserter.apply(out, toInsert); + } catch (Exception e) { + notAccepted = toInsert; + } + if (Double.isNaN(notAccepted) || notAccepted < 0) notAccepted = 0; + if (notAccepted > toInsert) notAccepted = toInsert; + double inserted = toInsert - notAccepted; + + if (inserted > MIN_ACCEPTED_SU) { + progress = true; + remaining -= inserted; + // keep this output active; it may accept more in the next round + stillActive.add(out); + } + // if inserted == 0, remove it (do not attempt to send more) + } + + active = stillActive; + } + + return remaining; + } + + @Override + public void clearInvalid(Level level) { + List toRemove = new ArrayList<>(); + for (BlockPos p : positions) { + if (level == null || !level.isLoaded(p)) { + toRemove.add(p); + continue; + } + BlockEntity be = level.getBlockEntity(p); + if (!(be instanceof ReactorOutputEntity)) toRemove.add(p); + } + positions.removeAll(toRemove); + } + + @Override + public List getBlocksPosition(Level level) { + List positions = new ArrayList<>(); + + for (BlockPos p : this.getBlocksPosition()) { + if (level.getBlockEntity(p) instanceof ReactorOutputEntity) positions.add(p); + } + return List.copyOf(positions); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorOutputManagerI.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorOutputManagerI.java new file mode 100644 index 0000000..45ef40a --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/manager/ReactorOutputManagerI.java @@ -0,0 +1,27 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.manager; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.nuclearteam.createnuclear.content.multiblock.output.ReactorOutputEntity; + +import java.util.List; +import java.util.function.BiFunction; + +/** + * Interface exposing operations specific to reactor outputs. + * The main responsibility is to distribute an amount of energy (SU) + * among valid outputs and use an `inserter` to apply quantities to each + * `ReactorOutputEntity`. + */ +public interface ReactorOutputManagerI extends ReactorIOManager { + /** + * Distributes `totalSU` among valid outputs in `level`. + * The `inserter` receives the target entity and the proposed amount and + * must return the amount not accepted (>= 0). + * Returns the remaining amount that was not inserted. + */ + double distributeSU(double totalSU, Level level, BiFunction inserter); + + /** Returns an immutable copy of tracked positions. */ + List getBlocksPosition(Level level); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorCooler/ReactorCooler.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/cooler/ReactorCooler.java similarity index 97% rename from src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorCooler/ReactorCooler.java rename to src/main/java/net/nuclearteam/createnuclear/content/multiblock/cooler/ReactorCooler.java index 7ff6fc0..2c66f9b 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorCooler/ReactorCooler.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/cooler/ReactorCooler.java @@ -1,4 +1,4 @@ -package net.nuclearteam.createnuclear.content.multiblock.reactorCooler; +package net.nuclearteam.createnuclear.content.multiblock.cooler; import com.simibubi.create.content.equipment.wrench.IWrenchable; import net.minecraft.core.BlockPos; diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java index e0d4eff..1eb1704 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java @@ -6,8 +6,6 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.nuclearteam.createnuclear.CNBlocks; -import net.nuclearteam.createnuclear.CNDataComponents; -import net.nuclearteam.createnuclear.CreateNuclear; import net.nuclearteam.createnuclear.content.multiblock.IHeat; import net.nuclearteam.createnuclear.content.multiblock.casing.ReactorCasingEntity; import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; @@ -63,7 +61,7 @@ private static BlockPos FindController(char character) { .where('D', a -> a.getState().is(CNBlocks.REACTOR_COOLER.get())) .where('*', a -> a.getState().is(CNBlocks.REACTOR_CONTROLLER.get())) .where('O', a -> a.getState().is(CNBlocks.REACTOR_OUTPUT.get())) - .where('I', a -> a.getState().is(CNBlocks.REACTOR_INPUT.get())) + .where('I', a -> a.getState().is(CNBlocks.REACTOR_ROD_INPUT.get())) .getDistanceController(character); } diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/fluid/CNReactorFluidTypes.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/fluid/CNReactorFluidTypes.java new file mode 100644 index 0000000..b75a8ed --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/fluid/CNReactorFluidTypes.java @@ -0,0 +1,56 @@ +package net.nuclearteam.createnuclear.content.multiblock.fluid; + +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.material.Fluids; +import net.nuclearteam.createnuclear.CNFluids; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.api.CreateNuclearRegistries; +import net.nuclearteam.createnuclear.api.multiblock.fluid.ReactorFluidType; + +import static net.nuclearteam.createnuclear.api.ReactorFluidTypesValue.DEFAULT_REACTOR_FLUID_TYPE; + +public class CNReactorFluidTypes { + public static final ResourceKey FALLBACK = ResourceKey.create(CreateNuclearRegistries.FLUID_TYPE, CreateNuclear.asResource("fallback")); + + /** + * Static registry of reactor fluid types ({@link ReactorFluidType}) for the CreateNuclear mod. + * + *

This class exposes the {@link ResourceKey}s and the {@code bootstrap} + * method invoked by the datapack system to register reactor fluid types used + * by the multiblock (for example, specific fluids with heat/efficiency + * characteristics). The example below shows how to create a + * {@code ReactorFluidType} via its {@code ReactorFluidType.Builder}.

+ * + *

Example — Custom Reactor Fluid:

+ *
{@code
+     * register(ctx, "name", new ReactorFluidType.Builder()
+     *             .maxHeat(int)
+     *             .efficiency(int)
+     *             .fluid(Fluid)
+     *             .build());
+     * }
+ * + * @see ReactorFluidType + * @see CreateNuclearRegistries + */ + public static void bootstrap(BootstrapContext ctx) { + register(ctx, "fallback", DEFAULT_REACTOR_FLUID_TYPE); + register(ctx, "water", new ReactorFluidType.Builder() + .fluid(Fluids.WATER) + .maxHeat(2048) + .efficiency(1000) + .build() + ); + /* register(ctx, "nitrogen", new ReactorFluidType.Builder() + .fluid(CNFluids.LIQUID_NITROGEN.getSource()) + .maxHeat(8196) + .efficiency(100) + .build() + );*/ + } + + private static void register(BootstrapContext ctx, String name, ReactorFluidType type) { + ctx.register(ResourceKey.create(CreateNuclearRegistries.FLUID_TYPE, CreateNuclear.asResource(name)), type); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/frame/ReactorFrameEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/frame/ReactorFrameEntity.java new file mode 100644 index 0000000..893a660 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/frame/ReactorFrameEntity.java @@ -0,0 +1,80 @@ +package net.nuclearteam.createnuclear.content.multiblock.frame; + +import com.simibubi.create.foundation.blockEntity.SmartBlockEntity; +import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Block entity for {@link ReactorFrame}. It only stores a reference to the + * reactor controller that owns this frame so the client-side renderer + * ({@link ReactorFrameRenderer}) can fetch the reactor's current fluid and + * draw it dynamically inside the frame window. + * + *

The controller position is assigned by + * {@link net.nuclearteam.createnuclear.content.multiblock.ReactorAssembler} + * during assembly and persisted/synced through {@link SmartBlockEntity}.

+ */ +public class ReactorFrameEntity extends SmartBlockEntity { + + @Nullable + private BlockPos controller; + + public ReactorFrameEntity(BlockEntityType type, BlockPos pos, BlockState state) { + + super(type, pos, state); + setController(pos); + } + + @Override + public void addBehaviours(List behaviours) { } + + /** Assigns the owning reactor controller and syncs the change to clients. */ + public void setController(@Nullable BlockPos controllerPos) { + if (java.util.Objects.equals(this.controller, controllerPos)) return; + this.controller = controllerPos; + notifyUpdate(); + } + + @Nullable + public BlockPos getController() { + return controller; + } + + /** + * Resolves the controller block entity, returning {@code null} when no + * controller is assigned or the referenced block is no longer a controller. + */ + @Nullable + public ReactorControllerBlockEntity getControllerEntity() { + if (controller == null || level == null) return null; + if (level.getBlockEntity(controller) instanceof ReactorControllerBlockEntity controllerEntity) + return controllerEntity; + return null; + } + + @Override + protected void write(CompoundTag tag, HolderLookup.Provider registries, boolean clientPacket) { + super.write(tag, registries, clientPacket); + if (controller != null) { + tag.putLong("Controller", controller.asLong()); + } + } + + @Override + protected void read(CompoundTag tag, HolderLookup.Provider registries, boolean clientPacket) { + super.read(tag, registries, clientPacket); + if (tag.contains("Controller")) { + this.controller = BlockPos.of(tag.getLong("Controller")); + } else { + this.controller = null; + } + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/frame/ReactorFrameRenderer.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/frame/ReactorFrameRenderer.java new file mode 100644 index 0000000..154340c --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/frame/ReactorFrameRenderer.java @@ -0,0 +1,73 @@ +package net.nuclearteam.createnuclear.content.multiblock.frame; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRenderer; +import net.createmod.catnip.platform.CatnipServices; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.neoforged.neoforge.fluids.FluidStack; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; + +/** + * Renders the reactor fluid dynamically inside the {@link ReactorFrame} window. + * The fluid (texture + tint) is resolved from the owning reactor controller, so + * the visible liquid reflects whichever fluid the reactor actually uses + * (water, liquid nitrogen, ...) instead of a texture baked into the model. + */ +public class ReactorFrameRenderer extends SafeBlockEntityRenderer { + + // Horizontal interior of the window (1..15 px on a 16 px block). + private static final float X_MIN = 1f / 16f; + private static final float X_MAX = 15f / 16f; + private static final float Z_MIN = 1f / 16f; + private static final float Z_MAX = 15f / 16f; + + public ReactorFrameRenderer(BlockEntityRendererProvider.Context context) { } + + @Override + protected void renderSafe(ReactorFrameEntity be, float partialTicks, PoseStack ms, MultiBufferSource buffer, + int light, int overlay) { + ReactorControllerBlockEntity controller = be.getControllerEntity(); + if (controller == null) return; + + FluidStack fluid = controller.getDisplayedFluid(); + if (fluid == null || fluid.isEmpty()) return; + + // Local vertical bounds of the liquid volume inside this block, matching + // what used to be baked into each frame part model + // (frame_top / frame_middle / frame_bottom / frame_none). + float boxYMin; + float boxYMax; + switch (be.getBlockState().getValue(ReactorFrame.PART)) { + case START -> { boxYMin = 0f; boxYMax = 9f / 16f; } + case MIDDLE -> { boxYMin = 0f; boxYMax = 1f; } + case END -> { boxYMin = 4f / 16f; boxYMax = 1f; } + default -> { boxYMin = 2.9f / 16f; boxYMax = 9.9f / 16f; } + } + + // Clamp the liquid to the reactor's global fill level so the whole wall + // shares one continuous surface that rises from the bottom as the input + // fills. The level is mapped over the range that liquid actually occupies: + // from the bottom frame's lip (frameMinY + 4/16) to the top frame's cap + // (frameMaxY + 9/16), so even a nearly-empty reactor still shows a sliver. + float yMax = boxYMax; + if (controller.hasFrameColumn()) { + float ratio = controller.getDisplayedFluidFillRatio(); + double liquidBottomWorldY = controller.getFrameColumnMinY() + 4.0 / 16.0; + double liquidTopWorldY = controller.getFrameColumnMaxY() + 9.0 / 16.0; + double surfaceWorldY = liquidBottomWorldY + ratio * (liquidTopWorldY - liquidBottomWorldY); + double localSurface = surfaceWorldY - be.getBlockPos().getY(); + if (localSurface <= boxYMin) return; // liquid level is below this block + yMax = (float) Math.min(boxYMax, localSurface); + } + + // Last arg (invertGasses) must be false: liquid nitrogen has density 0, so + // it counts as "lighter than air" and would otherwise be flipped 180°, + // hiding the top surface. We always fill bottom-to-top here. + CatnipServices.FLUID_RENDERER.renderFluidBox( + fluid.getFluid().defaultFluidState(), // Conversion de FluidStack en FluidState + X_MIN, boxYMin, Z_MIN, X_MAX, yMax, Z_MAX, + buffer, ms, light, false, false + ); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputGenerator.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputGenerator.java deleted file mode 100644 index 04c9518..0000000 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.nuclearteam.createnuclear.content.multiblock.input; - -import com.simibubi.create.foundation.data.SpecialBlockStateGen; -import com.tterrag.registrate.providers.DataGenContext; -import com.tterrag.registrate.providers.RegistrateBlockstateProvider; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.client.model.generators.ModelFile; - -public class ReactorInputGenerator extends SpecialBlockStateGen { - @Override - protected int getXRotation(BlockState state) { - return 0; - } - - @Override - protected int getYRotation(BlockState state) { - return horizontalAngle(state.getValue(ReactorInput.FACING)); - } - - @Override - public ModelFile getModel(DataGenContext ctx, RegistrateBlockstateProvider prov, BlockState state) { - return prov.models().getExistingFile(prov.modLoc("block/reactor/input/block")); - } -} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/FluidLockManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/FluidLockManager.java new file mode 100644 index 0000000..b646651 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/FluidLockManager.java @@ -0,0 +1,38 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.fluid; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * In-memory manager for ephemeral fluid locks on multiblock controllers. + * This class provides simple concurrent lock semantics to ensure a controller + * accepts only a single fluid type while a lock is held. + */ +public class FluidLockManager { + private static final Map locks = new ConcurrentHashMap<>(); + + public static boolean tryLock(BlockPos controllerPos, Fluid fluid) { + if (fluid == null) return true; + // Attempt to set the lock to the specified fluid if none exists + return locks.compute(controllerPos, (k,v) -> v == null ? fluid : v) == fluid; + } + + public static boolean canAccept(BlockPos controllerPos, FluidStack stack) { + if (stack == null || stack.isEmpty()) return true; + // True if no fluid locked or the locked fluid matches the stack + Fluid locked = locks.get(controllerPos); + return locked == null || locked == stack.getFluid(); + } + + public static void clearLock(BlockPos controller) { + locks.remove(controller); + } + + public static Fluid getLockedFluid(BlockPos controller) { + return locks.get(controller); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/PersistentFluidLocks.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/PersistentFluidLocks.java new file mode 100644 index 0000000..8328565 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/PersistentFluidLocks.java @@ -0,0 +1,105 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.fluid; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.saveddata.SavedData; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class PersistentFluidLocks extends SavedData { + private final Map locks = new ConcurrentHashMap<>(); + private static final String DATA_NAME = "createnuclear_fluid_locks"; + + /** + * Persistent storage for fluid locks associated with multiblock controllers. + * Locks are persisted to world saved data so controllers keep their preferred + * fluid across server restarts. + */ + public static SavedData.Factory factory() { + return new SavedData.Factory<>( + PersistentFluidLocks::new, + (tag, registries) -> { + PersistentFluidLocks d = new PersistentFluidLocks(); + d.readFrom(tag); + return d; + } + ); + } + + public static PersistentFluidLocks get(ServerLevel level) { + return level.getDataStorage().computeIfAbsent(PersistentFluidLocks.factory(), DATA_NAME); + } + + /** + * Attempt to acquire a persistent lock for the specified controller position. + * @param pos controller block position + * @param fluid fluid to lock to; if null the call is a no-op and returns true + * @return true if the lock was acquired or already held for the same fluid + */ + public boolean tryLock(BlockPos pos, Fluid fluid) { + if (fluid == null) return true; + boolean ok = locks.compute(pos, (k,v) -> v == null ? fluid : v) == fluid; + if (ok) setDirty(); + return ok; + } + + /** + * Check whether the given fluid is acceptable for the controller at {@code pos}. + * @param pos controller position + * @param fluid fluid to check + * @return true if no lock is present or the lock matches the provided fluid + */ + public boolean canAccept(BlockPos pos, Fluid fluid) { + if (fluid == null) return true; + Fluid locked = locks.get(pos); + return locked == null || locked == fluid; + } + + /** + * Clear any persistent lock for the specified controller position. + * Marks the saved data as dirty when a lock was removed. + */ + public void clearLock(BlockPos pos) { + if (locks.remove(pos) != null) setDirty(); + } + + public void readFrom(CompoundTag nbt) { + locks.clear(); + ListTag list = nbt.getList("locks", 10); + list.forEach(tag -> { + CompoundTag e = (CompoundTag) tag; + BlockPos pos = new BlockPos(e.getInt("x"), e.getInt("y"), e.getInt("z")); + ResourceLocation rl = ResourceLocation.tryParse(e.getString("fluid")); + if (rl != null) { + Fluid f = BuiltInRegistries.FLUID.get(rl); + if (f != null) locks.put(pos, f); + } + }); + } + + @Override + public @NotNull CompoundTag save(@NotNull CompoundTag nbt, @NotNull HolderLookup.Provider registries) { + ListTag list = new ListTag(); + locks.forEach((pos, fluid) -> { + CompoundTag e = new CompoundTag(); + e.putInt("x", pos.getX()); + e.putInt("y", pos.getY()); + e.putInt("z", pos.getZ()); + ResourceLocation rl = BuiltInRegistries.FLUID.getKey(fluid); + if (rl != null) { + e.putString("fluid", rl.toString()); + } + list.add(e); + }); + nbt.put("locks", list); + return nbt; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/PlayerInteracteReactorFluidInput.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/PlayerInteracteReactorFluidInput.java new file mode 100644 index 0000000..5cb663f --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/PlayerInteracteReactorFluidInput.java @@ -0,0 +1,112 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.fluid; + +import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity; +import com.simibubi.create.content.fluids.transfer.GenericItemEmptying; +import com.simibubi.create.content.fluids.transfer.GenericItemFilling; +import com.simibubi.create.foundation.fluid.FluidHelper; +import com.simibubi.create.foundation.fluid.FluidHelper.FluidExchange; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.BlockParticleOption; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; + +public class PlayerInteracteReactorFluidInput { + public static InteractionResult interact(Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, boolean onClient, BlockHitResult ray) { + FluidExchange exchange = null; + ReactorFluidInputEntity be = (ReactorFluidInputEntity) level.getBlockEntity(pos); + if (be == null) { + return InteractionResult.FAIL; + } + + IFluidHandler fluidInput = level.getCapability(Capabilities.FluidHandler.BLOCK, pos, null); + if (fluidInput == null) return InteractionResult.PASS; + + FluidStack prevFluidInInput = fluidInput.getFluidInTank(0).copy(); + + if (FluidHelper.tryEmptyItemIntoBE(level, player, hand, stack, be)) exchange = FluidExchange.ITEM_TO_TANK; + if (FluidHelper.tryFillItemFromBE(level, player, hand, stack, be)) exchange = FluidExchange.TANK_TO_ITEM; + + if (exchange == null) { + if (GenericItemEmptying.canItemBeEmptied(level, stack) || GenericItemFilling.canItemBeFilled(level, stack)) return InteractionResult.SUCCESS; + return InteractionResult.PASS; + } + + SoundEvent soundEvent = null; + BlockState fluidState = null; + FluidStack fluidInInput = fluidInput.getFluidInTank(0); + + if (exchange == FluidExchange.ITEM_TO_TANK) { + if (player.isCreative() && !onClient) { + FluidStack fluidInItem = GenericItemEmptying.emptyItem(level, stack, true).getFirst(); +// if (!fluidInItem.isEmpty() && fluidInInput instanceof ReactorLiquidInput) { +// fluidInInput.set +// } + } + + Fluid fluid = fluidInInput.getFluid(); + fluidState = fluid.defaultFluidState().createLegacyBlock(); + soundEvent = FluidHelper.getEmptySound(fluidInInput); + } + + if (exchange == FluidExchange.TANK_TO_ITEM) { + if ( player.isCreative() && !onClient) { + + } + + Fluid fluid = prevFluidInInput.getFluid(); + fluidState = fluid.defaultFluidState().createLegacyBlock(); + soundEvent = FluidHelper.getFillSound(prevFluidInInput); + } + + if (soundEvent != null && !onClient) { + float pitch = Mth + .clamp(1 - (1f * fluidInInput.getAmount() / (FluidTankBlockEntity.getCapacityMultiplier() * 16)), 0, 1); + pitch /= 1.5f; + pitch += .5f; + pitch += (level.random.nextFloat() - .5f) / 4f; + level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, .5f, pitch); + } + + if (!FluidStack.isSameFluidSameComponents(fluidInInput, prevFluidInInput) || fluidInInput.getAmount() != prevFluidInInput.getAmount()) { + if (be instanceof ReactorFluidInputEntity) { + if (fluidState != null && onClient) { + BlockParticleOption blockParticleData = + new BlockParticleOption(ParticleTypes.BLOCK, fluidState); + float flevel = (float) fluidInInput.getAmount() / fluidInput.getTankCapacity(0); + + boolean reversed = fluidInInput.getFluid() + .getFluidType() + .isLighterThanAir(); + if (reversed) + flevel = 1 - flevel; + + Vec3 vec = ray.getLocation(); + vec = new Vec3(vec.x, be.getBlockPos() + .getY() + flevel * (1 - .5f) + .25f, vec.z); + Vec3 motion = player.position() + .subtract(vec) + .scale(1 / 20f); + vec = vec.add(motion); + level.addParticle(blockParticleData, vec.x, vec.y, vec.z, motion.x, motion.y, motion.z); + return InteractionResult.SUCCESS; + } + } + } + return InteractionResult.SUCCESS; + + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInput.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInput.java new file mode 100644 index 0000000..00c6713 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInput.java @@ -0,0 +1,121 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.fluid; + +import com.simibubi.create.content.equipment.wrench.IWrenchable; +import com.simibubi.create.foundation.block.IBE; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.nuclearteam.createnuclear.CNBlockEntityTypes; +import net.nuclearteam.createnuclear.CNShapes; +import net.nuclearteam.createnuclear.content.multiblock.MultiblockHelpers; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancementBehaviour; +import net.nuclearteam.createnuclear.foundation.block.MultiDirectionalReactorBlock; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; + +public class ReactorFluidInput extends MultiDirectionalReactorBlock implements IWrenchable, IBE { + + public ReactorFluidInput(Properties properties) { + super(properties); + } + + @Override + public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean moved) { + super.onPlace(state, world, pos, oldState, moved); + MultiblockHelpers.handleOnPlace(pos, world, ReactorControllerBlockEntity::addInputFluid); + } + + @Override + public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity pPlacer, ItemStack stack) { + super.setPlacedBy(level, pos, state, pPlacer, stack); + CNAdvancementBehaviour.setPlacedBy(level, pos, pPlacer); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + super.createBlockStateDefinition(builder); + } + + @Override + public InteractionResult onWrenched(BlockState state, UseOnContext context) { + return InteractionResult.SUCCESS; + } + + @Override + public VoxelShape getBlockSupportShape(BlockState pState, BlockGetter pReader, BlockPos pPos) { + return Shapes.block(); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState() + .setValue(FACING, context.getNearestLookingDirection().getOpposite()); + } + + @Override + public @NotNull VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + return CNShapes.REACTOR_FLUID_INPUT.get(state.getValue(FACING)); + } + + @Override + protected @NotNull ItemInteractionResult useItemOn(ItemStack heldItem, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult ray) { + boolean onClient = world.isClientSide; + + if (heldItem.isEmpty()) + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + if (!player.isCreative()) + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + + InteractionResult result = PlayerInteracteReactorFluidInput.interact(world, pos, player, hand, heldItem, onClient, ray); + + // Convertit le vieux InteractionResult en ItemInteractionResult si nécessaire pour NeoForge + return result.consumesAction() ? ItemInteractionResult.SUCCESS : ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + @Override + public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { + super.playerDestroy(level, player, pos, state, blockEntity, tool); + MultiblockHelpers.handleRemoval(pos, level, ReactorControllerBlockEntity::removeInputFluid); + } + + @Override + public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) { + super.onRemove(state, world, pos, newState, isMoving); + MultiblockHelpers.handleRemoval(pos, world, ReactorControllerBlockEntity::removeInputFluid); + } + + @Override + public Class getBlockEntityClass() { + return ReactorFluidInputEntity.class; + } + + @Override + public BlockEntityType getBlockEntityType() { + return CNBlockEntityTypes.REACTOR_FLUID_INPUT.get(); + } + + @Override + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInputEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInputEntity.java new file mode 100644 index 0000000..7f5ce31 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInputEntity.java @@ -0,0 +1,223 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.fluid; + +import com.simibubi.create.api.equipment.goggles.IHaveGoggleInformation; +import com.simibubi.create.foundation.blockEntity.SmartBlockEntity; +import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; +import com.simibubi.create.foundation.fluid.SmartFluidTank; +import net.createmod.catnip.animation.LerpedFloat; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; +import net.nuclearteam.createnuclear.CNBlockEntityTypes; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import net.neoforged.neoforge.fluids.capability.templates.FluidTank; +import net.nuclearteam.createnuclear.content.multiblock.MultiblockHelpers; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class ReactorFluidInputEntity extends SmartBlockEntity implements IHaveGoggleInformation { + + /** Capacité par défaut tant que l'input n'est rattaché à aucun réacteur assemblé. */ + public static final int DEFAULT_CAPACITY = 16000; + + private final FluidTank internalTank; + private final IFluidHandler capabilityHandler; + private LerpedFloat fluidLevel; + + public ReactorFluidInputEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + internalTank = new SmartFluidTank(DEFAULT_CAPACITY, this::onTankContentsChanged); + capabilityHandler = new FilteredFluidHandler(); + } + + public IFluidHandler getCapabilityHandler() { + return capabilityHandler; + } + + public static void registerCapabilities(RegisterCapabilitiesEvent event) { + event.registerBlockEntity( + Capabilities.FluidHandler.BLOCK, + CNBlockEntityTypes.REACTOR_FLUID_INPUT.get(), + (be, context) -> be.capabilityHandler + ); + } + + /** + * Capacité du tank en fonction de la taille du réacteur (tier). + * 5x5 -> tier 1, 7x7 -> tier 2, 9x9 -> tier 3. + */ + public static int getCapacityForReactorSize(int reactorSize) { + return switch (reactorSize) { + case 5 -> 144000; + case 7 -> 448000; + case 9 -> 848000; + default -> DEFAULT_CAPACITY; + }; + } + + /** Applique la capacité correspondant à la taille du réacteur à laquelle cet input appartient. */ + public void applyReactorTierCapacity(int reactorSize) { + int capacity = getCapacityForReactorSize(reactorSize); + if (internalTank.getCapacity() == capacity) return; + internalTank.setCapacity(capacity); + if (level != null && !level.isClientSide) { + setChanged(); + sendData(); + } + } + + @Override + public void addBehaviours(List behaviours) { + } + + @Override + protected void write(CompoundTag tag, HolderLookup.Provider registries, boolean clientPacket) { + super.write(tag, registries, clientPacket); + CompoundTag tankTag = internalTank.writeToNBT(registries, new CompoundTag()); // Pensez à passer registries si requis par la v1.20+ pour les tanks, ou conservez new CompoundTag() selon votre version + tag.put("tank", tankTag); + tag.putInt("capacity", internalTank.getCapacity()); + } + + @Override + protected void read(CompoundTag tag, HolderLookup.Provider registries, boolean clientPacket) { + super.read(tag, registries, clientPacket); + if (tag.contains("capacity")) + internalTank.setCapacity(tag.getInt("capacity")); + internalTank.readFromNBT(registries, tag.getCompound("tank")); // Pareil ici selon l'implémentation de SmartFluidTank + + if (tag.contains("ForceFluidLevel") || fluidLevel == null) + fluidLevel = LerpedFloat.linear() + .startWithValue(getFillState()); + } + + public float getFillState() { + return (float) internalTank.getFluidAmount() / internalTank.getCapacity(); + } + + protected void onTankContentsChanged(FluidStack contents) { + // Avoid accessing level during deserialization when the block entity isn't attached yet + if (this.level == null) { + if (fluidLevel == null) + fluidLevel = LerpedFloat.linear() + .startWithValue(getFillState()); + return; + } + + if (!level.isClientSide) { + setChanged(); + sendData(); + } + + if (isVirtual()) { + if (fluidLevel == null) + fluidLevel = LerpedFloat.linear() + .startWithValue(getFillState()); + fluidLevel.chase(getFillState(), .5f, LerpedFloat.Chaser.EXP); + } + + if (!level.isClientSide && internalTank.getFluidAmount() == 0) { + ReactorControllerBlockEntity controller = MultiblockHelpers.getControllerForPart(level, worldPosition); + if (controller != null) controller.clearLockIfAllInputsEmpty(); + } + } + + @Override + public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneaking) { + return containedFluidTooltip(tooltip, isPlayerSneaking, internalTank); + } + + private class FilteredFluidHandler implements IFluidHandler { + private final IFluidHandler delegate = internalTank; + + @Override + public int getTanks() { + return delegate.getTanks(); + } + + @Override + public @NotNull FluidStack getFluidInTank(int tank) { + return delegate.getFluidInTank(tank); + } + + @Override + public int getTankCapacity(int tank) { + return delegate.getTankCapacity(tank); + } + + @Override + public boolean isFluidValid(int tank, @NotNull FluidStack stack) { + return delegate.isFluidValid(tank, stack); + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + if (resource == null || resource.isEmpty()) return 0; + ReactorControllerBlockEntity controller = MultiblockHelpers.getControllerForPart(level, worldPosition); + BlockPos controllerPos = controller != null ? controller.getBlockPos() : null; + + if (controllerPos != null && level instanceof ServerLevel serverLevel) { + PersistentFluidLocks lock = PersistentFluidLocks.get(serverLevel); + if (!lock.canAccept(controllerPos, resource.getFluid())) return 0; + } else if (controllerPos != null) { + if (!FluidLockManager.canAccept(controllerPos, resource)) return 0; + } + + int filled = delegate.fill(resource, action); + if (filled > 0 && action.execute() && controllerPos != null ){ + if (level instanceof ServerLevel serverLevel) { + PersistentFluidLocks.get(serverLevel).tryLock(controllerPos, resource.getFluid()); + } else { + FluidLockManager.tryLock(controllerPos, resource.getFluid()); + } + } + return filled; + } + + @Override + public @NotNull FluidStack drain(FluidStack resource, FluidAction action) { + FluidStack drained = delegate.drain(resource, action); + if (!drained.isEmpty() && action.execute()) { + ReactorControllerBlockEntity controller = MultiblockHelpers.getControllerForPart(level, worldPosition); + if (controller != null) { + BlockPos controllerPos = controller.getBlockPos(); + if (delegate.getFluidInTank(0).isEmpty()) { + if (level instanceof ServerLevel serverLevel) { + PersistentFluidLocks.get(serverLevel).clearLock(controllerPos); + } else { + FluidLockManager.clearLock(controllerPos); + } + } + } + } + return drained; + } + + @Override + public @NotNull FluidStack drain(int maxDrain, FluidAction action) { + FluidStack drained = delegate.drain(maxDrain, action); + if (!drained.isEmpty() && action.execute()) { + ReactorControllerBlockEntity controller = MultiblockHelpers.getControllerForPart(level, worldPosition); + if (controller != null) { + BlockPos controllerPos = controller.getBlockPos(); + if (delegate.getFluidInTank(0).isEmpty()) { + if (level instanceof ServerLevel serverLevel) { + PersistentFluidLocks.get(serverLevel).clearLock(controllerPos); + } else { + FluidLockManager.clearLock(controllerPos); + } + } + } + } + return drained; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInputGenerator.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInputGenerator.java new file mode 100644 index 0000000..e80c5fd --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/ReactorFluidInputGenerator.java @@ -0,0 +1,34 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.fluid; + +import com.simibubi.create.foundation.data.SpecialBlockStateGen; +import com.tterrag.registrate.providers.DataGenContext; +import com.tterrag.registrate.providers.RegistrateBlockstateProvider; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.client.model.generators.ModelFile; + +public class ReactorFluidInputGenerator extends SpecialBlockStateGen { + @Override + protected int getXRotation(BlockState state) { + return state.getValue(ReactorFluidInput.FACING) == Direction.DOWN ? 180 : 0; + } + + @Override + protected int getYRotation(BlockState state) { + return state.getValue(ReactorFluidInput.FACING).getAxis().isVertical() + ? 0 + : horizontalAngle(state.getValue(ReactorFluidInput.FACING)); + } + + @Override + public ModelFile getModel(DataGenContext ctx, RegistrateBlockstateProvider prov, BlockState state) { + return prov + .models() + .getExistingFile(prov + .modLoc("block/reactor/fluid_input/fluid_input" + (state.getValue(ReactorFluidInput.FACING).getAxis().isVertical() + ? "_vertical" + : "" + ))); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/VirtualReactorInputFluid.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/VirtualReactorInputFluid.java new file mode 100644 index 0000000..aea43a8 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/fluid/VirtualReactorInputFluid.java @@ -0,0 +1,92 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.fluid; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; +import net.minecraft.core.registries.BuiltInRegistries; +import net.nuclearteam.createnuclear.content.logistics.BigFluidStack; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * A lightweight, in-memory aggregation of fluid quantities keyed by + * fluid ResourceLocation. This record is used to represent the combined + * input fluids available to a reactor without modifying the real handlers. + */ +public record VirtualReactorInputFluid(Map fluids) { + + /** + * Create an empty virtual inventory. + */ + public VirtualReactorInputFluid() { + this(new HashMap<>()); + } + + /** + * Add the contents of the provided FluidStack to this virtual inventory. + * Empty stacks or non-registered fluids are ignored. + * @param stack the fluid stack to add + */ + public void addFluid(@NotNull FluidStack stack) { + if (stack.isEmpty() || stack.getAmount() <= 0) return; + ResourceLocation id = BuiltInRegistries.FLUID.getKey(stack.getFluid()); + if (id == null) return; + fluids.merge(id, (long) stack.getAmount(), Long::sum); + } + + /** + * Remove up to {@code amount} units of the specified fluid from this virtual inventory. + * Returns a FluidStack describing the removed amount (or {@link FluidStack#EMPTY}). + * @param fluidId fluid identifier + * @param amount maximum amount to remove + */ + public FluidStack removeFluid(@NotNull ResourceLocation fluidId, long amount) { + if (amount < 0 || fluidId == null) return FluidStack.EMPTY; + long current = fluids.getOrDefault(fluidId, 0L); + long removed = Math.min(current, amount); + if (removed == 0) return FluidStack.EMPTY; + long remaining = current - removed; + if (remaining == 0) fluids.remove(fluidId); + else fluids.put(fluidId, remaining); + + int removedInt = (int) Math.min(removed, Integer.MAX_VALUE); + return new FluidStack(BuiltInRegistries.FLUID.get(fluidId), removedInt); + } + + /** + * Get the stored amount for the given fluid id. + * @param fluidId fluid identifier + * @return total amount stored for that fluid + */ + public long getAmount(@NotNull ResourceLocation fluidId) { + if (fluidId == null) return 0L; + return fluids.getOrDefault(fluidId, 0L); + } + + /** + * Convert a map of ResourceLocation->long into a list of BigFluidStack + * suitable for display or consumption elsewhere. + */ + public static List toBigList(Map map) { + List list = new ArrayList<>(); + for (Entry e : map.entrySet()) { + ResourceLocation id = e.getKey(); + long total = e.getValue(); + Fluid fluid = BuiltInRegistries.FLUID.get(id); + if (fluid == null || total <= 0) continue; + int amount = (int) Math.min(total, BigFluidStack.INF); + list.add(new BigFluidStack(new FluidStack(fluid, amount), amount)); + } + return list; + } + + @Override + public @NotNull String toString() { + return "VirtualReactorInputFluid" + fluids.toString(); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInput.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInput.java similarity index 84% rename from src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInput.java rename to src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInput.java index afe2fd2..3f0a4ec 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInput.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInput.java @@ -1,14 +1,11 @@ -package net.nuclearteam.createnuclear.content.multiblock.input; +package net.nuclearteam.createnuclear.content.multiblock.input.item; import com.simibubi.create.content.equipment.wrench.IWrenchable; import com.simibubi.create.foundation.block.IBE; import com.simibubi.create.foundation.item.ItemHelper; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; @@ -28,17 +25,19 @@ import net.nuclearteam.createnuclear.CNBlockEntityTypes; import net.nuclearteam.createnuclear.CNBlocks; import net.nuclearteam.createnuclear.CNShapes; +import net.nuclearteam.createnuclear.content.multiblock.MultiblockHelpers; import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlock; import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; import net.nuclearteam.createnuclear.foundation.block.HorizontalDirectionalReactorBlock; +import net.nuclearteam.createnuclear.foundation.block.MultiDirectionalReactorBlock; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.util.List; -public class ReactorInput extends HorizontalDirectionalReactorBlock implements IWrenchable, IBE { +public class ReactorRodInput extends MultiDirectionalReactorBlock implements IWrenchable, IBE { - public ReactorInput(Properties properties) { + public ReactorRodInput(Properties properties) { super(properties); } @@ -66,8 +65,13 @@ public ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level @Override public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { super.onPlace(state, level, pos, oldState, isMoving); - List players = level.players(); - FindController(pos, level, players, true); + MultiblockHelpers.handleOnPlace(pos, level, ReactorControllerBlockEntity::addInput); + } + + @Override + public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity pPlacer, ItemStack stack) { + super.setPlacedBy(level, pos, state, pPlacer, stack); + MultiblockHelpers.handleAdvancedPlacedBy(pos, level, pPlacer); } // @Override // ! may be useless @@ -123,20 +127,21 @@ public ReactorControllerBlock FindController(BlockPos blockPos, Level level, Lis @Override public BlockState getStateForPlacement(BlockPlaceContext context) { return this.defaultBlockState() - .setValue(FACING, context.getHorizontalDirection().getOpposite()); + .setValue(FACING, context.getNearestLookingDirection().getOpposite()); } + @Override public @NotNull VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { return CNShapes.REACTOR_INPUT.get(state.getValue(FACING)); } @Override - public Class getBlockEntityClass() { - return ReactorInputEntity.class; + public Class getBlockEntityClass() { + return ReactorRodInputEntity.class; } @Override - public BlockEntityType getBlockEntityType() { + public BlockEntityType getBlockEntityType() { return CNBlockEntityTypes.REACTOR_INPUT.get(); } } \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputEntity.java similarity index 90% rename from src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputEntity.java rename to src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputEntity.java index 5e69944..671ede8 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputEntity.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputEntity.java @@ -1,4 +1,4 @@ -package net.nuclearteam.createnuclear.content.multiblock.input; +package net.nuclearteam.createnuclear.content.multiblock.input.item; import com.simibubi.create.foundation.blockEntity.SmartBlockEntity; import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; @@ -24,15 +24,15 @@ import static net.nuclearteam.createnuclear.content.multiblock.CNMultiblock.*; -public class ReactorInputEntity extends SmartBlockEntity implements MenuProvider { +public class ReactorRodInputEntity extends SmartBlockEntity implements MenuProvider { protected BlockPos block; - public ReactorInputInventory inventory; + public ReactorRodInputInventory inventory; - public ReactorInputEntity(BlockEntityType type, BlockPos pos, BlockState state) { + public ReactorRodInputEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); - inventory = new ReactorInputInventory(this); + inventory = new ReactorRodInputInventory(this); } public static void registerCapabilities(RegisterCapabilitiesEvent event) { @@ -85,7 +85,7 @@ private static BlockPos FindController(char character) { .where('D', a -> a.getState().is(CNBlocks.REACTOR_COOLER.get())) .where('*', a -> a.getState().is(CNBlocks.REACTOR_CONTROLLER.get())) .where('O', a -> a.getState().is(CNBlocks.REACTOR_OUTPUT.get())) - .where('I', a -> a.getState().is(CNBlocks.REACTOR_INPUT.get())) + .where('I', a -> a.getState().is(CNBlocks.REACTOR_ROD_INPUT.get())) .getDistanceController(character); } @@ -97,7 +97,7 @@ public Component getDisplayName() { @Nullable @Override public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { - return ReactorInputMenu.create(i, inventory, this); + return ReactorRodInputMenu.create(i, inventory, this); } @Override diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputGenerator.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputGenerator.java new file mode 100644 index 0000000..b038c38 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputGenerator.java @@ -0,0 +1,34 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.item; + +import com.simibubi.create.foundation.data.SpecialBlockStateGen; +import com.tterrag.registrate.providers.DataGenContext; +import com.tterrag.registrate.providers.RegistrateBlockstateProvider; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.client.model.generators.ModelFile; + +public class ReactorRodInputGenerator extends SpecialBlockStateGen { + @Override + protected int getXRotation(BlockState state) { + return state.getValue(ReactorRodInput.FACING) == Direction.DOWN ? 180 : 0; + } + + @Override + protected int getYRotation(BlockState state) { + return state.getValue(ReactorRodInput.FACING).getAxis().isVertical() + ? 0 + : horizontalAngle(state.getValue(ReactorRodInput.FACING)); + } + + @Override + public ModelFile getModel(DataGenContext ctx, RegistrateBlockstateProvider prov, BlockState state) { + return prov + .models() + .getExistingFile(prov + .modLoc("block/reactor/rod_input/rod_input" + (state.getValue(ReactorRodInput.FACING).getAxis().isVertical() + ? "_vertical" + : "" + ))); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputInventory.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputInventory.java similarity index 73% rename from src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputInventory.java rename to src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputInventory.java index dce2c1a..351dfa8 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputInventory.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputInventory.java @@ -1,4 +1,4 @@ -package net.nuclearteam.createnuclear.content.multiblock.input; +package net.nuclearteam.createnuclear.content.multiblock.input.item; import net.minecraft.world.item.ItemStack; @@ -6,10 +6,10 @@ import net.nuclearteam.createnuclear.CNItems; import org.jetbrains.annotations.NotNull; -public class ReactorInputInventory extends ItemStackHandler { - private final ReactorInputEntity be; +public class ReactorRodInputInventory extends ItemStackHandler { + private final ReactorRodInputEntity be; - public ReactorInputInventory(ReactorInputEntity be) { + public ReactorRodInputInventory(ReactorRodInputEntity be) { super(2); this.be = be; } diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputMenu.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputMenu.java similarity index 75% rename from src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputMenu.java rename to src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputMenu.java index f042457..bcca196 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputMenu.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputMenu.java @@ -1,10 +1,8 @@ -package net.nuclearteam.createnuclear.content.multiblock.input; +package net.nuclearteam.createnuclear.content.multiblock.input.item; import com.simibubi.create.foundation.gui.menu.MenuBase; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -16,19 +14,19 @@ import net.neoforged.neoforge.items.SlotItemHandler; import net.nuclearteam.createnuclear.CNMenus; -public class ReactorInputMenu extends MenuBase { +public class ReactorRodInputMenu extends MenuBase { - public ReactorInputMenu(MenuType type, int id, Inventory inv, RegistryFriendlyByteBuf extraData) { + public ReactorRodInputMenu(MenuType type, int id, Inventory inv, RegistryFriendlyByteBuf extraData) { super(type, id, inv, extraData); } - public ReactorInputMenu(MenuType type, int id, Inventory inv, ReactorInputEntity contentHolder) { + public ReactorRodInputMenu(MenuType type, int id, Inventory inv, ReactorRodInputEntity contentHolder) { super(type, id, inv, contentHolder); } - public static ReactorInputMenu create(int id, Inventory inv, ReactorInputEntity contentHolder) { - return new ReactorInputMenu(CNMenus.SLOT_ITEM_STORAGE.get(), id, inv, contentHolder); + public static ReactorRodInputMenu create(int id, Inventory inv, ReactorRodInputEntity contentHolder) { + return new ReactorRodInputMenu(CNMenus.SLOT_ITEM_STORAGE.get(), id, inv, contentHolder); } @Override @@ -44,11 +42,11 @@ public ItemStack quickMoveStack(Player player, int index) { @Override - protected ReactorInputEntity createOnClient(RegistryFriendlyByteBuf extraData) { + protected ReactorRodInputEntity createOnClient(RegistryFriendlyByteBuf extraData) { ClientLevel world = Minecraft.getInstance().level; BlockEntity blockEntity = world.getBlockEntity(extraData.readBlockPos()); - if (blockEntity instanceof ReactorInputEntity reactorInput) { + if (blockEntity instanceof ReactorRodInputEntity reactorInput) { reactorInput.readClient(extraData.readNbt(), extraData.registryAccess()); return reactorInput; } @@ -56,7 +54,7 @@ protected ReactorInputEntity createOnClient(RegistryFriendlyByteBuf extraData) { } @Override - protected void initAndReadInventory(ReactorInputEntity contentHolder) { + protected void initAndReadInventory(ReactorRodInputEntity contentHolder) { } @@ -83,7 +81,7 @@ protected void addSlots() { } @Override - protected void saveData(ReactorInputEntity contentHolder) { + protected void saveData(ReactorRodInputEntity contentHolder) { } @Override diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputScreen.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputScreen.java similarity index 81% rename from src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputScreen.java rename to src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputScreen.java index 9bd900f..5b6621d 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/ReactorInputScreen.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/ReactorRodInputScreen.java @@ -1,4 +1,4 @@ -package net.nuclearteam.createnuclear.content.multiblock.input; +package net.nuclearteam.createnuclear.content.multiblock.input.item; import com.simibubi.create.foundation.gui.AllGuiTextures; import com.simibubi.create.foundation.gui.menu.AbstractSimiContainerScreen; @@ -9,11 +9,11 @@ import static com.simibubi.create.foundation.gui.AllGuiTextures.PLAYER_INVENTORY; -public class ReactorInputScreen extends AbstractSimiContainerScreen { +public class ReactorRodInputScreen extends AbstractSimiContainerScreen { protected static final CNGuiTextures background = CNGuiTextures.REACTOR_SLOT_INVENTOR; - public ReactorInputScreen(ReactorInputMenu container, Inventory inv, Component title) { + public ReactorRodInputScreen(ReactorRodInputMenu container, Inventory inv, Component title) { super(container, inv, title); } diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/VirtualReactorInputsItem.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/VirtualReactorInputsItem.java new file mode 100644 index 0000000..5964ff3 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/input/item/VirtualReactorInputsItem.java @@ -0,0 +1,25 @@ +package net.nuclearteam.createnuclear.content.multiblock.input.item; + +import com.simibubi.create.content.logistics.BigItemStack; +import net.nuclearteam.createnuclear.CNItems; +import org.jetbrains.annotations.NotNull; + +public record VirtualReactorInputsItem(int fuel, int cooler) { + public VirtualReactorInputsItem() { + this(0,0); + } + + public BigItemStack getBigFuelRod() { + return new BigItemStack(CNItems.URANIUM_ROD.asStack(), fuel); + } + + public BigItemStack getBigCooledRod() { + return new BigItemStack(CNItems.GRAPHITE_ROD.asStack(), cooler); + } + + @Override + public @NotNull String toString() { + return "fuel: " + fuel + " cooler: " + cooler; + } +} + diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/output/ReactorOutputEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/output/ReactorOutputEntity.java index 8e0a4c3..69fc051 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/output/ReactorOutputEntity.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/output/ReactorOutputEntity.java @@ -9,6 +9,7 @@ import com.simibubi.create.foundation.utility.CreateLang; import dev.engine_room.flywheel.lib.transform.TransformStack; import net.createmod.catnip.math.AngleHelper; +import net.createmod.catnip.math.VecHelper; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -146,7 +147,7 @@ class ReactorOutputValue extends ValueBoxTransform.Sided { @Override protected Vec3 getSouthLocation() { - return net.createmod.catnip.math.VecHelper.voxelSpace(8, 8, 12.5); + return VecHelper.voxelSpace(8, 8, 12.5); } @Override diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/pattern/ReactorPattern.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/pattern/ReactorPattern.java new file mode 100644 index 0000000..2dd9510 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/pattern/ReactorPattern.java @@ -0,0 +1,140 @@ +package net.nuclearteam.createnuclear.content.multiblock.pattern; + +import lib.multiblock.SimpleMultiBlockAislePatternBuilder; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.pattern.BlockInWorld; +import net.nuclearteam.createnuclear.CNBlocks; +import net.nuclearteam.createnuclear.content.multiblock.ReactorAssembler; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; + +import java.util.function.Predicate; + +public class ReactorPattern { + //public BlockPos VerifyPattern(char character) {} + + private static final Predicate blockInWorldAPredicate = state -> + stateIs(CNBlocks.REACTOR_CASING.get()).test(state) + || stateIs(CNBlocks.REACTOR_OUTPUT.get()).test(state) + || stateIs(CNBlocks.REACTOR_ROD_INPUT.get()).test(state) + || stateIs(CNBlocks.REACTOR_FLUID_INPUT.get()).test(state) + //|| stateIs(CNBlocks.REACTOR_ALARM.get()).test(state) + ; + + private static Predicate stateIs(Block block) { + return a -> { + BlockState state = a.getState(); + return state != null && state.is(block); + }; + } + + public BlockPos VerifyPattern5x5(char character) { + return SimpleMultiBlockAislePatternBuilder.start() + .aisle("OOOOO", "OAAAO", "OAAAO", "OAAAO", "OOOOO") + .aisle("OABAO", "ODDDO", "BDCDB", "ODDDO", "OABAO") + .aisle("OABAO", "ODDDO", "BDCDB", "ODDDO", "OABAO") + .aisle("OABAO", "ODDDO", "BDCDB", "ODDDO", "OA*AO") + .aisle("OABAO", "ODDDO", "BDCDB", "ODDDO", "OABAO") + .aisle("OABAO", "ODDDO", "BDCDB", "ODDDO", "OABAO") + .aisle("OOOOO", "OAAAO", "OAAAO", "OAAAO", "OOOOO") + .where('A', blockInWorldAPredicate) + .where('B', a -> a.getState().is(CNBlocks.REACTOR_FRAME.get())) + .where('C', a -> a.getState().is(CNBlocks.REACTOR_CORE.get())) + .where('D', a -> a.getState().is(CNBlocks.REACTOR_COOLER.get())) + .where('*', a -> a.getState().is(CNBlocks.REACTOR_CONTROLLER.get())) + .where('O', a -> a.getState().is(CNBlocks.REACTOR_CASING.get())) + .getDistanceController(character); + } + + public BlockPos VerifyPattern7x7(char character) { + return SimpleMultiBlockAislePatternBuilder.start() + .aisle("OOOOOOO", "OAAAAAO", "OAAAAAO", "OAAAAAO", "OAAAAAO", "OAAAAAO", "OOOOOOO") + .aisle("OABABAO", "ADDDDDA", "BDCDCDB", "ADDDDDA", "BDCDCDB", "ADDDDDA", "OABABAO") + .aisle("OABABAO", "ADDDDDA", "BDCDCDB", "ADDDDDA", "BDCDCDB", "ADDDDDA", "OABABAO") + .aisle("OABABAO", "ADDDDDA", "BDCDCDB", "ADDDDDA", "BDCDCDB", "ADDDDDA", "OABABAO") + .aisle("OABABAO", "ADDDDDA", "BDCDCDB", "ADDDDDA", "BDCDCDB", "ADDDDDA", "OAB*BAO") + .aisle("OABABAO", "ADDDDDA", "BDCDCDB", "ADDDDDA", "BDCDCDB", "ADDDDDA", "OABABAO") + .aisle("OABABAO", "ADDDDDA", "BDCDCDB", "ADDDDDA", "BDCDCDB", "ADDDDDA", "OABABAO") + .aisle("OABABAO", "ADDDDDA", "BDCDCDB", "ADDDDDA", "BDCDCDB", "ADDDDDA", "OABABAO") + .aisle("OOOOOOO", "OAAAAAO", "OAAAAAO", "OAAAAAO", "OAAAAAO", "OAAAAAO", "OOOOOOO") + .where('A', blockInWorldAPredicate) + .where('B', a -> a.getState().is(CNBlocks.REACTOR_FRAME.get())) + .where('C', a -> a.getState().is(CNBlocks.REACTOR_CORE.get())) + .where('D', a -> a.getState().is(CNBlocks.REACTOR_COOLER.get())) + .where('*', a -> a.getState().is(CNBlocks.REACTOR_CONTROLLER.get())) + .where('O', a -> a.getState().is(CNBlocks.REACTOR_CASING.get())) + .getDistanceController(character); + } + + public BlockPos VerifyPattern9x9(char character) { + return SimpleMultiBlockAislePatternBuilder.start() + .aisle("OOOOOOOOO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OOOOOOOOO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAA*AABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OBAABAABO", "BDDDDDDDB", "ADCDCDCDA", "ADDDDDDDA", "BDCDCDCDB", "ADDDDDDDA", "ADCDCDCDA", "BDDDDDDDB", "OBAABAABO") + .aisle("OOOOOOOOO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OAAAAAAAO", "OOOOOOOOO") + .where('A', blockInWorldAPredicate) + .where('B', a -> a.getState().is(CNBlocks.REACTOR_FRAME.get())) + .where('C', a -> a.getState().is(CNBlocks.REACTOR_CORE.get())) + .where('D', a -> a.getState().is(CNBlocks.REACTOR_COOLER.get())) + .where('*', a -> a.getState().is(CNBlocks.REACTOR_CONTROLLER.get())) + .where('O', a -> a.getState().is(CNBlocks.REACTOR_CASING.get())) + .getDistanceController(character); + } + + public void findController(BlockPos blockPos, Level level, boolean first){ + BlockPos newBlock; + Vec3i pos = new Vec3i(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + for (int y = pos.getY()-5; y != pos.getY()+6; y+=1) { + for (int x = pos.getX()-9; x != pos.getX()+10; x+=1) { + for (int z = pos.getZ()-9; z != pos.getZ()+10; z+=1) { + newBlock = new BlockPos(x, y, z); + if (level.getBlockState(newBlock).is(CNBlocks.REACTOR_CONTROLLER.get())) { + if (first) { + ReactorAssembler.assemble(newBlock, level); + } else { + ReactorAssembler.disassemble(newBlock, level); + } + } + } + } + } + } + + public BlockPos findControllerPos(BlockPos blockPos, Level level){ + BlockPos newBlock; + Vec3i pos = new Vec3i(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + for (int y = pos.getY()-5; y != pos.getY()+6; y+=1) { + for (int x = pos.getX()-9; x != pos.getX()+10; x+=1) { + for (int z = pos.getZ()-9; z != pos.getZ()+10; z+=1) { + newBlock = new BlockPos(x, y, z); + if (level.getBlockState(newBlock).is(CNBlocks.REACTOR_CONTROLLER.get())) { + if (level.getBlockEntity(newBlock) instanceof ReactorControllerBlockEntity entity) { + if (isInReactorRange(entity.getMultiblockPos(), blockPos)) { + return newBlock; + } + } + } + } + } + } + return null; + } + + public boolean isInReactorRange(int []reactorPos, BlockPos blockPos) { + //[xMin, xMax, yMin, yMax, zMin, zMax] + if (reactorPos == null || reactorPos.length < 6) return false; + return blockPos.getX() >= reactorPos[0] && blockPos.getX() <= reactorPos[1] + && blockPos.getY() >= reactorPos[2] && blockPos.getY() <= reactorPos[3] + && blockPos.getZ() >= reactorPos[4] && blockPos.getZ() <= reactorPos[5]; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/particles/IrradiatedParticles.java b/src/main/java/net/nuclearteam/createnuclear/content/particles/IrradiatedParticles.java new file mode 100644 index 0000000..a3f2a58 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/particles/IrradiatedParticles.java @@ -0,0 +1,50 @@ +package net.nuclearteam.createnuclear.content.particles; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.*; +import net.minecraft.util.RandomSource; + +import javax.annotation.Nullable; + +public class IrradiatedParticles extends TextureSheetParticle { + protected IrradiatedParticles(ClientLevel pLevel, double pX, double pY, double pZ, + SpriteSet spriteSet, double pXSpeed, double pYSpeed, double pZSpeed) { + super(pLevel, pX, pY, pZ, pXSpeed, pYSpeed, pZSpeed); + + RandomSource randomSource = level.random; + + this.xo = randomSource.nextGaussian() * (double)1.0E-6f; + this.yo = randomSource.nextGaussian() * (double)1.0E-4f; + this.zo = randomSource.nextGaussian() * (double)1.0E-6f; + + this.quadSize *= this.random.nextFloat() * 0.6f + 0.6f; + this.hasPhysics = false; + this.gravity = 0.0f; + + this.friction = 0.8f; + this.lifetime = 40; + + this.setSpriteFromAge(spriteSet); + + } + + @Override + public ParticleRenderType getRenderType() { + return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; + } + + public static class Provider implements ParticleProvider { + private final SpriteSet spriteSet; + + public Provider(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + @Nullable + @Override + public Particle createParticle(IrradiatedParticlesData pType, ClientLevel pLevel, double pX, double pY, double pZ, + double pXSpeed, double pYSpeed, double pZSpeed) { + return new IrradiatedParticles(pLevel, pX, pY, pZ, this.spriteSet, pXSpeed, pYSpeed, pZSpeed); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/particles/IrradiatedParticlesData.java b/src/main/java/net/nuclearteam/createnuclear/content/particles/IrradiatedParticlesData.java new file mode 100644 index 0000000..22fcb05 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/particles/IrradiatedParticlesData.java @@ -0,0 +1,68 @@ +package net.nuclearteam.createnuclear.content.particles; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.simibubi.create.foundation.particle.ICustomParticleDataWithSprite; +import net.minecraft.client.particle.ParticleEngine.SpriteParticleRegistration; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.nuclearteam.createnuclear.CNParticleTypes; + +import java.util.Locale; + +public class IrradiatedParticlesData implements ParticleOptions, ICustomParticleDataWithSprite { + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> + i.group( + Codec.INT.optionalFieldOf("t", 0).forGetter(p -> p.t) + ) + .apply(i, IrradiatedParticlesData::new) + ); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, + p -> p.t, + IrradiatedParticlesData::new + ); + + int t; + + public IrradiatedParticlesData(int _t) { + t = _t; + } + + public IrradiatedParticlesData() { + this(0); + } + + @Override + public ParticleType getType() { + return CNParticleTypes.IRRADIATED_PARTICLES.get(); + } + + public void writeToNetwork(FriendlyByteBuf buffer) { + buffer.writeInt(t); + } + + public String writeToString() { + return String.format(Locale.ROOT, "%s %d", CNParticleTypes.IRRADIATED_PARTICLES.parameter(), t); + } + + public MapCodec getCodec(ParticleType type) { + return CODEC; + } + + public StreamCodec getStreamCodec() { + return STREAM_CODEC; + } + + @Override + public SpriteParticleRegistration getMetaFactory() { + return IrradiatedParticles.Provider::new; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/particles/NuclearMushroomCloudModel.java b/src/main/java/net/nuclearteam/createnuclear/content/particles/NuclearMushroomCloudModel.java new file mode 100644 index 0000000..dbc75c7 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/particles/NuclearMushroomCloudModel.java @@ -0,0 +1,213 @@ +package net.nuclearteam.createnuclear.content.particles; + +import com.google.common.collect.ImmutableList; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.nuclearteam.createnuclear.content.explosion.CNAdvancedEntityModel; +import net.nuclearteam.createnuclear.content.explosion.CNAdvancedModelBox; +import net.nuclearteam.createnuclear.content.explosion.CNBasicModelPart; + +public class NuclearMushroomCloudModel extends CNAdvancedEntityModel { + private final CNAdvancedModelBox mushroom_cloud; + private final CNAdvancedModelBox lightBall; + private final CNAdvancedModelBox lowerCloud; + private final CNAdvancedModelBox lowerCloud_planes; + private final CNAdvancedModelBox cube_r1; + private final CNAdvancedModelBox cube_r2; + private final CNAdvancedModelBox cube_r3; + private final CNAdvancedModelBox cube_r4; + private final CNAdvancedModelBox plume; + private final CNAdvancedModelBox cube_r5; + private final CNAdvancedModelBox cube_r6; + private final CNAdvancedModelBox plumeBlast; + private final CNAdvancedModelBox plumeRing; + private final CNAdvancedModelBox upperCloud; + private final CNAdvancedModelBox upperCloud1; + private final CNAdvancedModelBox upperCloud_planes; + private final CNAdvancedModelBox cube_r7; + private final CNAdvancedModelBox cube_r8; + private final CNAdvancedModelBox cube_r9; + private final CNAdvancedModelBox cube_r10; + private final CNAdvancedModelBox rings; + private final CNAdvancedModelBox upperRing; + private final CNAdvancedModelBox lowerRing; + + public NuclearMushroomCloudModel() { + texWidth = 512; + texHeight = 512; + + mushroom_cloud = new CNAdvancedModelBox(this); + mushroom_cloud.setRotationPoint(0.0F, 24.0F, 0.0F); + + + lightBall = new CNAdvancedModelBox(this); + lightBall.setRotationPoint(0.0F, -19.0F, 0.0F); + mushroom_cloud.addChild(lightBall); + lightBall.setTextureOffset(300, 0).addBox(-16.0F, -16.0F, -16.0F, 32.0F, 32.0F, 32.0F, 0.0F, false); + + lowerCloud = new CNAdvancedModelBox(this); + lowerCloud.setRotationPoint(0.0F, 5.0F, 0.0F); + lightBall.addChild(lowerCloud); + lowerCloud.setTextureOffset(0, 0).addBox(-50.0F, -9.0F, -50.0F, 100.0F, 16.0F, 100.0F, 0.0F, false); + + lowerCloud_planes = new CNAdvancedModelBox(this); + lowerCloud_planes.setRotationPoint(0.0F, 0.0F, 0.0F); + lowerCloud.addChild(lowerCloud_planes); + + + cube_r1 = new CNAdvancedModelBox(this); + cube_r1.setRotationPoint(-16.0F, -20.0F, 16.0F); + lowerCloud_planes.addChild(cube_r1); + setRotateAngle(cube_r1, 0.0F, 0.7854F, 0.0F); + cube_r1.setTextureOffset(142, 342).addBox(-66.0F, -13.0F, 0.0F, 66.0F, 40.0F, 0.0F, 0.0F, true); + + cube_r2 = new CNAdvancedModelBox(this); + cube_r2.setRotationPoint(16.0F, -20.0F, -16.0F); + lowerCloud_planes.addChild(cube_r2); + setRotateAngle(cube_r2, 0.0F, 0.7854F, 0.0F); + cube_r2.setTextureOffset(142, 342).addBox(0.0F, -13.0F, 0.0F, 66.0F, 40.0F, 0.0F, 0.0F, false); + + cube_r3 = new CNAdvancedModelBox(this); + cube_r3.setRotationPoint(16.0F, -20.0F, 16.0F); + lowerCloud_planes.addChild(cube_r3); + setRotateAngle(cube_r3, 0.0F, -0.7854F, 0.0F); + cube_r3.setTextureOffset(142, 342).addBox(0.0F, -13.0F, 0.0F, 66.0F, 40.0F, 0.0F, 0.0F, false); + + cube_r4 = new CNAdvancedModelBox(this); + cube_r4.setRotationPoint(-16.0F, -20.0F, -16.0F); + lowerCloud_planes.addChild(cube_r4); + setRotateAngle(cube_r4, 0.0F, -0.7854F, 0.0F); + cube_r4.setTextureOffset(142, 342).addBox(-66.0F, -13.0F, 0.0F, 66.0F, 40.0F, 0.0F, 0.0F, true); + + plume = new CNAdvancedModelBox(this); + plume.setRotationPoint(0.0F, 9, 0.0F); + lightBall.addChild(plume); + + + cube_r5 = new CNAdvancedModelBox(this); + cube_r5.setRotationPoint(0.0F, -44.8333F, 0.0F); + plume.addChild(cube_r5); + setRotateAngle(cube_r5, 0.0F, -0.7854F, 0.0F); + cube_r5.setTextureOffset(256, 220).addBox(-32.0F, -61.0F, 0.0F, 64.0F, 122.0F, 0.0F, 0.0F, false); + + cube_r6 = new CNAdvancedModelBox(this); + cube_r6.setRotationPoint(0.0F, -44.8333F, 0.0F); + plume.addChild(cube_r6); + setRotateAngle(cube_r6, 0.0F, 0.7854F, 0.0F); + cube_r6.setTextureOffset(256, 220).addBox(-32.0F, -61.0F, 0.0F, 64.0F, 122.0F, 0.0F, 0.0F, false); + + plumeBlast = new CNAdvancedModelBox(this); + plumeBlast.setRotationPoint(0.0F, -80.8333F, 0.0F); + plume.addChild(plumeBlast); + plumeBlast.setTextureOffset(240, 116).addBox(-18.0F, 0.0F, -18.0F, 36.0F, 29.0F, 36.0F, 0.0F, false); + + plumeRing = new CNAdvancedModelBox(this); + plumeRing.setRotationPoint(0.0F, -35.8333F, 0.0F); + plume.addChild(plumeRing); + plumeRing.setTextureOffset(296, 440).addBox(-36.0F, 0.0F, -36.0F, 72.0F, 0.0F, 72.0F, 0.0F, false); + + upperCloud = new CNAdvancedModelBox(this); + upperCloud.setRotationPoint(0.0F, -80.8333F, 0.0F); + plume.addChild(upperCloud); + upperCloud.setTextureOffset(0, 116).addBox(-40.0F, -24.0F, -40.0F, 80.0F, 24.0F, 80.0F, 0.0F, false); + + upperCloud1 = new CNAdvancedModelBox(this); + upperCloud1.setRotationPoint(0.0F, -24.0F, 0.0F); + upperCloud.addChild(upperCloud1); + upperCloud1.setTextureOffset(0, 220).addBox(-32.0F, -15.0F, -32.0F, 64.0F, 15.0F, 64.0F, 0.0F, false); + + upperCloud_planes = new CNAdvancedModelBox(this); + upperCloud_planes.setRotationPoint(0.0F, -12.0F, 0.0F); + upperCloud.addChild(upperCloud_planes); + + + cube_r7 = new CNAdvancedModelBox(this); + cube_r7.setRotationPoint(16.0F, 112.0F, -16.0F); + upperCloud_planes.addChild(cube_r7); + setRotateAngle(cube_r7, 0.0F, 0.7854F, 0.0F); + cube_r7.setTextureOffset(0, 299).addBox(-23.0F, -152.0F, 0.0F, 71.0F, 69.0F, 0.0F, 0.0F, false); + + cube_r8 = new CNAdvancedModelBox(this); + cube_r8.setRotationPoint(16.0F, 112.0F, 16.0F); + upperCloud_planes.addChild(cube_r8); + setRotateAngle(cube_r8, 0.0F, -0.7854F, 0.0F); + cube_r8.setTextureOffset(0, 299).addBox(-23.0F, -152.0F, 0.0F, 71.0F, 69.0F, 0.0F, 0.0F, false); + + cube_r9 = new CNAdvancedModelBox(this); + cube_r9.setRotationPoint(-16.0F, 112.0F, 16.0F); + upperCloud_planes.addChild(cube_r9); + setRotateAngle(cube_r9, 0.0F, 0.7854F, 0.0F); + cube_r9.setTextureOffset(0, 299).addBox(-48.0F, -152.0F, 0.0F, 71.0F, 69.0F, 0.0F, 0.0F, true); + + cube_r10 = new CNAdvancedModelBox(this); + cube_r10.setRotationPoint(-16.0F, 112.0F, -16.0F); + upperCloud_planes.addChild(cube_r10); + setRotateAngle(cube_r10, 0.0F, -0.7854F, 0.0F); + cube_r10.setTextureOffset(0, 299).addBox(-48.0F, -152.0F, 0.0F, 71.0F, 69.0F, 0.0F, 0.0F, true); + + rings = new CNAdvancedModelBox(this); + rings.setRotationPoint(0.0F, -55.0F, 0.0F); + upperCloud.addChild(rings); + + + upperRing = new CNAdvancedModelBox(this); + upperRing.setRotationPoint(0.0F, -4.0F, 0.0F); + rings.addChild(upperRing); + upperRing.setTextureOffset(296, 440).addBox(-36.0F, 0.0F, -36.0F, 72.0F, 0.0F, 72.0F, 0.0F, false); + + lowerRing = new CNAdvancedModelBox(this); + lowerRing.setRotationPoint(0.0F, 4.0F, 0.0F); + rings.addChild(lowerRing); + lowerRing.setTextureOffset(296, 440).addBox(-36.0F, 0.0F, -36.0F, 72.0F, 0.0F, 72.0F, 0.0F, false); + this.updateDefaultPose(); + } + + + @Override + public Iterable parts() { + return ImmutableList.of(mushroom_cloud); + } + + @Override + public Iterable getAllParts() { + return ImmutableList.of(rings, mushroom_cloud, lowerCloud, lowerCloud_planes, lightBall, cube_r1, cube_r2, cube_r3, cube_r4, cube_r5, cube_r6, cube_r7, cube_r8, cube_r9, cube_r10, plume, plumeBlast, plumeRing, upperCloud, upperCloud_planes, upperCloud1, upperRing, lowerRing); + } + + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + this.resetToDefaultPose(); + } + + public void hideFireball(boolean fireball) { + this.plume.showModel = fireball; + this.lowerCloud.showModel = fireball; + this.upperCloud.showModel = fireball; + } + + public void animateParticle(float age, float life, float partialTicks) { + this.resetToDefaultPose(); + float lerpedAge = age + partialTicks; + float baseExpand1 = life + life * life; + float upperExpand = offset(life, 0.3F, 0.4F, 1.5F); + float plumeRingExpand = offset(life, 0.0F, 0.0F, 0.5F) * 45; + float lowerRingExpand = offset(life, 0.0F, 0.1F, 0.65F) * 20; + float upperRingExpand = offset(life, 0.0F, 0.2F, 0.65F) * 15; + this.plume.scaleChildren = true; + this.upperCloud.scaleChildren = true; + this.lowerCloud.scaleChildren = true; + this.plume.setScale(1, life * 1.5F, 1); + this.upperCloud.setScale(upperExpand, 1, upperExpand); + this.plumeRing.setScale(plumeRingExpand, 1, plumeRingExpand); + this.lowerRing.setScale(lowerRingExpand, 1, lowerRingExpand); + this.upperRing.setScale(upperRingExpand, 1, upperRingExpand); + this.lowerCloud.setScale(baseExpand1, baseExpand1, baseExpand1); + this.plumeRing.rotateAngleY += lerpedAge * 0.1; + this.lowerRing.rotateAngleY -= lerpedAge * 0.2; + this.upperRing.rotateAngleY += lerpedAge * 0.1; + } + + public float offset(float life, float forwards, float min, float max) { + return Mth.clamp(life + forwards, min, max); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/particles/NuclearMushroomCloudParticle.java b/src/main/java/net/nuclearteam/createnuclear/content/particles/NuclearMushroomCloudParticle.java new file mode 100644 index 0000000..9abc3cf --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/particles/NuclearMushroomCloudParticle.java @@ -0,0 +1,137 @@ +package net.nuclearteam.createnuclear.content.particles; + +import net.nuclearteam.createnuclear.*; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.particles.SimpleParticleType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; +import net.nuclearteam.createnuclear.content.explosion.CNNuclearExplosionSound; +import net.nuclearteam.createnuclear.foundation.utility.Maths; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; + +public class NuclearMushroomCloudParticle extends Particle { + + private static final ResourceLocation TEXTURE = CreateNuclear.asResource("textures/particle/nuclear_mushroom_cloud.png"); + private static final ResourceLocation TEXTURE_GLOW = CreateNuclear.asResource("textures/particle/nuclear_mushroom_cloud_glow.png"); + private static final ResourceLocation TEXTURE_PINK = CreateNuclear.asResource("textures/particle/nuclear_mushroom_cloud_pink.png"); + private static final ResourceLocation TEXTURE_PINK_GLOW = CreateNuclear.asResource("textures/particle/nuclear_mushroom_cloud_pink_glow.png"); + private static final NuclearMushroomCloudModel MODEL = new NuclearMushroomCloudModel(); + private static final int BALL_FOR = 10; + private static final int GLOW_FOR = 20; + private static final int FADE_SPEED = 10; + private final float scale; + + private boolean playedRinging; + private boolean playedExplosion; + private boolean playedRumble; + + private final boolean pink; + + protected NuclearMushroomCloudParticle(ClientLevel level, double x, double y, double z, float scale, boolean pink) { + super(level, x, y, z); + this.gravity = 0.0F; + this.lifetime = (int) Math.ceil(133.33F * scale); + this.scale = scale + 0.2F; + this.setSize(3.0F, 3.0F); + this.pink = pink; + } + + public boolean shouldCull() { + return false; + } + + public void tick() { + CNClientProxy.renderNukeSkyDarkFor = 70; + CNClientProxy.muteNonNukeSoundsFor = 50; + boolean large = this.scale > 2.0F; + if(age > BALL_FOR / 2 + 5){ + if(!playedExplosion){ + playedExplosion = true; + CreateNuclear.LOGGER.info("EXPLOSIOOOOOON"); + playSound(CNSoundEvents.NUCLEAR_EXPLOSION_MAIN.getMainEvent(), lifetime - 20, lifetime, 0.2F, false); + } + } + if (age < BALL_FOR) { + if (!playedRinging && CNConfigs.client().nuclearBombFlash.get()) { + playedRinging = true; + playSound(CNSoundEvents.NUCLEAR_EXPLOSION_RINGING.getMainEvent(), 100, 50, 0.05F, true); + // playSound(CNSoundEvents.NUCLEAR_EXPLOSION_SHOCKWAVE.getMainEvent(), 100, 50, 0.05F, true); + } + CNClientProxy.renderNukeFlashFor = 16; + } else if (age < lifetime - FADE_SPEED) { + float life = (float) (Math.log(1 + (age - BALL_FOR) / (float) (lifetime - BALL_FOR))) * 2F; + float explosionSpread = (12 * life + 4F) * scale; + for (int i = 0; i < (1 + random.nextInt(2)) * scale; i++) { + Vec3 from = new Vec3(level.random.nextFloat() - 0.5F, level.random.nextFloat() - 0.5F, level.random.nextFloat() - 0.5F).scale(scale * 1.4F).add(this.x, this.y, this.z); + Vec3 away = new Vec3(level.random.nextFloat() - 0.5F, level.random.nextFloat() - 0.5F, level.random.nextFloat() - 0.5F).scale(2.34F); + this.level.addParticle(CNParticleRegistry.NUCLEAR_MUSHROOM_CLOUD_SMOKE.get(), from.x, from.y, from.z, away.x, away.y, away.z); + } + for (int j = 0; j < scale * scale; j++) { + Vec3 explosionBase = new Vec3((level.random.nextFloat() - 0.5F) * explosionSpread, (-0.6F + level.random.nextFloat() * 0.5F) * explosionSpread * 0.1F, (level.random.nextFloat() - 0.5F) * explosionSpread).add(this.x, this.y, this.z); + this.level.addParticle(CNParticleRegistry.NUCLEAR_MUSHROOM_CLOUD_EXPLOSION.get(), explosionBase.x, explosionBase.y, explosionBase.z, 0, 0, 0); + } + if(age > BALL_FOR){ + if(!playedRumble){ + playedRumble = true; + + playSound(CNSoundEvents.NUCLEAR_EXPLOSION_RUMBLE_2.getMainEvent(), lifetime + 100, lifetime, 0.1F, true); + } + } + } + super.tick(); + } + + private void playSound(SoundEvent soundEvent, int duration, int fadesAt, float fadeInBy, boolean looping){ + Minecraft.getInstance().getSoundManager().queueTickingSound(new CNNuclearExplosionSound(soundEvent, this.x, this.y, this.z, duration, fadesAt, fadeInBy, looping)); + } + + public void render(VertexConsumer vertexConsumer, Camera camera, float partialTick) { + Vec3 vec3 = camera.getPosition(); + float f = (float) (Mth.lerp((double) partialTick, this.xo, this.x) - vec3.x()); + float f1 = (float) (Mth.lerp((double) partialTick, this.yo, this.y) - vec3.y()); + float f2 = (float) (Mth.lerp((double) partialTick, this.zo, this.z) - vec3.z()); + PoseStack posestack = new PoseStack(); + posestack.pushPose(); + posestack.translate(f, f1 - 0.5F, f2); + posestack.scale(-scale, -scale, scale); + MultiBufferSource.BufferSource multibuffersource$buffersource = Minecraft.getInstance().renderBuffers().bufferSource(); + MODEL.hideFireball(age >= BALL_FOR); + float life = (float) (Math.log(1 + (age - BALL_FOR + partialTick) / (lifetime - BALL_FOR))) * 2F; + float glowLife = life < 1F ? 1F - life : 0; + int left = lifetime - age; + float alpha = left <= FADE_SPEED ? left / (float) FADE_SPEED : 1.0F; + MODEL.animateParticle(age, Maths.smin(life, 1.0F, 0.5F), partialTick); + VertexConsumer baseConsumer = multibuffersource$buffersource.getBuffer(RenderType.entityTranslucent(pink ? TEXTURE_PINK : TEXTURE)); + int color = ((int)(alpha * 255.0F) << 24) | 0xFFFFFF; + MODEL.renderToBuffer(posestack, baseConsumer, getLightColor(partialTick), OverlayTexture.NO_OVERLAY, color); + multibuffersource$buffersource.endBatch(); + posestack.popPose(); + } + + @Override + public ParticleRenderType getRenderType() { + return ParticleRenderType.CUSTOM; + } + + public static class Factory implements ParticleProvider { + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + if (xSpeed == 0.0) { + xSpeed = 1.0F; + } + return new NuclearMushroomCloudParticle(worldIn, x, y, z, (float) Math.max(0.5F, xSpeed), ySpeed >= 1.0F); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/particles/SmallNuclearExplosionParticle.java b/src/main/java/net/nuclearteam/createnuclear/content/particles/SmallNuclearExplosionParticle.java new file mode 100644 index 0000000..9318a33 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/particles/SmallNuclearExplosionParticle.java @@ -0,0 +1,280 @@ +package net.nuclearteam.createnuclear.content.particles; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.*; +import net.minecraft.core.particles.SimpleParticleType; +import net.minecraft.util.FastColor; + +public class SmallNuclearExplosionParticle extends TextureSheetParticle { + + private final SpriteSet sprites; + private boolean hasFadeColor = false; + private float fadeR; + private float fadeG; + private float fadeB; + protected SmallNuclearExplosionParticle(ClientLevel world, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, SpriteSet sprites, boolean shortLifespan, int color1) { + super(world, x, y, z, xSpeed, ySpeed, zSpeed); + this.xd = xSpeed; + this.yd = ySpeed; + this.zd = zSpeed; + this.setSize(0.5F, 0.5F); + this.quadSize = (shortLifespan ? 1 : 0.8F) + world.random.nextFloat() * 0.3F; + this.lifetime = shortLifespan ? 5 + world.random.nextInt(3) : 15 + world.random.nextInt(10); + this.friction = 0.96F; + float randCol = world.random.nextFloat() * 0.05F; + this.sprites = sprites; + this.setColor(Math.min(FastColor.ARGB32.red(color1) / 255F + randCol, 1), Math.min(1F, FastColor.ARGB32.green(color1) / 255F + randCol), Math.min(1F, FastColor.ARGB32.blue(color1) / 255F + randCol)); + } + + public void setFadeColor(int i){ + hasFadeColor = true; + this.fadeR = (float) ((i & 16711680) >> 16) / 255.0F; + this.fadeG = (float) ((i & '\uff00') >> 8) / 255.0F; + this.fadeB = (float) ((i & 255) >> 0) / 255.0F; + } + + public void tick() { + this.xo = this.x; + this.yo = this.y; + this.zo = this.z; + this.setSpriteFromAge(this.sprites); + if (this.age++ >= this.lifetime) { + this.remove(); + } else { + if(hasFadeColor){ + this.rCol += (fadeR - this.rCol) * 0.2F; + this.gCol += (fadeG - this.gCol) * 0.2F; + this.bCol += (fadeB - this.bCol) * 0.2F; + }else{ + this.rCol = this.rCol * 0.95F; + this.gCol = this.gCol * 0.95F; + this.bCol = this.bCol * 0.95F; + } + this.move(this.xd, this.yd, this.zd); + this.xd *= (double) this.friction; + this.yd *= (double) this.friction; + this.zd *= (double) this.friction; + } + } + + @Override + public ParticleRenderType getRenderType() { + return ParticleRenderType.PARTICLE_SHEET_LIT; + } + + public float getQuadSize(float scaleFactor) { + return super.getQuadSize(scaleFactor); + } + + public int getLightColor(float partialTicks) { + return 240; + } + + public static class NukeFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public NukeFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, false, 0XFFB300); + particle.setSpriteFromAge(spriteSet); + return particle; + } + } + + public static class MineFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public MineFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XFFB300); + particle.setSpriteFromAge(spriteSet); + return particle; + } + } + + public static class UnderzealotFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public UnderzealotFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, false, 0); + particle.setSpriteFromAge(spriteSet); + return particle; + } + } + + public static class RaygunFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public RaygunFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XEEEEEE); + particle.setSpriteFromAge(spriteSet); + particle.lifetime = 5 + worldIn.random.nextInt(3); + particle.scale(0.6F + worldIn.random.nextFloat() * 0.3F); + particle.setFadeColor(0X40EE40); + return particle; + } + } + + public static class BlueRaygunFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public BlueRaygunFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XEEEEEE); + particle.setSpriteFromAge(spriteSet); + particle.lifetime = 5 + worldIn.random.nextInt(5); + particle.scale(0.5F + worldIn.random.nextFloat() * 0.5F); + particle.setFadeColor(0X40EEDA); + return particle; + } + } + + public static class TremorzillaFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public TremorzillaFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XEEEEEE); + particle.setSpriteFromAge(spriteSet); + particle.lifetime = 9 + worldIn.random.nextInt(3); + particle.scale(1.0F + worldIn.random.nextFloat() * 0.9F); + particle.setFadeColor(0X9BFF3D); + return particle; + } + } + + public static class TremorzillaRetroFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public TremorzillaRetroFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XEEEEEE); + particle.setSpriteFromAge(spriteSet); + particle.lifetime = 9 + worldIn.random.nextInt(3); + particle.scale(1.0F + worldIn.random.nextFloat() * 0.9F); + particle.setFadeColor(0XE06EFF); + return particle; + } + } + + + public static class TremorzillaTectonicFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public TremorzillaTectonicFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XEEEEEE); + particle.setSpriteFromAge(spriteSet); + particle.lifetime = 9 + worldIn.random.nextInt(3); + particle.scale(1.0F + worldIn.random.nextFloat() * 0.9F); + particle.setFadeColor(0XFFD631); + return particle; + } + } + + public static class AmberFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public AmberFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, false, 0XFFDA1E); + particle.setSpriteFromAge(spriteSet); + particle.scale(0.8F); + return particle; + } + } + + public static class TotemFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public TotemFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XFF0000); + particle.setSpriteFromAge(spriteSet); + particle.lifetime = 5 + worldIn.random.nextInt(3); + particle.scale(1.2F + worldIn.random.nextFloat() * 0.3F); + particle.setFadeColor(0); + return particle; + } + } + + public static class PurpleWitchFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public PurpleWitchFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XFF69FF); + particle.setSpriteFromAge(spriteSet); + particle.scale(0.8F); + particle.setFadeColor(0XFFFFFF); + return particle; + } + } + + public static class ConversionCrucibleFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public ConversionCrucibleFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, 0.0D, 0.0D, 0.0D, spriteSet, true, FastColor.ARGB32.color(255, (int)(255F * xSpeed), (int)(255F * ySpeed), (int)(255F * zSpeed))); + particle.setSpriteFromAge(spriteSet); + particle.scale(0.8F); + particle.setFadeColor(0XFFFFFF); + return particle; + } + } + + public static class FrostmintFactory implements ParticleProvider { + private final SpriteSet spriteSet; + + public FrostmintFactory(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + public Particle createParticle(SimpleParticleType typeIn, ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + SmallNuclearExplosionParticle particle = new SmallNuclearExplosionParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, spriteSet, true, 0XFFFFFF); + particle.quadSize *= 1.6F; + particle.setSpriteFromAge(spriteSet); + particle.setFadeColor(0XE5F9FA); + return particle; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/radiation/CNRadiationValues.java b/src/main/java/net/nuclearteam/createnuclear/content/radiation/CNRadiationValues.java new file mode 100644 index 0000000..c419ec1 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/radiation/CNRadiationValues.java @@ -0,0 +1,19 @@ +package net.nuclearteam.createnuclear.content.radiation; + +import com.simibubi.create.AllItems; +import net.nuclearteam.createnuclear.api.radiation.RadiationRegistry; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.CNBiomes; + +public class CNRadiationValues { + public static void register() { + RadiationRegistry.register() + .item(AllItems.CRUSHED_URANIUM.asItem()) + .value(.5D) + .build(); + + RadiationRegistry.register() + .biome(CNBiomes.Irradiated.PLAIN) + .value(25D) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationBucketItem.java b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationBucketItem.java new file mode 100644 index 0000000..89d01e5 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationBucketItem.java @@ -0,0 +1,23 @@ +package net.nuclearteam.createnuclear.content.radiation; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.material.Fluid; +import net.nuclearteam.createnuclear.api.radiation.IRadiationSource; + +import java.util.function.Supplier; + +public class RadiationBucketItem extends BucketItem implements IRadiationSource { + private final double radiation; + + public RadiationBucketItem(Fluid fluid, Properties builder, double radiation) { + super(fluid, builder); + this.radiation = radiation; + } + + @Override + public double getRadiation(ItemStack stack, LivingEntity entity) { + return this.radiation; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/effects/RadiationEffect.java b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationEffect.java similarity index 77% rename from src/main/java/net/nuclearteam/createnuclear/content/effects/RadiationEffect.java rename to src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationEffect.java index e782a7c..fec1b43 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/effects/RadiationEffect.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationEffect.java @@ -1,15 +1,13 @@ -package net.nuclearteam.createnuclear.content.effects; +package net.nuclearteam.createnuclear.content.radiation; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectCategory; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; import net.nuclearteam.createnuclear.CNEffects; -import net.nuclearteam.createnuclear.CNPotions; import net.nuclearteam.createnuclear.CNTags; import net.nuclearteam.createnuclear.CreateNuclear; -import net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem; +import net.nuclearteam.createnuclear.content.radiation.capability.RadiationCapability; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.AttributeModifier; @@ -38,7 +36,7 @@ public RadiationEffect() { /** * Applies the radiation effect to the entity. * - Does nothing if the entity is immune via tag. - * - Skips damage if the entity wears any anti-radiation armor. + * - Skips damage if the entity is fully protected (anti-radiation armor, iodine effect, ...). * - Otherwise, applies magic damage based on the amplifier. * * @param livingEntity The affected living entity. @@ -52,19 +50,11 @@ public boolean applyEffectTick(LivingEntity livingEntity, int amplifier) { return true; } - // Check if the entity is wearing any anti-radiation armor - boolean isWearingAntiRadiationArmor = false; - for (ItemStack armor : livingEntity.getArmorSlots()) { - if (AntiRadiationArmorItem.Armor.isArmored(armor)) { - isWearingAntiRadiationArmor = true; - break; - } - } - - // If protected by armor, do not apply damage - if (isWearingAntiRadiationArmor) { - return false; - + // Full irradiated resistance (anti-radiation armor, iodine, ...) cancels the effect entirely, + // whatever its source (radiation dose, potion, explosion). + if (RadiationCapability.getRadiationResistance(livingEntity) >= 1.0) { + livingEntity.removeEffect(CNEffects.RADIATION); + return true; } // Apply radiation damage (magic type), scaled by amplifier diff --git a/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationEffectHandler.java b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationEffectHandler.java new file mode 100644 index 0000000..f0ef2ec --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationEffectHandler.java @@ -0,0 +1,25 @@ +package net.nuclearteam.createnuclear.content.radiation; + +import com.simibubi.create.api.effect.OpenPipeEffectHandler; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.neoforged.neoforge.fluids.FluidStack; +import net.nuclearteam.createnuclear.content.radiation.capability.RadiationCapability; + +import java.util.List; + +public class RadiationEffectHandler implements OpenPipeEffectHandler { + private static final double PIPE_LEAK_DOSE = 40.0D; + + @Override + public void apply(Level level, AABB area, FluidStack fluid) { + if (level.getGameTime() % 5 != 0) return; + + List entities = level.getEntitiesOfClass(LivingEntity.class, area, LivingEntity::isAffectedByPotions); + for (LivingEntity entity : entities) { + if (!RadiationCapability.canBeIrradiated(entity)) continue; + RadiationCapability.applyContagion(entity, PIPE_LEAK_DOSE, 20); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationItem.java b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationItem.java new file mode 100644 index 0000000..e560215 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/radiation/RadiationItem.java @@ -0,0 +1,21 @@ +package net.nuclearteam.createnuclear.content.radiation; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.nuclearteam.createnuclear.api.radiation.IRadiationSource; + +public class RadiationItem extends Item implements IRadiationSource { + + private final double radiation; + + public RadiationItem(Item.Properties settings, double radiation) { + super(settings); + this.radiation = radiation; + } + + @Override + public double getRadiation(ItemStack stack, LivingEntity entity) { + return this.radiation * stack.getCount(); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/radiation/capability/RadiationCapability.java b/src/main/java/net/nuclearteam/createnuclear/content/radiation/capability/RadiationCapability.java new file mode 100644 index 0000000..17a22aa --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/radiation/capability/RadiationCapability.java @@ -0,0 +1,225 @@ +package net.nuclearteam.createnuclear.content.radiation.capability; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.tick.EntityTickEvent; +import net.nuclearteam.createnuclear.*; +import net.nuclearteam.createnuclear.CNTags.CNEntityTags; +import net.nuclearteam.createnuclear.api.radiation.IRadiationSource; +import net.nuclearteam.createnuclear.api.radiation.RadiationRegistry; +import net.nuclearteam.createnuclear.foundation.utility.ConfigValueResolver; +import net.nuclearteam.createnuclear.foundation.utility.InventoryHashUtil; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; + +import java.util.*; + +@EventBusSubscriber(modid = CreateNuclear.MOD_ID) +public class RadiationCapability { + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + Codec.DOUBLE.optionalFieldOf("radiation", 0D).forGetter(RadiationCapability::getRadiation), + Codec.LONG.optionalFieldOf("hash", 0L).forGetter(RadiationCapability::getInventoryHash), + ResourceLocation.CODEC.optionalFieldOf("lastBiome").forGetter(c -> Optional.ofNullable(c.getLastBiomeLocation())), + Codec.DOUBLE.optionalFieldOf("contagionDose", 0D).forGetter(RadiationCapability::getContagionDose), + Codec.INT.optionalFieldOf("contagionTicks", 0).forGetter(RadiationCapability::getContagionTicks) + ).apply(i, RadiationCapability::create)); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.DOUBLE, RadiationCapability::getRadiation, + ByteBufCodecs.VAR_LONG, RadiationCapability::getInventoryHash, + ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC), c -> Optional.ofNullable(c.getLastBiomeLocation()), + ByteBufCodecs.DOUBLE, RadiationCapability::getContagionDose, + ByteBufCodecs.VAR_INT, RadiationCapability::getContagionTicks, + RadiationCapability::create + ); + + private double radiation; + private long inventoryHash; + private ResourceLocation lastBiomeLocation; + private double contagionDose; + private int contagionTicks; + + public double getRadiation() { return this.radiation; } + public void setRadiation(double value) { this.radiation = value; } + + public long getInventoryHash() { return this.inventoryHash; } + public void setInventoryHash(long hash) { this.inventoryHash = hash; } + + public ResourceLocation getLastBiomeLocation() { return this.lastBiomeLocation; } + public void setLastBiomeLocation(ResourceLocation location) { this.lastBiomeLocation = location; } + + public double getContagionDose() { return this.contagionDose; } + public void setContagionDose(double dose) { this.contagionDose = dose; } + + public int getContagionTicks() { return this.contagionTicks; } + public void setContagionTicks(int ticks) { this.contagionTicks = ticks; } + + private static RadiationCapability create(double radiation, long hash, Optional lastBiome, double contagionDose, int contagionTicks) { + RadiationCapability cap = new RadiationCapability(); + cap.setRadiation(radiation); + cap.setInventoryHash(hash); + cap.setLastBiomeLocation(lastBiome.orElse(null)); + cap.setContagionDose(contagionDose); + cap.setContagionTicks(contagionTicks); + + return cap; + } + + @SubscribeEvent + public static void onEntityTick(EntityTickEvent.Pre event) { + if (event.getEntity() instanceof LivingEntity living) { + tickRadiation(living); + } + } + + public static void applyContagion(LivingEntity entity, double doseValue, int durationTicks) { + RadiationCapability cap = entity.getData(CNAttachmentTypes.RADIATION); + cap.setContagionDose(doseValue); + cap.setContagionTicks(durationTicks); + } + + public static void tickRadiation(LivingEntity entity) { + Level level = entity.level(); + if (level.isClientSide) return; + + // Checked before getData: getData creates and stores the attachment on first access, so + // guarding first keeps immune/blacklisted entities from accumulating one every tick. + if (!canBeIrradiated(entity)) return; + + RadiationCapability cap = entity.getData(CNAttachmentTypes.RADIATION); + + if (entity instanceof Player player) { + long newHash = InventoryHashUtil.compute(player); + if (newHash != cap.getInventoryHash()) { + cap.setInventoryHash(newHash); + cap.setRadiation(Math.max(0, computeItemRadiation(player))); + } + player.syncData(CNAttachmentTypes.RADIATION); + } else { + cap.setRadiation(Math.max(0, computeItemRadiation(entity))); + } + + ResourceKey biomeKey = level.getBiome(entity.blockPosition()).unwrapKey().orElse(null); + ResourceLocation biomeLoc = biomeKey != null ? biomeKey.location() : null; + if (!Objects.equals(biomeLoc, cap.getLastBiomeLocation())) { + cap.setLastBiomeLocation(biomeLoc); + } + + if (cap.getContagionTicks() > 0) { + cap.setContagionTicks(cap.getContagionTicks() - 1); + } + + double contagionDose = cap.getContagionTicks() > 0 ? cap.getContagionDose() : 0; + + double totalRaw = cap.getRadiation() + getRawBiomeRadiation(biomeKey) + contagionDose; + double resistance = getRadiationResistance(entity); + double totalRadiation = totalRaw * (1.0 - resistance); + + applyEffects(entity, totalRadiation); + } + + private static double computeItemRadiation(Player player) { + double radiation = 0; + for (ItemStack stack : player.getInventory().items) { + if (stack.getItem() instanceof IRadiationSource source) + radiation += source.getRadiation(stack, player); + radiation += RadiationRegistry.getRadiation(stack, player); + } + for (ItemStack stack : player.getInventory().offhand) { + if (stack.getItem() instanceof IRadiationSource source) + radiation += source.getRadiation(stack, player); + radiation += RadiationRegistry.getRadiation(stack, player); + } + return radiation; + } + + private static double getStackRadiation(ItemStack stack, LivingEntity entity) { + double radiation = 0; + if (stack.getItem() instanceof IRadiationSource source) radiation += source.getRadiation(stack, entity); + radiation += RadiationRegistry.getRadiation(stack, entity); + return radiation; + } + + private static double computeItemRadiation(LivingEntity entity) { + double radiation = 0; + for (ItemStack stack : entity.getArmorSlots()) { + radiation += getStackRadiation(stack, entity); + } + radiation += getStackRadiation(entity.getMainHandItem(), entity); + radiation += getStackRadiation(entity.getOffhandItem(), entity); + return radiation; + } + + private static double getRawBiomeRadiation(ResourceKey biomeKey) { + if (biomeKey == null) return 0; + return RadiationRegistry.getRadiation(biomeKey, null); + } + + public static boolean canBeIrradiated(LivingEntity entity) { + if (entity.isSpectator()) return false; + if (entity.getType().is(CNEntityTags.IRRADIATED_IMMUNE.tag)) return false; + if (!CNConfigs.server().radiation.enabledItemRadiation.get()) return false; + if (getEntityBlacklist().contains(entity.getType())) return false; + return getRadiationResistance(entity) < 1.0; + } + + private static List cachedBlacklistSource; + private static Set> cachedBlacklist = Set.of(); + + private static Set> getEntityBlacklist() { + List source = CNConfigs.server().radiation.configuredLists.getEntityBlackList(); + if (source != cachedBlacklistSource) { + Set> resolved = new HashSet<>(); + ConfigValueResolver.loadValuesInSet(source, resolved, + entry -> BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.tryParse(entry))); + cachedBlacklist = resolved; + cachedBlacklistSource = source; + } + return cachedBlacklist; + } + + public static double getRadiationResistance(LivingEntity entity) { + double resistance = 0d; + AttributeInstance attribute = entity.getAttribute(CNAttributes.IRRADIATED_RESISTANCE); + if (attribute != null) resistance += attribute.getValue(); + return Mth.clamp(resistance, 0.0, 1.0); + } + + private static void applyEffects(LivingEntity entity, double radiation) { + final double radiation_desactive = 0; + if (radiation <= radiation_desactive) return; + + int amp; + if (radiation < CNConfigs.server().radiation.radiationLevel1.get()) amp = CNConfigs.server().radiation.amplifierLevel0.get(); + else if (radiation < CNConfigs.server().radiation.radiationLevel2.get()) amp = CNConfigs.server().radiation.amplifierLevel1.get(); + else if (radiation < CNConfigs.server().radiation.radiationLevel3.get()) amp = CNConfigs.server().radiation.amplifierLevel2.get(); + else amp = CNConfigs.server().radiation.amplifierLevel2.get(); + + MobEffectInstance current = entity.getEffect(CNEffects.RADIATION); + + if (current != null && current.getAmplifier() != amp) { + entity.removeEffect(CNEffects.RADIATION); + current = null; + } + + if (current == null || current.getDuration() <= 40) { + entity.addEffect(new MobEffectInstance(CNEffects.RADIATION, 100, amp, true, true)); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/redstone/displayLink/source/ReactorSummary.java b/src/main/java/net/nuclearteam/createnuclear/content/redstone/displayLink/source/ReactorSummary.java new file mode 100644 index 0000000..8079520 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/redstone/displayLink/source/ReactorSummary.java @@ -0,0 +1,106 @@ +package net.nuclearteam.createnuclear.content.redstone.displayLink.source; + +import net.minecraft.network.chat.MutableComponent; + +import java.util.ArrayList; +import java.util.List; + +/** + * Typed, immutable snapshot of a reactor controller's summary stats (status, size, + * fuel, cooler, fluid, heat), one named {@link ReactorSummaryRow} per stat. + *

+ * Replaces the previous {@code List>} representation, which + * relied on a size-based sentinel (1 element = no controller, 6 elements = full + * summary) and on fragile positional access (e.g. {@code components.get(2).get(1)} + * for the fuel value) that silently broke if the row order ever changed. + *

+ * Callers should build instances via {@link Builder}, and only call {@link #toRows()} + * at the point where Create's {@code DisplaySource} API actually requires the + * positional list format. + */ +public record ReactorSummary(ReactorSummaryRow status, + ReactorSummaryRow size, + ReactorSummaryRow fuel, + ReactorSummaryRow cooler, + ReactorSummaryRow fluid, + ReactorSummaryRow heat) { + + /** A single display row: a label component and its associated value component. */ + public record ReactorSummaryRow(MutableComponent label, MutableComponent value) { + List toPair() { + return List.of(label(), value()); + } + } + + /** + * Converts this summary to the positional {@code List>} + * format required by Create's {@code DisplaySource} API. This is the single place + * where row order matters; every other consumer should access fields by name + * (e.g. {@link #fuel()}) instead of relying on this order. + */ + public List> toRows() { + return List.of( + status().toPair(), size().toPair(), fuel().toPair(), + cooler().toPair(), fluid().toPair(), heat().toPair() + ); + } + + /** + * Fluent builder for {@link ReactorSummary}. All six rows are required; + * {@link #build()} throws {@link IllegalStateException} naming any missing row + * instead of allowing a partially-populated summary to be constructed silently. + */ + public static class Builder { + private ReactorSummaryRow status; + private ReactorSummaryRow size; + private ReactorSummaryRow fuel; + private ReactorSummaryRow cooler; + private ReactorSummaryRow fluid; + private ReactorSummaryRow heat; + + public Builder status(MutableComponent label, MutableComponent value) { + this.status = new ReactorSummaryRow(label, value); + return this; + } + + public Builder size(MutableComponent label, MutableComponent value) { + this.size = new ReactorSummaryRow(label, value); + return this; + } + + public Builder fuel(MutableComponent label, MutableComponent value) { + this.fuel = new ReactorSummaryRow(label, value); + return this; + } + + public Builder cooler(MutableComponent label, MutableComponent value) { + this.cooler = new ReactorSummaryRow(label, value); + return this; + } + + public Builder fluid(MutableComponent label, MutableComponent value) { + this.fluid = new ReactorSummaryRow(label, value); + return this; + } + + public Builder heat(MutableComponent label, MutableComponent value) { + this.heat = new ReactorSummaryRow(label, value); + return this; + } + + public ReactorSummary build() { + List missing = new ArrayList<>(); + if (status == null) missing.add("status"); + if (size == null) missing.add("size"); + if (fuel == null) missing.add("fuel"); + if (cooler == null) missing.add("cooler"); + if (fluid == null) missing.add("fluid"); + if (heat == null) missing.add("heat"); + + if (!missing.isEmpty()) + throw new IllegalStateException("Missing required ReactorSummary fields: " + String.join(", ", missing)); + + return new ReactorSummary(status, size, fuel, cooler, fluid, heat); + } + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/redstone/displayLink/source/ReactorSummaryDisplaySource.java b/src/main/java/net/nuclearteam/createnuclear/content/redstone/displayLink/source/ReactorSummaryDisplaySource.java new file mode 100644 index 0000000..22f4885 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/redstone/displayLink/source/ReactorSummaryDisplaySource.java @@ -0,0 +1,243 @@ +package net.nuclearteam.createnuclear.content.redstone.displayLink.source; + +import com.simibubi.create.api.behaviour.display.DisplaySource; +import com.simibubi.create.content.redstone.displayLink.DisplayLinkContext; +import com.simibubi.create.content.redstone.displayLink.target.DisplayTargetStats; +import com.simibubi.create.content.trains.display.FlapDisplayBlockEntity; +import com.simibubi.create.content.trains.display.FlapDisplayLayout; +import com.simibubi.create.content.trains.display.FlapDisplaySection; +import com.simibubi.create.foundation.gui.ModularGuiLineBuilder; +import net.minecraft.ChatFormatting; +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.block.entity.LecternBlockEntity; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.nuclearteam.createnuclear.api.multiblock.rods.RodType.TypeRodPredicate; +import net.nuclearteam.createnuclear.content.logistics.BigFluidStack; +import net.nuclearteam.createnuclear.content.multiblock.IHeat; +import net.nuclearteam.createnuclear.content.multiblock.MultiblockHelpers; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; +import net.nuclearteam.createnuclear.foundation.utility.CreateNuclearLang; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Display source rendering a reactor controller's summary (status, size, fuel, + * cooler, fluid, heat) as one line per stat, for both regular displays and + * flap displays. + *

+ * The underlying data is built by {@link #getReactorSummary} into a + * {@link ReactorSummary}, which is only converted to Create's positional + * {@code List>} format at the last moment via + * {@link ReactorSummary#toRows()}, right before returning it to the caller. + */ +public class ReactorSummaryDisplaySource extends DisplaySource { + + /** Fallback line shown when only a single display row is available. */ + public static final List notEnoughSpaceSingle = + List.of(CreateNuclearLang.translateDirect("display_source.reactor.not_enough_space") + .append(CreateNuclearLang.translateDirect("display_source.reactor.for_reactor_status"))); + + /** Fallback lines shown when fewer than 6 rows are available (not enough for the full summary). */ + public static final List notEnoughSpaceDouble = + List.of(CreateNuclearLang.translateDirect("display_source.reactor.not_enough_space"), + CreateNuclearLang.translateDirect("display_source.reactor.for_reactor_status")); + + /** Fallback flap display rows, mirroring {@link #notEnoughSpaceDouble} for the flap layout. */ + public static final List> notEnoughSpaceFlap = + List.of(List.of(CreateNuclearLang.translateDirect("display_source.reactor.not_enough_space")), + List.of(CreateNuclearLang.translateDirect("display_source.reactor.for_reactor_status"))); + + /** + * Provides the text lines for a regular (non-flap) display target. + * Falls back to {@link #notEnoughSpaceSingle}/{@link #notEnoughSpaceDouble} when there + * isn't enough room, and to the "no controller" message when no reactor controller + * could be resolved for this source. + */ + @Override + public List provideText(DisplayLinkContext context, DisplayTargetStats stats) { + if (stats.maxRows() < 2) return notEnoughSpaceSingle; + if (stats.maxRows() < 6) return notEnoughSpaceDouble; + + int gaugeWidth = (stats.maxColumns() >= 80) ? 10 : 6; + + Optional summary = getReactorSummary(context, gaugeWidth); + if (summary.isEmpty()) { + return List.of(CreateNuclearLang.translateDirect("display_source.reactor.no_controller")); + } + + List> components = summary.get().toRows(); + + if (context.getTargetBlockEntity() instanceof LecternBlockEntity) { + Stream componentList = components.stream().map(list -> list.stream().reduce(MutableComponent::append).orElse(EMPTY_LINE)); + return List.of(componentList.reduce((c1, c2) -> c1.append(Component.literal("\n")).append(c2)).orElse(EMPTY_LINE)); + } + + return components.stream().map(list -> list.stream().reduce(MutableComponent::append).orElse(EMPTY_LINE)).toList(); + } + + /** + * Provides the rows for a flap display target. Disables the flap display context + * (falls back to the default layout) when there isn't enough room, either because + * of the row count or because the fuel value wouldn't fit the available columns. + */ + @Override + public List> provideFlapDisplayText(DisplayLinkContext context, DisplayTargetStats stats) { + if (stats.maxRows() < 6) { + context.flapDisplayContext = Boolean.FALSE; + return notEnoughSpaceFlap; + } + + int gaugeWidth = 6; + Optional summary = getReactorSummary(context, gaugeWidth); + + if (summary.isEmpty()) { + return notEnoughSpaceFlap; + } + + ReactorSummary reactorSummary = summary.get(); + + if (stats.maxColumns() * FlapDisplaySection.MONOSPACE < 6 * FlapDisplaySection.MONOSPACE + reactorSummary.fuel() + .value().getString().length() * FlapDisplaySection.WIDE_MONOSPACE) { + context.flapDisplayContext = Boolean.FALSE; + return notEnoughSpaceFlap; + } + + return reactorSummary.toRows(); + } + + /** + * Configures the flap display layout: the default layout for the first line + * (or when the flap display context was disabled by {@link #provideFlapDisplayText}), + * otherwise a two-section "Reactor" layout (label column + value/gauge column). + */ + @Override + public void loadFlapDisplayLayout(DisplayLinkContext context, FlapDisplayBlockEntity flapDisplay, FlapDisplayLayout layout, int lineIndex) { + if (lineIndex == 0 || context.flapDisplayContext instanceof Boolean b && !b) { + if (layout.isLayout("Default")) return; + layout.loadDefault(flapDisplay.getMaxCharCount()); + return; + } + + String layoutKey = "Reactor"; + if (layout.isLayout(layoutKey)) return; + + int lw = labelWidth(); + int labelLength = (int) (lw * FlapDisplaySection.MONOSPACE); + float maxSpace = flapDisplay.getMaxCharCount(1) * FlapDisplaySection.MONOSPACE; + + FlapDisplaySection label = new FlapDisplaySection(labelLength, "alphabet", false, true); + FlapDisplaySection symbols = new FlapDisplaySection(maxSpace - labelLength, "pixel", false, false).wideFlaps(); + + layout.configure(layoutKey, List.of(label, symbols)); + } + + /** + * Resolves the reactor controller for this source and builds a {@link ReactorSummary} + * from its current state. Returns {@link Optional#empty()} when no controller is + * assigned to this display source or it has been removed, so callers can distinguish + * that case from a valid summary without relying on list size/positional access. + */ + private Optional getReactorSummary(DisplayLinkContext context, int gaugeWidth) { + ReactorControllerBlockEntity controller = MultiblockHelpers.getControllerForPart(context.level(), context.getSourcePos()); + + if (controller == null || controller.isRemoved()) { + return Optional.empty(); + } + + int mode = context.sourceConfig().getInt("display_mode"); + + int heat = (int) controller.getConfiguredPattern() + .getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY) + .copyTag() + .getDouble("heat"); + int fuel = 0; + int cooler = 0; + + /*if (controller.getDisplayState() != null && controller.getDisplayState().items() != null) { + for (Map.Entry entry : controller.getDisplayState().items().entrySet()) { + if (TypeRodPredicate.isFuel(entry.getKey().getDefaultInstance(), context.level())) { + fuel += entry.getValue(); + } else if (TypeRodPredicate.isCooled(entry.getKey().getDefaultInstance(), context.level())) { + cooler += entry.getValue(); + } + } + }*/ + + int size = controller.getMultiblockSize(); + List fluidList = controller.getBigFluidStack(); + int fluid = (fluidList != null && !fluidList.isEmpty() && fluidList.get(0) != null) ? fluidList.get(0).amount : 0; + + int lw = labelWidth(); + MutableComponent lStatus = padLabel("status", lw).append(" "); + MutableComponent lSize = padLabel("size", lw).append(" "); + MutableComponent lFuel = padLabel("fuel", lw).append(" "); + MutableComponent lCooler = padLabel("cooler", lw).append(" "); + MutableComponent lFluid = padLabel("fluid", lw).append(" "); + MutableComponent lHeat = padLabel("heat", lw).append(" "); + + return Optional.of(new ReactorSummary.Builder() + .status(lStatus, controller.isAssembled() ? + CreateNuclearLang.translateDirect("display_source.reactor.active").withStyle(ChatFormatting.GOLD) : + CreateNuclearLang.translateDirect("display_source.reactor.idle").withStyle(ChatFormatting.GRAY)) + .size(lSize, formatSize(size)) + //.fuel(lFuel, formatValue(fuel, ReactorDisplayConstants.MAX_FUEL, mode, false, ChatFormatting.GREEN, gaugeWidth)) + //.cooler(lCooler, formatValue(cooler, ReactorDisplayConstants.MAX_COOLER, mode, false, ChatFormatting.AQUA, gaugeWidth)) + //.fluid(lFluid, formatFluid(fluid, ReactorDisplayConstants.MAX_FLUID, mode, ChatFormatting.BLUE, gaugeWidth)) + //.heat(lHeat, formatValue(heat, ReactorDisplayConstants.MAX_HEAT, mode, true, IHeat.HeatLevel.of(heat, controller.getMultiblockSize()).getTextColor(), gaugeWidth)) + .build()); + } + + private MutableComponent padLabel(String key, int lw) { + return Component.literal(" ".repeat(lw - labelWidthOf(key))).append(labelOf(key)); + } + + private MutableComponent formatSize(int size) { + String key = size <= 5 ? "small" : size <= 7 ? "medium" : "large"; + return CreateNuclearLang.translateDirect("display_source.reactor.size." + key).withStyle(ChatFormatting.BLUE); + } + + private MutableComponent formatFluid(int current, int max, int mode, ChatFormatting color, int width) { + //if (mode == 0 || mode == 3) return ReactorGaugeRenderer.drawGauge(current, max, color, width); + if (mode == 2) return Component.literal((current * 100 / max) + "%").withStyle(color); + return Component.literal(String.valueOf(current)).append(" ").append(CreateNuclearLang.translateDirect("generic.unit.fluid.value")).withStyle(color); + } + + private MutableComponent formatValue(int current, int max, int mode, boolean gaugeOnNormal, ChatFormatting color, int width) { + //if (mode == 3 || (mode == 0 && gaugeOnNormal)) return ReactorGaugeRenderer.drawGauge(current, max, color, width); + if (mode == 2) return Component.literal((current * 100 / max) + "%").withStyle(color); + return Component.literal(String.valueOf(current)).withStyle(color); + } + + private int labelWidth() { + return Stream.of("status", "size", "fuel", "cooler", "fluid", "heat").mapToInt(this::labelWidthOf).max().orElse(0); + } + + private int labelWidthOf(String label) { + return labelOf(label).getString().length(); + } + + private MutableComponent labelOf(String label) { + return CreateNuclearLang.translateDirect("display_source.reactor." + label); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void initConfigurationWidgets(DisplayLinkContext context, ModularGuiLineBuilder builder, boolean isFirstLine) { + if (isFirstLine) return; + builder.addSelectionScrollInput(0, 100, (selectionScrollInput, label) -> selectionScrollInput + .forOptions(CreateNuclearLang.translatedOptions("display_source.reactor.mode", "normal", "value", "percent", "gauge")), "display_mode"); + } + + @Override + protected String getTranslationKey() { + return "reactor_summary"; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/content/rod/CNRodTypes.java b/src/main/java/net/nuclearteam/createnuclear/content/rod/CNRodTypes.java new file mode 100644 index 0000000..2b90239 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/rod/CNRodTypes.java @@ -0,0 +1,43 @@ +package net.nuclearteam.createnuclear.content.rod; + +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.resources.ResourceKey; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.api.CreateNuclearRegistries; +import net.nuclearteam.createnuclear.api.multiblock.rods.RodType; + +import static net.nuclearteam.createnuclear.api.ItemRodTypesValue.DEFAULT_ROD_TYPE; + +public class CNRodTypes { + public static final ResourceKey FALLBACK = ResourceKey.create(CreateNuclearRegistries.ROD_TYPE, CreateNuclear.asResource("fallback")); + + /** + * Static registry of rod types ({@code RodType}) for the CreateNuclear mod. + * + *

This class exposes the {@link ResourceKey}s and the {@code bootstrap} + * method invoked by the datapack system to register rod types used by the + * multiblock (for example, fuel rods and cooler rods). The examples below + * show how to create a {@code RodType} via its {@code RodType.Builder}.

+ * + *

Example — Custom Rod:

+ *
{@code
+     * register(ctx, "name", new RodType.Builder()
+     *     .baseRodHeat(int)
+     *     .proximityRodHeat(int)
+     *     .rodTimer(int)
+     *     .addItems(Item)
+     *     .fuelRodType()
+     *     .build());
+     * }
+ * + * @see RodType + * @see CreateNuclearRegistries + */ + public static void bootstrap(BootstrapContext ctx) { + register(ctx, "fallback", DEFAULT_ROD_TYPE); + } + + private static void register(BootstrapContext ctx, String name, RodType type) { + ctx.register(ResourceKey.create(CreateNuclearRegistries.ROD_TYPE, CreateNuclear.asResource(name)), type); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNAdvancement.java b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNAdvancement.java index 5ff725a..abfcd5a 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNAdvancement.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNAdvancement.java @@ -5,38 +5,44 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.advancements.Advancement; import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.CriteriaTriggers; +import net.minecraft.advancements.critereon.*; import net.minecraft.core.HolderLookup; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.DyeColor; -import net.nuclearteam.createnuclear.CNBlocks; -import net.nuclearteam.createnuclear.CNFluids; -import net.nuclearteam.createnuclear.CNItems; -import net.nuclearteam.createnuclear.CNTags; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.entity.EntityType; +import net.neoforged.neoforge.common.Tags; +import net.nuclearteam.createnuclear.*; +import net.nuclearteam.createnuclear.content.decoration.palettes.CNPaletteStoneTypes; import net.nuclearteam.createnuclear.foundation.advancement.CreateNuclearAdvancement.Builder; - import javax.annotation.ParametersAreNonnullByDefault; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.UnaryOperator; - -import static net.nuclearteam.createnuclear.foundation.advancement.CreateNuclearAdvancement.TaskType.SILENT; -import static net.nuclearteam.createnuclear.foundation.advancement.CreateNuclearAdvancement.TaskType.SECRET; +import static net.nuclearteam.createnuclear.foundation.advancement.CreateNuclearAdvancement.TaskType.*; @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault @SuppressWarnings("unused") public class CNAdvancement implements DataProvider { + private static final List PREDICATES = List.of( + ItemPredicate.Builder.item().of(CNBlocks.ENRICHED_SOUL_SOIL), + ItemPredicate.Builder.item().of(ItemTags.LOGS), + ItemPredicate.Builder.item().of(Tags.Items.RODS_WOODEN) + ); + public static final List ENTRIES = new ArrayList<>(); public static final CreateNuclearAdvancement START = null, @@ -44,176 +50,321 @@ public class CNAdvancement implements DataProvider { .title("Starting The Nuclear Journey") .description("Unlock the basics of nuclear energy and get your first uranium powder") .awardedForFree() - .special(SILENT)), + .special(SILENT) + ), + + CRAFT_ENRICHING_CAMPFIRE = create("craft_enriching_campfire", b -> b.icon(CNBlocks.ENRICHING_CAMPFIRE.asItem()) + .title("Does That Make Smoke?") + .description("Craft an Enriching Campfire") + .externalTrigger(RecipeCraftedTrigger.TriggerInstance.craftedItem(CreateNuclear.asResource("crafting/enriching_campfire"), PREDICATES)) + .after(ROOT) + ), RAW_URANIUM = create("raw_uranium", b -> b.icon(CNItems.RAW_URANIUM) .title("The Raw Power") .description("Mine uranium ore to obtain raw uranium for further processing") .after(ROOT) - .whenIconCollected()), + .whenIconCollected() + ), URANIUM_POWDER = create("uranium_powder", b -> b.icon(CNItems.URANIUM_POWDER) .title("Powdered Uranium") .description("Crush raw uranium into powder to prepare for further refining") .after(RAW_URANIUM) - .whenIconCollected()), + .whenIconCollected() + ), + + AUTOMATIC_URANIUM = create("automatic_uranium", b -> b.icon(CNItems.URANIUM_POWDER) + .title("Automating Uranium") + .description("Obtain some uranium powder using the automatic uranium processing") + .after(ROOT) + .special(SECRET) + ), URANIUM_LIQUID = create("uranium_liquid", b -> b.icon(CNFluids.URANIUM.getBucket().get()) .title("Turning Solid To Liquid") .description("Obtain some uranium liquid by mixing uranium powder") .after(URANIUM_POWDER) - .whenIconCollected()), + .whenIconCollected() + ), YELLOWCAKE = create("yellowcake", b -> b.icon(CNItems.YELLOWCAKE) .title("The Yellowcake Process") .description("Compact uranium liquid to create yellowcake") .after(URANIUM_LIQUID) - .whenIconCollected()), + .whenIconCollected() + ), + + CRUNCHY_URANIUM = create("crunchy_uranium", b -> b.icon(CNItems.YELLOWCAKE) + .title("What Did You Expect") + .description("Bro..... you did what ??") + .after(YELLOWCAKE) + .externalTrigger(ConsumeItemTrigger.TriggerInstance.usedItem(CNItems.YELLOWCAKE)) + .special(SECRET) + ), ENRICHED_YELLOWCAKE = create("enriched_yellowcake", b -> b.icon(CNItems.ENRICHED_YELLOWCAKE) .title("Enhancing Yellowcake") .description("Use a fan to enrich yellowcake and make it more powerful") .after(YELLOWCAKE) - .whenIconCollected()), + .whenIconCollected() + ), URANIUM_ROD = create("uranium_rod", b -> b.icon(CNItems.URANIUM_ROD) .title("The Power Of The Atom") .description("Create your first uranium rod using enriched yellowcake in a mechanical crafter") .after(ENRICHED_YELLOWCAKE) - .whenIconCollected()), + .whenIconCollected() + ), + + RAW_THORIUM = create("raw_thorium", b -> b.icon(CNItems.RAW_THORIUM) + .title("Thorium's Glow") + .description("Mine thorium ore to obtain raw thorium for further processing") + .after(ROOT) + .whenIconCollected() + ), + + THORIUM_DUST = create("thorium_dust", b -> b.icon(CNItems.THORIUM_DUST) + .title("Powdered Thorium") + .description("Crush raw thorium into dust to prepare for further refining") + .after(RAW_THORIUM) + .whenIconCollected() + ), + + THORIUM_LIQUID = create("thorium_liquid", b -> b.icon(CNFluids.THORIUM.getBucket().get()) + .title("Molten Thorium") + .description("Obtain some thorium liquid by mixing thorium dust") + .after(THORIUM_DUST) + .whenIconCollected() + ), + + THORIUM_INGOT = create("thorium_ingot", b -> b.icon(CNItems.THORIUM_INGOT) + .title("Compacted Thorium") + .description("Compact thorium liquid to create a thorium ingot") + .after(THORIUM_LIQUID) + .whenIconCollected() + ), + + THORIUM_ROD = create("thorium_rod", b -> b.icon(CNItems.THORIUM_ROD) + .title("What A Blue Stick") + .description("Craft a thorium rod for the first time") + .after(THORIUM_INGOT) + .whenIconCollected() + ), + + CHEMISTRY_101 = create("chemistry_101", b -> b.icon(CNItems.NITRATE) + .title("Crushing It") + .description("Mine nitrate ore or crush limestone to obtain nitrate") + .after(ROOT) + .whenIconCollected() + ), + + GETTING_CONCENTRATED = create("getting_concentrated", b -> b.icon(CNItems.NITROGEN_CONCENTRATE) + .title("Getting Concentrated") + .description("Smelt nitrate in a blast furnace to obtain nitrogen concentrate") + .after(CHEMISTRY_101) + .whenIconCollected() + ), + + CHILL_OUT = create("chill_out", b -> b.icon(CNItems.COOLED_NITROGEN_CONCENTRATE) + .title("Chill Out") + .description("Cool down nitrogen concentrate to prepare it for liquefaction") + .after(GETTING_CONCENTRATED) + .whenIconCollected() + ), + + ABSOLUTE_ZERO = create("absolute_zero", b -> b.icon(CNFluids.LIQUID_NITROGEN.getBucket().get()) + .title("Absolute Zero") + .description("Mix cooled nitrogen concentrate to obtain liquid nitrogen") + .after(CHILL_OUT) + .whenIconCollected() + ), + + CRYOGENIC_BAPTISM = create("cryogenic_baptism", b -> b.icon(CNFluids.LIQUID_NITROGEN.getBucket().get()) + .title("I can't Feel My Feet Anymore") + .description("Swim in the liquid nitrogen for the first time") + .after(ABSOLUTE_ZERO) + .special(SECRET) + ), COAL_DUST = create("coal_dust", b -> b.icon(CNItems.COAL_DUST) .title("Coal Dust") .description("Crush coal or charcoal to obtain coal dust, a key crafting material") .after(ROOT) - .whenIconCollected()), + .whenIconCollected() + ), STEEL_INGOT = create("steel_ingot", b -> b.icon(CNItems.STEEL_INGOT) .title("Steel Ingot") .description("Combine coal dust and iron ingots to create steel ingots") .after(COAL_DUST) - .whenIconCollected()), + .whenIconCollected() + ), GRAPHENE = create("graphene", b -> b.icon(CNItems.GRAPHENE) .title("Graphene") .description("Press coal dust to create graphene") .after(COAL_DUST) - .whenIconCollected()), + .whenIconCollected() + ), GRAPHITE_ROD = create("graphite_rod", b -> b.icon(CNItems.GRAPHITE_ROD) .title("Don't Forget Those Ones") .description("Combine graphene and steel ingots in a mechanical crafter to make graphite rods") .after(GRAPHENE) - .whenIconCollected()), - + .whenIconCollected() + ), RAW_LEAD = create("raw_lead", b -> b.icon(CNItems.RAW_LEAD) .title("Raw Lead") .description("Obtain some raw lead by mining lead ore") .after(ROOT) - .whenIconCollected()), + .whenIconCollected() + ), LEAD_INGOT = create("lead_ingot", b -> b.icon(CNItems.LEAD_INGOT) - .title("Lead ingot") + .title("Lead Ingot") .description("Smelt a raw lead to obtain a lead ingot") .after(RAW_LEAD) - .whenIconCollected()), + .whenIconCollected() + ), REINFORCED_GLASS = create("reinforced_glass", b -> b.icon(CNBlocks.REINFORCED_GLASS) .title("Reinforced Glass") .description("Craft some reinforced glass for the first time") .after(LEAD_INGOT) - .whenIconCollected()), + .whenIconCollected() + ), - ANTI_RADIATION_ARMOR = create("anti_radiation_armor", b -> b.icon(CNItems.ANTI_RADIATION_HELMETS.get(DyeColor.WHITE)) - .title("Anti radiation Armor") + /* + ANTI_RADIATION_ARMOR = create("anti_radiation_armor", b -> b.icon(CNItems.ANTI_RADIATION_HELMETS) + .title("Anti Radiation Armor") .description("Craft your first anti-radiation armor piece to protect yourself from radiation") .after(LEAD_INGOT) - .whenItemCollected(CNTags.CNItemTags.ALL_ANTI_RADIATION_ARMORS.tag)), - - FULL_ANTI_RADIATION_ARMOR = create("full_anti_radiation_armor", b -> b.icon(CNItems.ANTI_RADIATION_CHESTPLATES.get(DyeColor.WHITE)) - .title("Fully Protected") - .description("Wear a full set of anti-radiation armor to fully protect yourself from radiation") - //.externalTrigger( - // InventoryChangeTrigger.TriggerInstance.hasItems( - // new ItemPredicate(CNTags.CNItemTags.ANTI_RADIATION_HELMET_FULL_DYE.tag, null, MinMaxBounds.Ints.ANY, MinMaxBounds.Ints.ANY, null, null, null, NbtPredicate.ANY), - // new ItemPredicate(CNTags.CNItemTags.ANTI_RADIATION_CHESTPLATE_FULL_DYE.tag, null, MinMaxBounds.Ints.ANY, MinMaxBounds.Ints.ANY, null, null, null, NbtPredicate.ANY), - // new ItemPredicate(CNTags.CNItemTags.ANTI_RADIATION_LEGGINGS_FULL_DYE.tag, null, MinMaxBounds.Ints.ANY, MinMaxBounds.Ints.ANY, null, null, null, NbtPredicate.ANY), - // new ItemPredicate(CNTags.CNItemTags.ANTI_RADIATION_BOOTS_DYE.tag, null, MinMaxBounds.Ints.ANY, MinMaxBounds.Ints.ANY, null, null, null, null) - // )) - .after(ANTI_RADIATION_ARMOR)), - - DYE_ANTI_RADIATION_ARMOR = create("dye_anti_radiation_armor", b -> b.icon(CNItems.ANTI_RADIATION_HELMETS.get(DyeColor.RED)) + .whenItemCollected(CNTags.CNItemTags.ANTI_RADIATION_ARMOR.tag) + ), + + AVOIDING_CANCER = create("avoiding_cancer", b -> b.icon(CNItems.ANTI_RADIATION_HELMETS) + .title("Best Keep Yourself Covered") + .description("Equip anti-radiation armor for the first time") + .after(ANTI_RADIATION_ARMOR) + .externalTrigger( + InventoryChangeTrigger.TriggerInstance.hasItems(ItemPredicate.Builder.item().of(CNItems.ANTI_RADIATION_HELMETS).build()) + ) + ), + + DYE_ANTI_RADIATION_ARMOR = create("dye_anti_radiation_armor", b -> b.icon(CNItems.ANTI_RADIATION_HELMETS) .title("Pimp My Armor") .description("Dye your anti radiation armor to any color") - .whenItemCollected(CNTags.CNItemTags.ANTI_RADIATION_HELMET_DYE.tag) - .whenItemCollected(CNTags.CNItemTags.ANTI_RADIATION_CHESTPLATE_DYE.tag) - .whenItemCollected(CNTags.CNItemTags.ANTI_RADIATION_LEGGINGS_DYE.tag) - .after(ANTI_RADIATION_ARMOR)), - - - AUTOMATIC_URANIUM = create("automatic_uranium", b -> b.icon(CNItems.URANIUM_POWDER) - .title("Automating Uranium") - .description("Obtain some uranium powder using the automatic uranium processing") - .after(ROOT) - .special(SECRET)), + .after(ANTI_RADIATION_ARMOR) + ), + */ REACTOR_CASING = create("reactor_casing", b -> b.icon(CNBlocks.REACTOR_CASING) .title("The Power Of The Reactor") .description("Craft a reactor casing to begin building your nuclear reactor") .after(ROOT) - .whenIconCollected()), + .whenIconCollected() + ), + + + SILENCE_THE_CORE = create("silence_the_core", b -> b.icon(CNBlocks.REACTOR_ALARM.asItem()) + .title("It Makes So Much Noise !") + .description("sound the alarm for the first time") + .after(REACTOR_CASING) + ), + REACTOR_CONTROLLER = create("reactor_controller", b -> b.icon(CNBlocks.REACTOR_CONTROLLER) .title("Controller Of The Core") .description("Craft a reactor controller to manage and regulate your reactor") .after(REACTOR_CASING) - .whenIconCollected()), + .whenIconCollected() + ), + NO_TIME_TO_DIE = create("no_time_to_die", b -> b.icon(CNPaletteStoneTypes.AUTUNITE.baseBlock.get().asItem()) + .title("No Time To Die") + .description("Stop the reactor before the KABOOOM !") + .after(REACTOR_CONTROLLER) + ), + + T1_REACTOR = create("t1", b -> b.icon(CNBlocks.REACTOR_CONTROLLER.asItem()) + .title("Reactor Controller T1") + .description("Build a tier 1 Nuclear Reactor") + .special(EXPERT) + .after(REACTOR_CONTROLLER) + ), + + T2_REACTOR = create("t2", b -> b.icon(CNBlocks.REACTOR_CONTROLLER.asItem()) + .title("Reactor Controller T2") + .description("Build a tier 2 Nuclear Reactor") + .special(EXPERT) + .after(T1_REACTOR) + ), + + T3_REACTOR = create("t3", b -> b.icon(CNBlocks.REACTOR_CONTROLLER.asItem()) + .title("Reactor Controller T3") + .description("Build a tier 3 Nuclear Reactor") + .special(EXPERT) + .after(T2_REACTOR) + ), REACTOR_BLUEPRINT = create("reactor_blueprint", b -> b.icon(CNItems.REACTOR_BLUEPRINT) .title("Blueprint For Power") .description("Craft a reactor blueprint to design the layout of rods in your reactor") .after(REACTOR_CONTROLLER) - .whenIconCollected()), + .whenIconCollected() + ), - REACTOR_COOLER = create("reactor_cooler", b -> b.icon(CNBlocks.REACTOR_COOLER) - .title("Cooling The Reactor") - .description("Craft a reactor cooler to cool your reactor") + /* + REACTOR_ROD_INPUT = create("feeding_the_reactor", b -> b.icon(CNBlocks.REACTOR_ROD_INPUT) + .title("Feeding The Reactor") + .description("Craft a Reactor Rod Input to feed heating and cooling rods into your reactor") .after(REACTOR_CASING) - .whenIconCollected()), + .whenIconCollected() + ), - REACTOR_FRAME = create("reactor_frame", b -> b.icon(CNBlocks.REACTOR_FRAME) - .title("Reactor Frame") - .description("Craft a reactor frame to build your nuclear reactor") - .after(REACTOR_CASING) - .whenIconCollected()), - - REACTOR_INPUT = create("reactor_input", b -> b.icon(CNBlocks.REACTOR_INPUT) + REACTOR_FLUID_INPUT = create("fueling_the_reactor", b -> b.icon(CNBlocks.REACTOR_FLUID_INPUT) .title("Fueling The Reactor") - .description("Craft a reactor input to feed uranium and graphite rods into your reactor") - .after(REACTOR_CASING) - .whenIconCollected()), - - REACTOR_OUTPUT = create("reactor_output", b -> b.icon(CNBlocks.REACTOR_OUTPUT) - .title("Power Output") - .description("Craft a reactor output to transfer the energy produced by your reactor") - .after(REACTOR_CASING) - .whenIconCollected()), + .description("Craft a Reactor Fluid Input to feed liquid coolant into your reactor") + .after(REACTOR_ROD_INPUT) + .whenIconCollected() + ), + + REACTOR_OUTPUT = create("unlimited_power", b -> b.icon(CNBlocks.REACTOR_OUTPUT) + .title("Unlimited Power") + .description("Craft a Reactor Output to extract the energy produced by your reactor") + .after(REACTOR_FLUID_INPUT) + .whenIconCollected() + ), + */ REACTOR_CORE = create("reactor_core", b -> b.icon(CNBlocks.REACTOR_CORE) - .title("Core of Power") + .title("Core Of Power") .description("Craft the reactor core to harness the full energy of your nuclear reactor") .after(REACTOR_CASING) - .whenIconCollected()), + .whenIconCollected() + ), - END = null; + REACTOR_COOLER = create("reactor_cooler", b -> b.icon(CNBlocks.REACTOR_COOLER) + .title("Cooling The Reactor") + .description("Craft a reactor cooler to cool your reactor") + .after(REACTOR_CORE) + .whenIconCollected() + ), + + REACTOR_FRAME = create("reactor_frame", b -> b.icon(CNBlocks.REACTOR_FRAME) + .title("Reactor Frame") + .description("Craft a reactor frame to build your nuclear reactor") + .after(REACTOR_COOLER) + .whenIconCollected() + ) + ; private final PackOutput output; private final CompletableFuture registries; - private static CreateNuclearAdvancement create(String id, UnaryOperator b) { return new CreateNuclearAdvancement(id, b); } @@ -228,14 +379,13 @@ public CompletableFuture run(CachedOutput cache) { return this.registries.thenCompose(provider -> { PackOutput.PathProvider pathProvider = output.createPathProvider(PackOutput.Target.DATA_PACK, "advancement"); List> futures = new ArrayList<>(); - Set set = Sets.newHashSet(); + Consumer consumer = (advancement) -> { ResourceLocation id = advancement.id(); if (!set.add(id)) throw new IllegalStateException("Duplicate advancement " + id); Path path = pathProvider.json(id); - LOGGER.info("Saving advancement {}", id); futures.add(DataProvider.saveStable(cache, provider, Advancement.CODEC, advancement.value(), path)); }; @@ -248,7 +398,7 @@ public CompletableFuture run(CachedOutput cache) { @Override public String getName() { - return "CreateNuclear's Advancements"; + return "Create Nuclear Advancements"; } public static void provideLang(BiConsumer consumer) { @@ -256,7 +406,5 @@ public static void provideLang(BiConsumer consumer) { advancement.provideLang(consumer); } - public static void register() { - } - + public static void register() {} } \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNAdvancementBehaviour.java b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNAdvancementBehaviour.java new file mode 100644 index 0000000..0c2a4ba --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNAdvancementBehaviour.java @@ -0,0 +1,128 @@ +package net.nuclearteam.createnuclear.foundation.advancement; + +import com.simibubi.create.foundation.advancement.AdvancementBehaviour; +import com.simibubi.create.foundation.advancement.CreateAdvancement; +import com.simibubi.create.foundation.blockEntity.SmartBlockEntity; +import com.simibubi.create.foundation.blockEntity.behaviour.BehaviourType; +import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.common.util.FakePlayer; + +import java.util.*; + +public class CNAdvancementBehaviour extends BlockEntityBehaviour { + public static final BehaviourType TYPE = new BehaviourType<>(); + + private UUID playerId; + private final Set advancements; + + public CNAdvancementBehaviour(SmartBlockEntity be, CreateNuclearAdvancement ...advancement) { + super(be); + this.advancements = new HashSet<>(); + add(advancement); + } + + public void add(CreateNuclearAdvancement ...advancement) { + Collections.addAll(this.advancements, advancement); + } + + public boolean isOwnerPresent() { + return playerId != null; + } + + public void setPlayer(UUID id) { + Player player = getWorld().getPlayerByUUID(id); + if (player == null) return; + playerId = id; + removeAwarded(); + blockEntity.setChanged(); + } + + @Override + public void initialize() { + super.initialize(); + removeAwarded(); + } + + private void removeAwarded() { + Player player = getPlayer(); + if (player == null) return; + + advancements.removeIf(c -> c.isAlreadyAwardedTo(player)); + if (advancements.isEmpty()) { + playerId = null; + blockEntity.setChanged(); + } + } + + public void awardPlayerIfNear(CreateNuclearAdvancement advancement, int maxDistance) { + Player player = getPlayer(); + if (player == null) + return; + if (player.distanceToSqr(Vec3.atCenterOf(getPos())) > maxDistance * maxDistance) + return; + award(advancement, player); + } + + public void awardPlayer(CreateNuclearAdvancement advancement) { + Player player = getPlayer(); + if (player == null) + return; + award(advancement, player); + } + + private void award(CreateNuclearAdvancement advancement, Player player) { + if (!advancements.contains(advancement)) advancement.awardTo(player); + removeAwarded(); + } + + private Player getPlayer() { + if (playerId == null) return null; + return getWorld().getPlayerByUUID(playerId); + } + + @Override + public void write(CompoundTag nbt, HolderLookup.Provider registries, boolean clientPacket) { + super.write(nbt, registries, clientPacket); + + if (playerId != null) + nbt.putUUID("Owner", playerId); + } + + @Override + public void read(CompoundTag nbt, HolderLookup.Provider registries, boolean clientPacket) { + super.read(nbt, registries, clientPacket); + + if (nbt.contains("Owner")) + playerId = nbt.getUUID("Owner"); + } + + public static void tryAward(BlockGetter reader, BlockPos pos, CreateAdvancement advancement) { + AdvancementBehaviour behaviour = BlockEntityBehaviour.get(reader, pos, AdvancementBehaviour.TYPE); + if (behaviour != null) + behaviour.awardPlayer(advancement); + } + + @Override + public BehaviourType getType() { + return TYPE; + } + + public static void setPlacedBy(Level worldIn, BlockPos pos, LivingEntity placer) { + CNAdvancementBehaviour behaviour = BlockEntityBehaviour.get(worldIn, pos, TYPE); + if (behaviour == null) + return; + if (placer instanceof FakePlayer) + return; + if (placer instanceof ServerPlayer) + behaviour.setPlayer(placer.getUUID()); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNTriggers.java b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNTriggers.java index fe30a57..9f256ff 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNTriggers.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CNTriggers.java @@ -1,7 +1,5 @@ package net.nuclearteam.createnuclear.foundation.advancement; -import com.simibubi.create.foundation.advancement.CriterionTriggerBase; -import com.simibubi.create.foundation.advancement.SimpleCreateTrigger; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; @@ -11,18 +9,19 @@ public class CNTriggers { private static final List> triggers = new LinkedList<>(); - public static SimpleCreateTrigger addSimple(String id) { - return add(new SimpleCreateTrigger(id)); + public static SimpleCreateNuclearTrigger addSimple(String id) { + return add(new SimpleCreateNuclearTrigger(id)); } private static > T add(T instance) { triggers.add(instance); + return instance; } public static void register() { - triggers.forEach(trigger -> { - Registry.register(BuiltInRegistries.TRIGGER_TYPES, trigger.getId(), trigger); + triggers.forEach(triggers -> { + Registry.register(BuiltInRegistries.TRIGGER_TYPES, triggers.getId(), triggers); }); } -} +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CreateNuclearAdvancement.java b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CreateNuclearAdvancement.java index 87383d9..afd1f76 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CreateNuclearAdvancement.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CreateNuclearAdvancement.java @@ -1,12 +1,11 @@ package net.nuclearteam.createnuclear.foundation.advancement; -import com.simibubi.create.Create; -import com.simibubi.create.foundation.advancement.AllAdvancements; -import com.simibubi.create.foundation.advancement.AllTriggers; import com.simibubi.create.foundation.advancement.CreateAdvancement; -import com.simibubi.create.foundation.advancement.SimpleCreateTrigger; import com.tterrag.registrate.util.entry.ItemProviderEntry; -import net.minecraft.advancements.*; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementType; +import net.minecraft.advancements.Criterion; import net.minecraft.advancements.critereon.*; import net.minecraft.core.HolderLookup; import net.minecraft.network.chat.Component; @@ -19,6 +18,8 @@ import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; import net.nuclearteam.createnuclear.CreateNuclear; +import net.minecraft.core.HolderLookup.Provider; + import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -28,18 +29,18 @@ @SuppressWarnings("unused") public class CreateNuclearAdvancement { - static final ResourceLocation BACKGROUND = CreateNuclear.asResource("textures/block/steel_block.png"); + static final ResourceLocation BACKGROUND = CreateNuclear.asResource("textures/gui/advancements/backgrounds/background_advancement.png"); static final String LANG = "advancement." + CreateNuclear.MOD_ID + "."; static final String SECRET_SUFFIX = "\n\u00A77(Hidden Advancement)"; - private final Advancement.Builder mcBuilder = Advancement.Builder.advancement(); - private SimpleCreateTrigger builtinTrigger; + private final Advancement.Builder builder = Advancement.Builder.advancement(); + private SimpleCreateNuclearTrigger builtinTrigger; private CreateNuclearAdvancement parent; - private final Builder createBuilder = new Builder(); + private final Builder createNuclearBuilder = new Builder(); AdvancementHolder datagenResult; - private final String id; + final String id; private String title; private String description; @@ -47,14 +48,14 @@ public class CreateNuclearAdvancement { public CreateNuclearAdvancement(String id, UnaryOperator b) { this.id = id; - b.apply(createBuilder); + b.apply(createNuclearBuilder); - if (!createBuilder.externalTrigger) { + if (!createNuclearBuilder.externalTrigger) { builtinTrigger = CNTriggers.addSimple(id + "_builtin"); - mcBuilder.addCriterion("0", builtinTrigger.createCriterion(builtinTrigger.instance())); + builder.addCriterion("0", builtinTrigger.createCriterion(builtinTrigger.instance())); } - if (createBuilder.type == CreateNuclearAdvancement.TaskType.SECRET) + if (createNuclearBuilder.type == TaskType.SECRET) description += SECRET_SUFFIX; CNAdvancement.ENTRIES.add(this); @@ -92,17 +93,22 @@ public void awardTo(Player player) { void save(Consumer t, HolderLookup.Provider registries) { if (parent != null) - mcBuilder.parent(parent.datagenResult); + builder.parent(parent.datagenResult); - if (createBuilder.func != null) - createBuilder.icon(createBuilder.func.apply(registries)); + if (createNuclearBuilder.func != null) + createNuclearBuilder.icon(createNuclearBuilder.func.apply(registries)); - mcBuilder.display(createBuilder.icon, Component.translatable(titleKey()), + builder.display( + createNuclearBuilder.icon, + Component.translatable(titleKey()), Component.translatable(descriptionKey()).withStyle(s -> s.withColor(0xDBA213)), - id.equals("root") ? BACKGROUND : null, createBuilder.type.advancementType, createBuilder.type.toast, - createBuilder.type.announce, createBuilder.type.hide); - - datagenResult = mcBuilder.save(t, CreateNuclear.asResource(id).toString()); + id.equals("root") ? BACKGROUND : null, + createNuclearBuilder.type.advancementType, + createNuclearBuilder.type.toast, + createNuclearBuilder.type.announce, + createNuclearBuilder.type.hide + ); + datagenResult = builder.save(t, CreateNuclear.asResource(id).toString()); } void provideLang(BiConsumer consumer) { @@ -125,8 +131,8 @@ enum TaskType { private final boolean announce; private final boolean hide; - TaskType(AdvancementType advancementType, boolean toast, boolean announce, boolean hide) { - this.advancementType = advancementType; + TaskType(AdvancementType frame, boolean toast, boolean announce, boolean hide) { + this.advancementType = frame; this.toast = toast; this.announce = announce; this.hide = hide; @@ -139,7 +145,7 @@ class Builder { private boolean externalTrigger; private int keyIndex; private ItemStack icon; - private Function func; + private Function func; Builder special(TaskType type) { this.type = type; @@ -164,7 +170,7 @@ Builder icon(ItemStack stack) { return this; } - Builder icon(Function func) { + Builder icon(Function func) { this.func = func; return this; } @@ -197,8 +203,7 @@ Builder whenItemCollected(ItemLike itemProvider) { } Builder whenItemCollected(TagKey tag) { - return externalTrigger(InventoryChangeTrigger.TriggerInstance - .hasItems(ItemPredicate.Builder.item().of(tag).build())); + return externalTrigger(InventoryChangeTrigger.TriggerInstance.hasItems(ItemPredicate.Builder.item().of(tag).build())); } Builder awardedForFree() { @@ -206,12 +211,11 @@ Builder awardedForFree() { } Builder externalTrigger(Criterion trigger) { - mcBuilder.addCriterion(String.valueOf(keyIndex), trigger); + builder.addCriterion(String.valueOf(keyIndex), trigger); externalTrigger = true; keyIndex++; return this; } } - -} +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CriterionTriggerBase.java b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CriterionTriggerBase.java new file mode 100644 index 0000000..6463366 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/CriterionTriggerBase.java @@ -0,0 +1,70 @@ +package net.nuclearteam.createnuclear.foundation.advancement; + +import com.google.common.collect.Maps; +import net.minecraft.advancements.CriterionTrigger; +import net.minecraft.advancements.critereon.SimpleCriterionTrigger; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.PlayerAdvancements; +import net.minecraft.server.level.ServerPlayer; +import net.nuclearteam.createnuclear.CreateNuclear; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Supplier; + +public abstract class CriterionTriggerBase implements CriterionTrigger { + private final ResourceLocation id; + protected final Map>> listeners = Maps.newHashMap(); + + public CriterionTriggerBase(String id) { + this.id = CreateNuclear.asResource(id); + } + + @Override + public void addPlayerListener(PlayerAdvancements playerAdvancements, Listener listener) { + Set> playerListeners = this.listeners.computeIfAbsent(playerAdvancements, k -> new HashSet<>()); + + playerListeners.add(listener); + } + + @Override + public void removePlayerListener(PlayerAdvancements playerAdvancements, Listener listener) { + Set> playerListeners = this.listeners.get(playerAdvancements); + + if (playerListeners != null) { + playerListeners.remove(listener); + if (playerListeners.isEmpty()) { + this.listeners.remove(playerAdvancements); + } + } + } + + @Override + public void removePlayerListeners(PlayerAdvancements playerAdvancements) { + this.listeners.remove(playerAdvancements); + } + + public ResourceLocation getId() { + return id; + } + + protected void trigger(ServerPlayer player, @Nullable List> suppliers) { + PlayerAdvancements playerAdvancements = player.getAdvancements(); + Set> playerListeners = this.listeners.get(playerAdvancements); + if (playerListeners != null) { + List> list = new LinkedList<>(); + + for (Listener listener : playerListeners) { + if (listener.trigger().test(suppliers)) { + list.add(listener); + } + } + + list.forEach(listener -> listener.run(playerAdvancements)); + } + } + + public abstract static class Instance implements SimpleCriterionTrigger.SimpleInstance { + protected abstract boolean test(@Nullable List> suppliers); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/SimpleCreateNuclearTrigger.java b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/SimpleCreateNuclearTrigger.java new file mode 100644 index 0000000..106a550 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/advancement/SimpleCreateNuclearTrigger.java @@ -0,0 +1,60 @@ +package net.nuclearteam.createnuclear.foundation.advancement; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.critereon.ContextAwarePredicate; +import net.minecraft.advancements.critereon.EntityPredicate; +import net.minecraft.advancements.critereon.SimpleCriterionTrigger; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +public class SimpleCreateNuclearTrigger extends CriterionTriggerBase { + + public SimpleCreateNuclearTrigger(String id) { + super(id); + } + + public void trigger(ServerPlayer player) { + super.trigger(player, null); + } + + public SimpleCreateNuclearTrigger.Instance instance() { + return new SimpleCreateNuclearTrigger.Instance(); + } + + @Override + public Codec codec() { + return Instance.CODEC; + } + + public static class Instance extends CriterionTriggerBase.Instance { + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(Instance::player) + ).apply(instance, Instance::new)); + + private final Optional player; + + public Instance() { + player = Optional.empty(); + } + + public Instance(Optional player) { + this.player = player; + } + + @Override + protected boolean test(@Nullable List> suppliers) { + return true; + } + + @Override + public Optional player() { + return player; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/block/EventTriggerBlock.java b/src/main/java/net/nuclearteam/createnuclear/foundation/block/EventTriggerBlock.java deleted file mode 100644 index c7829ae..0000000 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/block/EventTriggerBlock.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.nuclearteam.createnuclear.foundation.block; - -import net.createmod.catnip.platform.CatnipServices; -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.ItemInteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.BlockHitResult; -import net.nuclearteam.createnuclear.CNPackets; -import net.nuclearteam.createnuclear.CreateNuclear; -import net.nuclearteam.createnuclear.content.multiblock.controller.EventTriggerPacket; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class EventTriggerBlock extends Block { - public EventTriggerBlock(Properties properties) { - super(properties); - } - - @Override - public ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { - if (!level.isClientSide) { - // Send a packet to all clients around this block within 16 blocks - EventTriggerPacket packet = new EventTriggerPacket(100); // display for 100 ticks - CreateNuclear.LOGGER.warn("hum EventTriggerBlock ? {}", packet); - CatnipServices.NETWORK.sendToClientsAround((ServerLevel) level, pos, 16, packet); - } - return ItemInteractionResult.SUCCESS; - } -} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/block/MultiDirectionalReactorBlock.java b/src/main/java/net/nuclearteam/createnuclear/foundation/block/MultiDirectionalReactorBlock.java new file mode 100644 index 0000000..790087c --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/block/MultiDirectionalReactorBlock.java @@ -0,0 +1,29 @@ +package net.nuclearteam.createnuclear.foundation.block; + +import com.mojang.math.MethodsReturnNonnullByDefault; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; + +@MethodsReturnNonnullByDefault +@SuppressWarnings("deprecation") +public abstract class MultiDirectionalReactorBlock extends Block { + public static final DirectionProperty FACING = BlockStateProperties.FACING; + + public MultiDirectionalReactorBlock(Properties properties) { + super(properties); + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/damagesTypes/CNDamageSources.java b/src/main/java/net/nuclearteam/createnuclear/foundation/damagesTypes/CNDamageSources.java new file mode 100644 index 0000000..1c2dee6 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/damagesTypes/CNDamageSources.java @@ -0,0 +1,37 @@ +package net.nuclearteam.createnuclear.foundation.damagesTypes; + +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageType; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; +import net.nuclearteam.createnuclear.CNDamageTypes; + +import javax.annotation.Nullable; + +public class CNDamageSources { + public static DamageSource radiation(Level level) { + return source(CNDamageTypes.RADIATION, level); + } + /*public static DamageSource fanRadiation(Level level) { + return source(CNDamageTypes.FAN_RADIATION, level); + }*/ + + private static DamageSource source(ResourceKey key, LevelReader level) { + Registry registry = level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE); + return new DamageSource(registry.getHolderOrThrow(key)); + } + + private static DamageSource source(ResourceKey key, LevelReader level, @Nullable Entity entity) { + Registry registry = level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE); + return new DamageSource(registry.getHolderOrThrow(key), entity); + } + + private static DamageSource source(ResourceKey key, LevelReader level, @Nullable Entity causingEntity, @Nullable Entity directEntity) { + Registry registry = level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE); + return new DamageSource(registry.getHolderOrThrow(key), causingEntity, directEntity); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/CNBuilderTransformers.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/CNBuilderTransformers.java new file mode 100644 index 0000000..612154c --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/CNBuilderTransformers.java @@ -0,0 +1,97 @@ +package net.nuclearteam.createnuclear.foundation.data; + +import com.tterrag.registrate.providers.DataGenContext; +import com.tterrag.registrate.providers.RegistrateItemModelProvider; +import com.tterrag.registrate.util.entry.ItemEntry; +import com.tterrag.registrate.util.nullness.NonNullBiConsumer; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.Item; +import net.neoforged.neoforge.client.model.generators.ItemModelBuilder; +import net.neoforged.neoforge.common.DeferredSpawnEggItem; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.biome.BiomeIrradiationExtractorItem; + +import java.util.List; +import java.util.function.Supplier; + +public class CNBuilderTransformers { + + /** + * Generates the item model for a dyeable anti-radiation armor piece. + *

+ * The undyed ("default") piece uses a flat 2D icon ({@code item/generated} with the + * {@code item/armors/default_anti_radiation_} sprite) instead of the 3D Blockbench + * model, since the dropped/inventory 3D rendering was problematic. The worn armor on the + * body is unaffected (handled by {@code AntiRadiationArmorModel}). + *

+ * One model override per {@link DyeColor} is added, driven by the client-side + * {@code createnuclear:cloth_color} item property (see {@code CreateNuclearClient}). Each + * per-color child model still re-textures the 3D Blockbench geometry ({@code item//item}) + * with the matching {@code item/armors/_anti_radiation_suit} sheet. + *

+ * The texture keys differ per slot: the helmet geometry uses {@code layer0}/{@code particle}, + * the other pieces use {@code 14} — hence {@code textureKeys} is passed explicitly. + */ + public static NonNullBiConsumer, RegistrateItemModelProvider> coloredArmorModel(String slot, String... textureKeys) { + return (c, p) -> { + ResourceLocation baseParent = p.modLoc("item/" + c.getName() + "/item"); + ItemModelBuilder outer = p.generated(c, CreateNuclear.asResource("item/armors/default_anti_radiation_" + slot)); + // DyeColor.values() is ordered by id (0..15); overrides must be ascending by predicate + // value so vanilla override resolution selects the exact color (returns last match <= value).cloth + // The cloth_color property is clamped to [0,1] (see CreateNuclearClient), hence the + // (id+1)/16 normalization here must match the value returned there exactly. + for (DyeColor color : DyeColor.values()) { + String colorName = color.getSerializedName(); + ItemModelBuilder child = p.withExistingParent("item/colored/" + colorName + "_anti_radiation_" + slot, baseParent); + for (String key : textureKeys) { + child.texture(key, "models/armor/" + colorName + "_anti_radiation_suit"); + } + outer.override() + .predicate(CreateNuclear.asResource("cloth_color"), (color.getId() + 1) / 16f) + .model(child) + .end(); + } + }; + } + + /** + * Generates the per-tier model overrides for the biome restore cell, driven by the + * client-side {@code createnuclear:charge} item property (see {@code CreateNuclearClient}). + * Overrides must stay ascending by predicate value (vanilla resolves to the last match <= value), + * and the thresholds here must match the ratio returned by {@code biomeRestoreItemProperties}. + */ + public static NonNullBiConsumer, RegistrateItemModelProvider> biomeRestoreModel() { + return (c, p) -> { + ItemModelBuilder outer = p.generated(c, CreateNuclear.asResource("item/biome_irradiation_extractor/empty")); + + record Tier(String name, float threshold) {} + List tiers = List.of( + new Tier("quarter", 0.25f), + new Tier("half", 0.5f), + new Tier("three_quarters", 0.75f), + new Tier("full", 1.0f) + ); + + for (Tier tier : tiers) { + ItemModelBuilder child = p.withExistingParent("item/biome_irradiation_extractor/" + tier.name(), p.mcLoc("item/generated")) + .texture("layer0", CreateNuclear.asResource("item/biome_irradiation_extractor/" + tier.name())); + + outer.override() + .predicate(CreateNuclear.asResource(BiomeIrradiationExtractorItem.TAG), tier.threshold()) + .model(child) + .end(); + } + }; + } + + public static ItemEntry spawnEgg(String name, Supplier> entity, int backgroundColor, int highlightColor, String nameItems) { + return CreateNuclear.REGISTRATE + .item(name, p -> new DeferredSpawnEggItem(entity, backgroundColor, highlightColor, p)) + .lang(nameItems) + .model((c, p) -> p.withExistingParent(c.getName(), ResourceLocation.parse("item/template_spawn_egg"))) + .register(); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCompactingRecipeGen.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCompactingRecipeGen.java index 73f46b4..2a04486 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCompactingRecipeGen.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCompactingRecipeGen.java @@ -16,7 +16,12 @@ public class CNCompactingRecipeGen extends CompactingRecipeGen { YELLOWCAKE = create(CreateNuclear.asResource("uranium_fluid_to_yellowcake"), b -> b .require(CNFluids.URANIUM.get(), 100) .output(CNItems.YELLOWCAKE, 1) - ); + ), + THORIUM = create(CreateNuclear.asResource("thorium_fluid_to_thorium_ingot"), b -> b + .require(CNTags.CNFluidTags.THORIUM.tag, 200) + .output(CNItems.THORIUM_INGOT, 1) + ) + ; public CNCompactingRecipeGen(PackOutput output, CompletableFuture registries) { diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCrushingRecipeGen.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCrushingRecipeGen.java index 994a431..add3630 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCrushingRecipeGen.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNCrushingRecipeGen.java @@ -2,6 +2,7 @@ import com.simibubi.create.AllItems; import com.simibubi.create.api.data.recipe.CrushingRecipeGen; +import com.simibubi.create.content.decoration.palettes.AllPaletteStoneTypes; import net.minecraft.core.HolderLookup; import net.minecraft.data.PackOutput; import net.minecraft.tags.ItemTags; @@ -38,6 +39,18 @@ public class CNCrushingRecipeGen extends CrushingRecipeGen { .output(1, CNItems.URANIUM_POWDER,81) ), + RAW_THORIUM_BLOCK = create(() -> CNBlocks.RAW_THORIUM_BLOCK, b -> b + .duration(250) + .output(1, CNItems.THORIUM_DUST, 9) + .output(0.5f, CNItems.THORIUM_DUST, 72) + ), + + RAW_THORIUM_ITEM = create(() -> CNItems.RAW_THORIUM, b -> b + .duration(125) + .output(1, CNItems.THORIUM_DUST, 1) + .output(0.5f, CNItems.THORIUM_DUST, 8) + ), + RAW_ZINC = create(() -> AllItems.RAW_ZINC, b -> b.duration(250) .output(1, AllItems.CRUSHED_ZINC, 1) .output(.75f, AllItems.EXP_NUGGET, 1) @@ -49,6 +62,13 @@ public class CNCrushingRecipeGen extends CrushingRecipeGen { .output(1, AllItems.CRUSHED_COPPER, 1) .output(.75f, AllItems.EXP_NUGGET, 1) .output(.15f, CNItems.LEAD_NUGGET,1) + ), + + NITRATE = create("nitrate", b -> b + .require(AllPaletteStoneTypes.LIMESTONE.materialTag) + .duration(250) + .output(.6f, CNItems.NITRATE, 1) + .output(.4f, CNItems.LEAD_NUGGET, 1) ) ; diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNItemApplicationRecipeGen.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNItemApplicationRecipeGen.java index 18589b3..67bcc6e 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNItemApplicationRecipeGen.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNItemApplicationRecipeGen.java @@ -31,22 +31,22 @@ public class CNItemApplicationRecipeGen extends ItemApplicationRecipeGen { GeneratedRecipe REACTOR_INPUT = itemApplication("reactor_input_from_hopper_and_reactor_casing", Items.HOPPER, CNBlocks.REACTOR_CASING.get(), - CNBlocks.REACTOR_INPUT.get() + CNBlocks.REACTOR_ROD_INPUT.get() ); protected GeneratedRecipe itemApplication(String name, Ingredient ingredient, ItemLike input, ItemLike output) { return create(CreateNuclear.asResource(name), b -> - b.require(input) - .require(ingredient) - .output(output) + b.require(input) + .require(ingredient) + .output(output) ); } protected GeneratedRecipe itemApplication(String name, Item ingredient, ItemLike input, ItemLike output) { return create(CreateNuclear.asResource(name), b -> - b.require(input) - .require(ingredient) - .output(output) + b.require(input) + .require(ingredient) + .output(output) ); } diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNMechanicalCraftingRecipeGen.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNMechanicalCraftingRecipeGen.java index ada2a3f..d6217fa 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNMechanicalCraftingRecipeGen.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNMechanicalCraftingRecipeGen.java @@ -44,6 +44,16 @@ public class CNMechanicalCraftingRecipeGen extends MechanicalCraftingRecipeGen { .patternLine("U ") ), + THORIUM_ROD = create(CNItems.THORIUM_ROD::get) + .recipe(b -> b + .key('U', Ingredient.of(CNItems.THORIUM_INGOT)) + .patternLine("U ") + .patternLine(" U ") + .patternLine(" U ") + .patternLine(" U ") + .patternLine(" U") + ), + REACTOR_MAIN_FRAME = create(CNBlocks.REACTOR_FRAME::get) .recipe(b -> b .key('C', Ingredient.of(CNBlocks.REACTOR_CASING)) diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNRecipeProvider.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNRecipeProvider.java index efabd09..abee1bd 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNRecipeProvider.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNRecipeProvider.java @@ -57,8 +57,8 @@ public String getName() { @Override public CompletableFuture run(CachedOutput dc) { return CompletableFuture.allOf(GENERATORS.stream() - .map(gen -> gen.run(dc)) - .toArray(CompletableFuture[]::new)); + .map(gen -> gen.run(dc)) + .toArray(CompletableFuture[]::new)); } }); } diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNShapelessRecipeGen.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNShapelessRecipeGen.java index d75ffd4..130af3a 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNShapelessRecipeGen.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNShapelessRecipeGen.java @@ -69,7 +69,7 @@ Marker enterFolder(String folder) { return new Marker(); } - GeneratedRecipeBuilder create(com.google.common.base.Supplier result) { + GeneratedRecipeBuilder create(Supplier result) { return new GeneratedRecipeBuilder(currentFolder, result); } @@ -90,7 +90,7 @@ GeneratedRecipe createSpecial(Function> builder, }); } - GeneratedRecipe blastCrushedMetal(com.google.common.base.Supplier result, com.google.common.base.Supplier ingredient) { + GeneratedRecipe blastCrushedMetal(Supplier result, Supplier ingredient) { return create(result::get).withSuffix("_from_crushed") .viaCooking(ingredient) .rewardXP(.1f) @@ -113,7 +113,7 @@ GeneratedRecipe recycleGlassPane(BlockEntry ingredient) { .inFurnace(); } - GeneratedRecipe blastFurnaceRecipe(com.google.common.base.Supplier result, com.google.common.base.Supplier ingredient, String suffix, int count) { + GeneratedRecipe blastFurnaceRecipe(Supplier result, Supplier ingredient, String suffix, int count) { return create(result::get).withSuffix(suffix) .returns(count) .viaCooking(ingredient) @@ -121,7 +121,7 @@ GeneratedRecipe blastFurnaceRecipe(com.google.common.base.Supplier result, com.google.common.base.Supplier> ingredient, String suffix, int count) { + GeneratedRecipe blastFurnaceRecipeTags(Supplier result, Supplier> ingredient, String suffix, int count) { return create(result::get).withSuffix(suffix) .returns(count) .viaCookingTag(ingredient) @@ -130,13 +130,13 @@ GeneratedRecipe blastFurnaceRecipeTags(com.google.common.base.Supplier> variants, - List>> ingredients) { + List>> ingredients) { GeneratedRecipe result = null; for (int i = 0; i + 1 < variants.size(); i++) { ItemProviderEntry currentEntry = variants.get(i); ItemProviderEntry nextEntry = variants.get(i + 1); - com.google.common.base.Supplier> currentIngredient = ingredients.get(i); - com.google.common.base.Supplier> nextIngredient = ingredients.get(i + 1); + Supplier> currentIngredient = ingredients.get(i); + Supplier> nextIngredient = ingredients.get(i + 1); result = create(nextEntry).withSuffix("_from_compacting") .unlockedBy(currentEntry::get) @@ -186,11 +186,11 @@ class GeneratedRecipeBuilder { private final String path; private String suffix; - private com.google.common.base.Supplier result; + private Supplier result; private ResourceLocation compatDatagenOutput; List recipeConditions; - private com.google.common.base.Supplier unlockedBy; + private Supplier unlockedBy; private int amount; private GeneratedRecipeBuilder(String path) { @@ -200,7 +200,7 @@ private GeneratedRecipeBuilder(String path) { this.amount = 1; } - public GeneratedRecipeBuilder(String path, com.google.common.base.Supplier result) { + public GeneratedRecipeBuilder(String path, Supplier result) { this(path); this.result = result; } @@ -215,14 +215,14 @@ GeneratedRecipeBuilder returns(int amount) { return this; } - GeneratedRecipeBuilder unlockedBy(com.google.common.base.Supplier item) { + GeneratedRecipeBuilder unlockedBy(Supplier item) { this.unlockedBy = () -> ItemPredicate.Builder.item() .of(item.get()) .build(); return this; } - GeneratedRecipeBuilder unlockedByTag(com.google.common.base.Supplier> tag) { + GeneratedRecipeBuilder unlockedByTag(Supplier> tag) { this.unlockedBy = () -> ItemPredicate.Builder.item() .of(tag.get()) .build(); @@ -271,7 +271,7 @@ GeneratedRecipe viaShapeless(UnaryOperator builder) { }); } - GeneratedRecipe viaNetheriteSmithing(com.google.common.base.Supplier base, com.google.common.base.Supplier upgradeMaterial) { + GeneratedRecipe viaNetheriteSmithing(Supplier base, Supplier upgradeMaterial) { return register(consumer -> { SmithingTransformRecipeBuilder b = SmithingTransformRecipeBuilder.smithing(Ingredient.of(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE), @@ -297,11 +297,11 @@ private ResourceLocation getRegistryName() { .asItem()) : compatDatagenOutput; } - GeneratedCookingRecipeBuilder viaCooking(com.google.common.base.Supplier item) { + GeneratedCookingRecipeBuilder viaCooking(Supplier item) { return unlockedBy(item).viaCookingIngredient(() -> Ingredient.of(item.get())); } - GeneratedCookingRecipeBuilder viaCookingTag(com.google.common.base.Supplier> tag) { + GeneratedCookingRecipeBuilder viaCookingTag(Supplier> tag) { return unlockedByTag(tag).viaCookingIngredient(() -> Ingredient.of(tag.get())); } @@ -444,9 +444,9 @@ private static ModdedCookingRecipeOutputShim.Serializer create(Recipe wrapped // getResourceKey and getId // byValue and toId // Holder.Reference: key - if (BuiltInRegistries.RECIPE_SERIALIZER instanceof com.simibubi.create.foundation.mixin.accessor.MappedRegistryAccessor mra) { + if (BuiltInRegistries.RECIPE_SERIALIZER instanceof MappedRegistryAccessor mra) { @SuppressWarnings("unchecked") - com.simibubi.create.foundation.mixin.accessor.MappedRegistryAccessor> mra$ = (MappedRegistryAccessor>) mra; + MappedRegistryAccessor> mra$ = (MappedRegistryAccessor>) mra; int wrappedId = mra$.getToId().getOrDefault(wrappedSerializer, -1); ResourceKey> wrappedKey = mra$.getByValue().get(wrappedSerializer).key(); diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNStandardRecipeGen.java b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNStandardRecipeGen.java index e575c40..a71f614 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNStandardRecipeGen.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/data/recipe/CNStandardRecipeGen.java @@ -483,9 +483,9 @@ private static ModdedCookingRecipeOutputShim.Serializer create(Recipe wrapped // getResourceKey and getId // byValue and toId // Holder.Reference: key - if (BuiltInRegistries.RECIPE_SERIALIZER instanceof com.simibubi.create.foundation.mixin.accessor.MappedRegistryAccessor mra) { + if (BuiltInRegistries.RECIPE_SERIALIZER instanceof MappedRegistryAccessor mra) { @SuppressWarnings("unchecked") - com.simibubi.create.foundation.mixin.accessor.MappedRegistryAccessor> mra$ = (MappedRegistryAccessor>) mra; + MappedRegistryAccessor> mra$ = (MappedRegistryAccessor>) mra; int wrappedId = mra$.getToId().getOrDefault(wrappedSerializer, -1); ResourceKey> wrappedKey = mra$.getByValue().get(wrappedSerializer).key(); diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/events/CNClientEvent.java b/src/main/java/net/nuclearteam/createnuclear/foundation/events/CNClientEvent.java index 8ba4463..413447a 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/events/CNClientEvent.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/events/CNClientEvent.java @@ -5,11 +5,19 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent; +import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; +import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; +import net.nuclearteam.createnuclear.CNItems; +import net.nuclearteam.createnuclear.CNParticleRegistry; +import net.nuclearteam.createnuclear.CNParticleTypes; import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorClientExtensions; +import net.nuclearteam.createnuclear.content.particles.NuclearMushroomCloudParticle; +import net.nuclearteam.createnuclear.content.particles.SmallNuclearExplosionParticle; import net.nuclearteam.createnuclear.foundation.events.overlay.IrradiatedOverlayRendererVision; -@EventBusSubscriber(modid = CreateNuclear.MOD_ID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +@EventBusSubscriber(modid = CreateNuclear.MOD_ID, value = Dist.CLIENT) public class CNClientEvent { private static final HudRenderer HUD_RENDERER = new HudRenderer(); @@ -19,5 +27,22 @@ public static void onRegisterGui(RegisterGuiLayersEvent event) { event.registerAbove(VanillaGuiLayers.CAMERA_OVERLAYS, CreateNuclear.asResource("irradiated_vision"), IrradiatedOverlayRendererVision::renderOverlay); } - + @SubscribeEvent + public static void onRegisterClientExtensions(RegisterClientExtensionsEvent event) { + AntiRadiationArmorClientExtensions extensions = new AntiRadiationArmorClientExtensions(); + event.registerItem(extensions, + CNItems.ANTI_RADIATION_HELMETS.get(), + CNItems.ANTI_RADIATION_CHESTPLATES.get(), + CNItems.ANTI_RADIATION_LEGGINGS.get(), + CNItems.ANTI_RADIATION_BOOTS.get() + ); + } + + @SubscribeEvent + public static void onRegisterParticleProviders(RegisterParticleProvidersEvent event) { + CNParticleTypes.registerFactories(event); + event.registerSpecial(CNParticleRegistry.NUCLEAR_MUSHROOM_CLOUD.get(), new NuclearMushroomCloudParticle.Factory()); + event.registerSpriteSet(CNParticleRegistry.NUCLEAR_MUSHROOM_CLOUD_SMOKE.get(), SmallNuclearExplosionParticle.NukeFactory::new); + event.registerSpriteSet(CNParticleRegistry.NUCLEAR_MUSHROOM_CLOUD_EXPLOSION.get(), SmallNuclearExplosionParticle.NukeFactory::new); + } } diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/events/ClientEvents.java b/src/main/java/net/nuclearteam/createnuclear/foundation/events/ClientEvents.java new file mode 100644 index 0000000..89a287d --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/events/ClientEvents.java @@ -0,0 +1,104 @@ +package net.nuclearteam.createnuclear.foundation.events; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.Entity; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.neoforge.client.event.RenderPlayerEvent; +import net.neoforged.neoforge.client.event.ViewportEvent; +import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.minecraft.client.model.PlayerModel; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; +import net.nuclearteam.createnuclear.CNClientProxy; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem; +// import net.nuclearteam.createnuclear.foundation.mixin.client.CameraAccessor; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; + +@EventBusSubscriber(modid = CreateNuclear.MOD_ID, value = Dist.CLIENT) +public class ClientEvents { + + /** + * Ticks down the nuke flash/darken timers, once per client tick. + */ + @SubscribeEvent + public static void onClientTick(ClientTickEvent.Post event) { + + // Store the previous value for smooth interpolation of the flash + CNClientProxy.prevNukeFlashAmount = CNClientProxy.nukeFlashAmount; + + // Count down the sky-darkening timer + if (CNClientProxy.renderNukeSkyDarkFor > 0) { + CNClientProxy.renderNukeSkyDarkFor--; + } + + // Ramp the white flash up while active, then fade it back out + if (CNClientProxy.renderNukeFlashFor > 0) { + if (CNClientProxy.nukeFlashAmount < 1F) { + CNClientProxy.nukeFlashAmount = Math.min(CNClientProxy.nukeFlashAmount + 0.4F, 1F); + } + CNClientProxy.renderNukeFlashFor--; + } else if (CNClientProxy.nukeFlashAmount > 0F) { + CNClientProxy.nukeFlashAmount = Math.max(CNClientProxy.nukeFlashAmount - 0.05F, 0F); + } + } + + /** + * Shakes the player's camera while a nuke explosion is active. + */ + @SubscribeEvent + public static void computeCameraAngles(ViewportEvent.ComputeCameraAngles event) { + Entity player = Minecraft.getInstance().getCameraEntity(); + float partialTick = Minecraft.getInstance().getTimer().getGameTimeDeltaTicks(); + + // Shake at 1.5F while the sky is darkened (nuke active), otherwise no shake + float tremorAmount = CNClientProxy.renderNukeSkyDarkFor > 0 ? 1.5F : 0F; + + if (player != null && CNConfigs.client().screenShaking.get()) { + if (tremorAmount > 0) { + // Generate random offsets for the shake, once per tick + if (CNClientProxy.lastTremorTick != player.tickCount) { + RandomSource rng = player.level().random; + CNClientProxy.randomTremorOffsets[0] = rng.nextFloat(); + CNClientProxy.randomTremorOffsets[1] = rng.nextFloat(); + CNClientProxy.randomTremorOffsets[2] = rng.nextFloat(); + CNClientProxy.lastTremorTick = player.tickCount; + } + + // Scale by Minecraft's screen-effect accessibility setting + double intensity = tremorAmount * Minecraft.getInstance().options.screenEffectScale().get(); + + // Physically offset the camera + /* + ((CameraAccessor) event.getCamera()).callMove( + CNClientProxy.randomTremorOffsets[0] * 0.2F * intensity, + CNClientProxy.randomTremorOffsets[1] * 0.2F * intensity, + CNClientProxy.randomTremorOffsets[2] * 0.5F * intensity + ); + */ + } + } + } + + @SubscribeEvent + public static void onRenderPlayerPre(RenderPlayerEvent.Pre event) { + Player player = event.getEntity(); + PlayerModel model = event.getRenderer().getModel(); + + if (player.getItemBySlot(EquipmentSlot.HEAD).getItem() instanceof AntiRadiationArmorItem.Helmet) { + model.hat.visible = false; + } + if (player.getItemBySlot(EquipmentSlot.CHEST).getItem() instanceof AntiRadiationArmorItem.Chestplate) { + model.jacket.visible = false; + model.rightSleeve.visible = false; + model.leftSleeve.visible = false; + } + if (player.getItemBySlot(EquipmentSlot.LEGS).getItem() instanceof AntiRadiationArmorItem.Leggings) { + model.rightPants.visible = false; + model.leftPants.visible = false; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEventClients.java b/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEventClients.java index 9e97952..c74092d 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEventClients.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEventClients.java @@ -4,11 +4,10 @@ import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.common.EventBusSubscriber.Bus; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.nuclearteam.createnuclear.CNEntityType; -@EventBusSubscriber(bus = Bus.MOD, value = Dist.CLIENT) +@EventBusSubscriber(value = Dist.CLIENT) public class CommentEventClients { @SubscribeEvent public static void registerLayers(EntityRenderersEvent.RegisterLayerDefinitions event) { diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEvents.java b/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEvents.java index 5f7042d..4bedd0d 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEvents.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/events/CommentEvents.java @@ -1,13 +1,18 @@ package net.nuclearteam.createnuclear.foundation.events; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.event.brewing.RegisterBrewingRecipesEvent; +import net.neoforged.neoforge.event.entity.EntityAttributeModificationEvent; +import net.nuclearteam.createnuclear.CNAttributes; import net.nuclearteam.createnuclear.CNPotions; import net.nuclearteam.createnuclear.CreateNuclear; -import net.nuclearteam.createnuclear.content.multiblock.input.ReactorInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.item.ReactorRodInputEntity; +import net.nuclearteam.createnuclear.content.multiblock.input.fluid.ReactorFluidInputEntity; @EventBusSubscriber(modid = CreateNuclear.MOD_ID, bus = EventBusSubscriber.Bus.GAME) public class CommentEvents { @@ -20,8 +25,20 @@ public static void onBrewingRecipeRegister(RegisterBrewingRecipesEvent event) { public static class ModBusEvents { @SubscribeEvent public static void registerCapabilities(RegisterCapabilitiesEvent event) { - ReactorInputEntity.registerCapabilities(event); + ReactorRodInputEntity.registerCapabilities(event); + ReactorFluidInputEntity.registerCapabilities(event); + } + } + + @EventBusSubscriber(modid = CreateNuclear.MOD_ID, bus = EventBusSubscriber.Bus.MOD) + public static class RadiationEvent { + @SubscribeEvent + public static void onEntityAttribute(EntityAttributeModificationEvent event) { + for (EntityType type : event.getTypes()) { + event.add(type, CNAttributes.IRRADIATED_RESISTANCE); + } } } } + diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/events/HudRenderer.java b/src/main/java/net/nuclearteam/createnuclear/foundation/events/HudRenderer.java index 7eeb910..06ff196 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/events/HudRenderer.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/events/HudRenderer.java @@ -4,7 +4,6 @@ import net.nuclearteam.createnuclear.foundation.events.overlay.EventTextOverlay; import net.nuclearteam.createnuclear.foundation.events.overlay.HelmetOverlay; import net.nuclearteam.createnuclear.foundation.events.overlay.HudOverlay; -import net.nuclearteam.createnuclear.foundation.events.overlay.RadiationOverlay; import java.util.Comparator; import java.util.List; diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/events/possible code b/src/main/java/net/nuclearteam/createnuclear/foundation/events/possible code deleted file mode 100644 index 87cf86c..0000000 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/events/possible code +++ /dev/null @@ -1,66 +0,0 @@ -/* - * META-INF/services/com.tonpackage.HudOverlay - * ------------------------------------------- - * Liste des implémentations de HudOverlay à charger via ServiceLoader - */ -com.tonpackage.HelmetOverlay -com.tonpackage.PotionOverlay -com.tonpackage.RadiationOverlay - -// -------------------------------------------------------- -package com.tonpackage; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.ServiceLoader; - -/** - * Chargement dynamique de tous les HudOverlay via Java SPI (ServiceLoader) - */ -public class HudOverlayRegistry { - private static final List ALL = new ArrayList<>(); - - static { - ServiceLoader.load(HudOverlay.class) - .forEach(ALL::add); - } - - /** - * Retourne la liste non modifiable de tous les overlays chargés. - */ - public static List getAll() { - return Collections.unmodifiableList(ALL); - } -} - -// -------------------------------------------------------- -package com.tonpackage; - -import net.minecraftforge.client.event.RenderGuiOverlayEvent; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraftforge.client.gui.overlay.VanillaGuiOverlay; - -import java.util.Comparator; - -@Mod.EventBusSubscriber(value = Dist.CLIENT) -public class CNClientEvent { - - @SubscribeEvent - public static void onRenderGuiOverlay(RenderGuiOverlayEvent.Post event) { - // On ne rend qu'après l'overlay "helmet" - if (!event.getOverlay().id().equals(VanillaGuiOverlay.HELMET.id())) return; - - GuiGraphics gfx = event.getGuiGraphics(); - float pt = event.getPartialTick(); - - HudOverlayRegistry.getAll().stream() - .filter(HudOverlay::isActive) - .filter(o -> o.getAfterOverlay().equals(VanillaGuiOverlay.HELMET.id())) - .sorted(Comparator.comparingInt(HudOverlay::getPriority).reversed()) - .forEach(o -> o.render(gfx, pt)); - } -} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/gui/CNGuiTextures.java b/src/main/java/net/nuclearteam/createnuclear/foundation/gui/CNGuiTextures.java index 186543d..4be1ed6 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/gui/CNGuiTextures.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/gui/CNGuiTextures.java @@ -24,19 +24,19 @@ public enum CNGuiTextures implements ScreenElement { public int width, height; public int startX, startY; - private CNGuiTextures(String location, int width, int height) { + CNGuiTextures(String location, int width, int height) { this(location, 0, 0, width, height); } - private CNGuiTextures(int startX, int startY) { + CNGuiTextures(int startX, int startY) { this("icons", startX * 16, startY * 16, 16, 16); } - private CNGuiTextures(String location, int startX, int startY, int width, int height) { + CNGuiTextures(String location, int startX, int startY, int width, int height) { this(CreateNuclear.MOD_ID, location, startX, startY, width, height); } - private CNGuiTextures(String namespace, String location, int startX, int startY, int width, int height) { + CNGuiTextures(String namespace, String location, int startX, int startY, int width, int height) { this.location = ResourceLocation.fromNamespaceAndPath(namespace, "textures/gui/" + location + ".png"); this.width = width; this.height = height; diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/item/DyedItemsList.java b/src/main/java/net/nuclearteam/createnuclear/foundation/item/DyedItemsList.java index 7cc8bbe..d52924a 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/item/DyedItemsList.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/item/DyedItemsList.java @@ -1,10 +1,8 @@ package net.nuclearteam.createnuclear.foundation.item; -import com.tterrag.registrate.util.entry.BlockEntry; import com.tterrag.registrate.util.entry.ItemEntry; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; import java.util.Arrays; import java.util.Iterator; @@ -13,12 +11,11 @@ public class DyedItemsList implements Iterable> { private static final int COLOR_AMOUNT = DyeColor.values().length; - private final ItemEntry[] values = new ItemEntry[COLOR_AMOUNT]; - public DyedItemsList(Function> filler) { + public DyedItemsList(Function> filter) { for (DyeColor color : DyeColor.values()) { - values[color.ordinal()] = filler.apply(color); + values[color.ordinal()] = filter.apply(color); } } @@ -54,8 +51,7 @@ public boolean hasNext() { @SuppressWarnings("unchecked") @Override public ItemEntry next() { - if (!hasNext()) - throw new NoSuchElementException(); + if (!hasNext()) throw new NoSuchElementException(); return (ItemEntry) values[index++]; } }; diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/SmithingTransformRecipeMixin.java b/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/SmithingTransformRecipeMixin.java new file mode 100644 index 0000000..a1a6834 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/SmithingTransformRecipeMixin.java @@ -0,0 +1,35 @@ +package net.nuclearteam.createnuclear.foundation.mixin; + +import net.minecraft.core.HolderLookup; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.SmithingRecipeInput; +import net.minecraft.world.item.crafting.SmithingTransformRecipe; +import net.nuclearteam.createnuclear.CNTags; +import net.nuclearteam.createnuclear.foundation.utility.ClothTagHelper; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(SmithingTransformRecipe.class) +public class SmithingTransformRecipeMixin { + + @Shadow + @Final + ItemStack result; + + @Inject(at = @At("HEAD"), method = "assemble", cancellable = true) + public void CN$assemble(SmithingRecipeInput pInput, HolderLookup.Provider pRegistries, CallbackInfoReturnable cir) { + ItemStack baseItem = pInput.base(); + if (baseItem.is(CNTags.CNItemTags.ANTI_RADIATION_ARMOR.tag)) { + ItemStack resultItem = this.result.copy(); + ItemStack additionItem = pInput.addition(); + + ClothTagHelper.initClothTagsForResult(resultItem, baseItem, additionItem); + + cir.setReturnValue(resultItem); + } + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/client/AntiRadiationArmorTextureMixin.java b/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/client/AntiRadiationArmorTextureMixin.java new file mode 100644 index 0000000..b81d05f --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/client/AntiRadiationArmorTextureMixin.java @@ -0,0 +1,31 @@ +package net.nuclearteam.createnuclear.foundation.mixin.client; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ArmorMaterial; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.client.ClientHooks; +import net.nuclearteam.createnuclear.CNDataComponents; +import net.nuclearteam.createnuclear.content.equipment.armor.AntiRadiationArmorItem; +import net.nuclearteam.createnuclear.foundation.utility.ClothTagHelper; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ClientHooks.class) +public class AntiRadiationArmorTextureMixin { + @Inject( + method = "getArmorTexture(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ArmorMaterial$Layer;ZLnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/resources/ResourceLocation;", + at = @At("HEAD"), + cancellable = true + ) + private static void CN$overrideClothTexture(Entity entity, ItemStack stack, ArmorMaterial.Layer layer, + boolean innerModel, EquipmentSlot slot, + CallbackInfoReturnable cir) { + if (stack.getItem() instanceof AntiRadiationArmorItem && stack.has(CNDataComponents.CLOTH_COLOR)) { + cir.setReturnValue(ClothTagHelper.getArmorTexturePath(stack, "anti_radiation_suit.png")); + } + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/client/RadiationHeartMixin.java b/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/client/RadiationHeartMixin.java new file mode 100644 index 0000000..a39870b --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/mixin/client/RadiationHeartMixin.java @@ -0,0 +1,40 @@ +package net.nuclearteam.createnuclear.foundation.mixin.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.nuclearteam.createnuclear.CNEffects; +import net.nuclearteam.createnuclear.CreateNuclear; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(Gui.class) +public class RadiationHeartMixin { + + @ModifyArg( + method = "renderHeart", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/GuiGraphics;blitSprite(Lnet/minecraft/resources/ResourceLocation;IIII)V" + ), + index = 0 + ) + private ResourceLocation CN$changeHeartTexture(ResourceLocation originalTexture) { + Player player = Minecraft.getInstance().player; + + if (player != null && player.hasEffect(CNEffects.RADIATION)) { + String path = originalTexture.getPath(); + if (originalTexture.getNamespace().equals("minecraft") && path.startsWith("hud/heart/")) { + String heartType = path.substring("hud/heart/".length()); + String baseType = heartType.replace("_blinking", ""); + + if (baseType.equals("full") || baseType.equals("half") || baseType.equals("hardcore_full") || baseType.equals("hardcore_half")) { + return CreateNuclear.asResource("hud/heart/radiation_" + baseType); + } + } + } + return originalTexture; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNPonderIndex.java b/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNPonderIndex.java index 3ded587..582bf4c 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNPonderIndex.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNPonderIndex.java @@ -1,29 +1,29 @@ package net.nuclearteam.createnuclear.foundation.ponder; - import com.tterrag.registrate.util.entry.ItemProviderEntry; import com.tterrag.registrate.util.entry.RegistryEntry; import net.createmod.ponder.api.registration.PonderSceneRegistrationHelper; import net.minecraft.resources.ResourceLocation; import net.nuclearteam.createnuclear.CNBlocks; import net.nuclearteam.createnuclear.CNItems; - +import net.nuclearteam.createnuclear.infrastructure.ponder.scenes.CNPonderReactorScenes; public class CNPonderIndex { public static void register(PonderSceneRegistrationHelper helper) { PonderSceneRegistrationHelper> HELPER = helper.withKeyFunction(RegistryEntry::getId); - // Reactor + // Reactor - Storyboards pour chaque taille HELPER.forComponents(CNBlocks.REACTOR_CONTROLLER) - .addStoryBoard("reactor/setup", CNPonderReactor::init) - .addStoryBoard("reactor/setup", CNPonderReactor::enable); - + .addStoryBoard("reactor/reactor_t1_ponder", CNPonderReactorScenes::t1) + .addStoryBoard("reactor/reactor_t2_ponder", CNPonderReactorScenes::t2) + .addStoryBoard("reactor/reactor_t3_ponder", CNPonderReactorScenes::t3) + .addStoryBoard("reactor/reactor_t1_ponder", CNPonderReactorScenes::ioPlacement); HELPER.forComponents(CNItems.REACTOR_BLUEPRINT) - .addStoryBoard("reactor/setup", CNPonderReactor::enable); - - ; - + .addStoryBoard("reactor/reactor_t1_ponder", CNPonderReactorScenes::t1) + .addStoryBoard("reactor/reactor_t2_ponder", CNPonderReactorScenes::t2) + .addStoryBoard("reactor/reactor_t3_ponder", CNPonderReactorScenes::t3) + .addStoryBoard("reactor/reactor_t1_ponder", CNPonderReactorScenes::ioPlacement); } } \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNPonderReactor.java b/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNPonderReactor.java deleted file mode 100644 index 4436ba4..0000000 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNPonderReactor.java +++ /dev/null @@ -1,103 +0,0 @@ -package net.nuclearteam.createnuclear.foundation.ponder; - -import net.createmod.catnip.math.Pointing; -import net.createmod.ponder.api.scene.SceneBuilder; -import net.createmod.ponder.api.scene.SceneBuildingUtil; -import net.createmod.ponder.foundation.element.InputWindowElement; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.nuclearteam.createnuclear.CNItems; -import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlock; - -public class CNPonderReactor { - - private static final BlockPos CONTROLLER = new BlockPos(4, 4, 6); - private static final BlockPos OUTPUT = new BlockPos(4, 1, 6); - private static final BlockPos INPUT = new BlockPos(4, 4, 2); - public static void init(SceneBuilder scene, SceneBuildingUtil util) { - scene.title("reactor", "Construction of the reactor"); - scene.configureBasePlate(0,0,9); - scene.showBasePlate(); - - - for (int y = 1; y < 8; y++) { - scene.overlay().showText(10) - .text("Floor " + y) - .attachKeyFrame() - .placeNearTarget(); - - for (int x = 1; x < 7; x++) { - for (int z = 1; z < 7; z++) { - scene.world().showSection(util.select().position(x,y,z), Direction.NORTH); - scene.idle(5); - if (x == 5 && z == 5 && y == 1) { - scene.rotateCameraY(180f); - } - if (x == INPUT.getX() && y == INPUT.getY() && z == INPUT.getZ()) { - scene.rotateCameraY(180f); - scene.overlay().showText(90) - .text("Reactor Input: A block that stores uranium and graphite rods to operate the reactor.") - .pointAt(util.vector().blockSurface(INPUT, Direction.UP)) - .attachKeyFrame() - .placeNearTarget(); - scene.idle(120); - scene.rotateCameraY(180f); - } - if (x == OUTPUT.getX() && y == OUTPUT.getY() && z == OUTPUT.getZ()) { - //AABB bb = new AABB(OUTPUT).inflate(1/16f, 0, 0); - scene.overlay().showText(90) - .text("Reactor Output: This is the block that outputs the energy (SU) generated by the reactor.") - .pointAt(util.vector().blockSurface(OUTPUT, Direction.UP)) - .attachKeyFrame() - .placeNearTarget(); - scene.idle(120); - } - if (x == CONTROLLER.getX() && y == CONTROLLER.getY() && z == CONTROLLER.getZ()){ - scene.overlay().showText(90) - .text("Reactor Controller: The most important block to operate the reactor; it handles all the calculations needed to generate power.") - .pointAt(util.vector().blockSurface(CONTROLLER, Direction.DOWN)) - .attachKeyFrame() - .placeNearTarget(); - scene.idle(120); - } - } - } - - } - } - - public static void enable(SceneBuilder scene, SceneBuildingUtil util) { - scene.title("reactor_blueprint", "Controller Activation"); - scene.configureBasePlate(0,0,9); - scene.world().showSection(util.select().layer(0), Direction.UP); - scene.showBasePlate(); - - scene.rotateCameraY(260f); - for (int y = 1; y < 8; y++) { - for (int x = 1; x < 7; x++) { - for (int z = 1; z < 7; z++) { - scene.world().showSection(util.select().position(x,y,z), Direction.NORTH); - } - } - } - // For more details, refer to the item's tooltip. - scene.idle(30); - scene.overlay() - .showText(90) - .text("Reactor Blueprint: the most important element; it allows the reactor to be configured according to specific models."); - - Vec3 topSide = util.vector().blockSurface(CONTROLLER, Direction.EAST); - - - scene.overlay() - .showControls(topSide, Pointing.UP, 90) - .withItem(CNItems.REACTOR_BLUEPRINT.asStack()) - .rightClick() - ; - - - scene.world().modifyBlock(CONTROLLER, s -> s.setValue(ReactorControllerBlock.ASSEMBLED, true), true); - } -} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CreateNuclearPonderPlugin.java b/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CreateNuclearPonderPlugin.java index 3172b1b..ef09769 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CreateNuclearPonderPlugin.java +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CreateNuclearPonderPlugin.java @@ -5,6 +5,7 @@ import net.createmod.ponder.api.registration.PonderTagRegistrationHelper; import net.minecraft.resources.ResourceLocation; import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.infrastructure.ponder.CNCreateNuclearPonderTags; public class CreateNuclearPonderPlugin implements PonderPlugin { @Override diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/utility/ClothTagHelper.java b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/ClothTagHelper.java new file mode 100644 index 0000000..72825e1 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/ClothTagHelper.java @@ -0,0 +1,43 @@ +package net.nuclearteam.createnuclear.foundation.utility; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.nuclearteam.createnuclear.CNDataComponents; +import net.nuclearteam.createnuclear.CNDataComponents.ClothItemStack; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.content.equipment.cloth.ClothItem; +import net.nuclearteam.createnuclear.content.equipment.cloth.ClothItem.Cloths; + +public final class ClothTagHelper { + private ClothTagHelper() {} + + public static void initClothTagsForResult(ItemStack resultItem, ItemStack baseItem, ItemStack additionItem) { + if (!resultItem.has(CNDataComponents.CLOTH_COLOR) && baseItem.has(CNDataComponents.CLOTH_COLOR)) { + resultItem.set(CNDataComponents.CLOTH_COLOR, baseItem.get(CNDataComponents.CLOTH_COLOR)); + } + + if (!resultItem.has(CNDataComponents.CLOTH_ITEM) && baseItem.has(CNDataComponents.CLOTH_ITEM)) { + resultItem.set(CNDataComponents.CLOTH_ITEM, baseItem.get(CNDataComponents.CLOTH_ITEM)); + } + + if (additionItem != null && additionItem.getItem() instanceof ClothItem clothItem) { + resultItem.set(CNDataComponents.CLOTH_COLOR, Cloths.of(clothItem.getColor())); + resultItem.set(CNDataComponents.CLOTH_ITEM, new ClothItemStack(additionItem.copy())); + } + } + + public static DyeColor getClothColor(ItemStack stack, DyeColor defaultColor) { + if (stack == null) return defaultColor; + Cloths cloth = stack.get(CNDataComponents.CLOTH_COLOR); + DyeColor color = cloth != null ? cloth.getDyeColor() : null; + return color != null ? color : defaultColor; + } + + public static ResourceLocation getArmorTexturePath(ItemStack stack, String armorFileName) { + DyeColor color = getClothColor(stack, null); + String prefix = color != null ? color.getSerializedName() : "default"; + String textureFile = prefix + "_" + armorFileName; + return CreateNuclear.asResource("textures/models/armor/" + textureFile); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/utility/ConfigValueResolver.java b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/ConfigValueResolver.java new file mode 100644 index 0000000..8829716 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/ConfigValueResolver.java @@ -0,0 +1,57 @@ +package net.nuclearteam.createnuclear.foundation.utility; + +import net.nuclearteam.createnuclear.CreateNuclear; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Function; + +/** + * Utility class for resolving and loading configuration values into a target set. + *

+ * This class provides a static method to map a collection of input values to a set of resolved values, + * using a provided resolver function. It handles errors and logs unknown or invalid values. + *

+ */ +public final class ConfigValueResolver { + private ConfigValueResolver() {} + + /** + * Loads and resolves values from a collection into a target set using the provided resolver function. + *

+ * The target set is cleared before loading. For each value in the input collection, the resolver function is applied. + * If the resolved value is not null, it is added to the target set. If the value is unknown or an exception occurs, + * an error is logged. + *

+ * + * Usage example: + *
+     * Set mySet = new HashSet<>();
+     * List configValues = Arrays.asList("A", "B", "C");
+     * ConfigValueResolver.loadValuesInSet(configValues, mySet, MyType::fromString);
+     * 
+ * + * @param values the collection of input values to resolve + * @param targetSet the set to populate with resolved values (will be cleared first) + * @param resolver the function to resolve each input value to the target type + * @param the input value type + * @param the target value type + */ + public static void loadValuesInSet(Collection values, Set targetSet, Function resolver) { + targetSet.clear(); + + for (I valueId : values) { + try { + T value = resolver.apply(valueId); + + if (value != null) { + targetSet.add(value); + } else { + CreateNuclear.LOGGER.error("Unknown value in config: {}", valueId); + } + } catch (Exception e) { + CreateNuclear.LOGGER.error("Unknown value in config: {} - {}", valueId, e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/utility/InventoryHashUtil.java b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/InventoryHashUtil.java new file mode 100644 index 0000000..ba04bd1 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/InventoryHashUtil.java @@ -0,0 +1,95 @@ +package net.nuclearteam.createnuclear.foundation.utility; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +/** + * Utility class for computing a compact long-valued hash that represents a + * {@link net.minecraft.world.entity.player.Player Player}'s inventory state. + * + *

The hash aggregates contributions from the player's main inventory, offhand + * slots, and armor slots. It is intended for fast change-detection, caching, + * or lightweight synchronization checks where a compact fingerprint is useful. + * This is not a cryptographic checksum and may collide for + * different inventories.

+ * + *

Implementation details: + * - Empty stacks contribute {@code 0}. + * - Each non-empty stack contributes a value computed from the numeric item id, + * stack count, and the NBT tag's {@code hashCode()} when present. Damage value + * is intentionally excluded: no radiation source reads it, so including it would + * only cause needless recomputation on every durability change. + * - Per-stack contributions are combined using a 31-multiplicative rolling scheme + * (similar to {@link String#hashCode()}).

+ * + *

Thread-safety: this class is stateless. Calling code must ensure the + * {@link Player} instance is not concurrently mutated if callers require + * perfectly consistent snapshots.

+ */ +public class InventoryHashUtil { + /** + * Computes a deterministic long value representing the contents of the + * provided player's inventory. + * + *

The method iterates the player's main inventory slots ({@code + * player.getInventory().items}), offhand slots ({@code + * player.getInventory().offhand}) and armor slots ({@code + * player.getArmorSlots()}) and combines each stack's contribution using a + * simple rolling scheme. Equal return values usually indicate identical + * inventory contents, but different inventories can collide.

+ * + * @param player the player whose inventory will be hashed; must not be + * {@code null} + * @return a long value derived from the contents of the player's + * inventory. This value is suitable for quick equality/change + * detection but is not collision-resistant. + * @throws NullPointerException if {@code player} is {@code null} + * @implNote The algorithm uses {@link Item#getId(net.minecraft.world.item.Item)} + * and basic stack properties (count, tag.hashCode()). It is + * intentionally simple and optimized for speed rather than + * cryptographic strength. + */ + public static long compute(Player player) { + long hash = 1; + + for (ItemStack stack : player.getInventory().items) { + hash = 31 * hash + stackHash(stack); + } + + for (ItemStack stack : player.getInventory().offhand) { + hash = 31 * hash + stackHash(stack); + } + + for (ItemStack stack : player.getArmorSlots()) { + hash = 31 * hash + stackHash(stack); + } + + return hash; + } + + /** + * Computes the contribution of a single {@link ItemStack} to the overall + * inventory hash. + * + *

Empty stacks return {@code 0}. For non-empty stacks the returned value + * encodes the numeric item id, the stack count, and, when applicable, the + * NBT tag's {@code hashCode()}.

+ * + * @param stack the item stack to hash; must not be {@code null} + * @return a {@code long} representing the stack's contribution; {@code 0} + * for empty stacks + * @throws NullPointerException if {@code stack} is {@code null} + * @see #compute(Player) + */ + private static long stackHash(ItemStack stack) { + if (stack.isEmpty()) return 0; + + long h = Item.getId(stack.getItem()); + h = 31 * h + stack.getCount(); + + h = 31 * h + stack.getComponents().hashCode(); + + return h; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/utility/Maths.java b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/Maths.java new file mode 100644 index 0000000..97c4693 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/Maths.java @@ -0,0 +1,147 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package net.nuclearteam.createnuclear.foundation.utility; + +import com.google.common.collect.Sets; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.QuartPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.levelgen.synth.SimplexNoise; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Climate; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +public class Maths { + public static final Direction[] HORIZONTAL_DIRECTIONS; + public static final Direction[] NOT_UP_DIRECTIONS; + public static final float HALF_SQRT_3; + public static final float QUARTER_PI = 0.7853982F; + + public Maths() { + } + + private static final SimplexNoise NOISE = new SimplexNoise(RandomSource.create(0)); + + public static float smin(float a, float b, float k) { + float h = Math.max(k - Math.abs(a - b), 0.0F) / k; + return Math.min(a, b) - h * h * k * 0.25F; + } + + public static float sampleNoise2D(int x, int z, float simplexSampleRate) { + return (float) NOISE.getValue((((float)x + simplexSampleRate) / simplexSampleRate), (((float)z + simplexSampleRate) / simplexSampleRate)); + } + + public static float sampleNoise3D(int x, int y, int z, float simplexSampleRate) { + return (float) NOISE.getValue((((float)x + simplexSampleRate) / simplexSampleRate), (((float)y + simplexSampleRate) / simplexSampleRate), (((float)z + simplexSampleRate) / simplexSampleRate)); + } + + public static float sampleNoise3D(float x, float y, float z, float simplexSampleRate) { + return (float) NOISE.getValue(((x + simplexSampleRate) / simplexSampleRate), ((y + simplexSampleRate) / simplexSampleRate), ((z + simplexSampleRate) / simplexSampleRate)); + } + + public static VoxelShape buildShape(VoxelShape... from) { + return (VoxelShape)Stream.of(from).reduce((v1, v2) -> { + return Shapes.join(v1, v2, BooleanOp.OR); + }).get(); + } + + public static float walkValue(float limbSwing, float limbSwingAmount, float speed, float offset, float degree, boolean inverse) { + return (float)(Math.cos((double)(limbSwing * speed + offset)) * (double)degree * (double)limbSwingAmount * (double)(inverse ? -1 : 1)); + } + + public static float approachRotation(float current, float target, float max) { + float f = Mth.wrapDegrees(target - current); + if (f > max) { + f = max; + } + + if (f < -max) { + f = -max; + } + + return Mth.wrapDegrees(current + f); + } + + public static Vec3 getGroundBelowPosition(BlockGetter level, Vec3 in) { + BlockPos pos; + for(pos = BlockPos.containing(in); pos.getY() > level.getMinBuildHeight() && level.getBlockState(pos).getCollisionShape(level, pos).isEmpty(); pos = pos.below()) { + } + + BlockState state = level.getBlockState(pos); + VoxelShape shape = state.getCollisionShape(level, pos); + float top; + if (shape.isEmpty()) { + top = 0.0F; + } else { + Vec3 modIn = new Vec3(in.x % 1.0, 1.0, in.z % 1.0); + Optional closest = shape.closestPointTo(modIn); + top = closest.isPresent() ? (float)((Vec3)closest.get()).y : 0.0F; + } + + return Vec3.upFromBottomCenterOf(pos, (double)top); + } + + public static Vec3 readVec3(FriendlyByteBuf buf) { + return new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble()); + } + + public static FriendlyByteBuf writeVec3(FriendlyByteBuf buf, Vec3 vec3) { + buf.writeDouble(vec3.x()); + buf.writeDouble(vec3.y()); + buf.writeDouble(vec3.z()); + return buf; + } + + public static float approachDegreesNoWrap(float from, float to, float by) { + float f = (to - from) % 360.0F; + return Mth.approach(from, from + f, by); + } + + public static float canyonStep(float heightScale, int scaleTo) { + int clampTo100 = (int)(heightScale * (float)scaleTo * (float)scaleTo); + return Mth.clamp((float)Math.round((float)clampTo100 / (float)scaleTo) / (float)scaleTo, 0.0F, 1.0F); + } + + public static Set> getBiomesWithinAtY(BiomeSource biomeSource, int x, int y, int z, int xzDist, Climate.Sampler sampler) { + int i = QuartPos.fromBlock(x - xzDist); + int j = QuartPos.fromBlock(y); + int k = QuartPos.fromBlock(z - xzDist); + int l = QuartPos.fromBlock(x + xzDist); + int j1 = QuartPos.fromBlock(z + xzDist); + int k1 = l - i + 1; + int i2 = j1 - k + 1; + Set> set = Sets.newHashSet(); + + for(int j2 = 0; j2 < i2; ++j2) { + for(int k2 = 0; k2 < k1; ++k2) { + int i3 = i + k2; + int k3 = k + j2; + set.add(biomeSource.getNoiseBiome(i3, j, k3, sampler)); + } + } + + return set; + } + + static { + HORIZONTAL_DIRECTIONS = new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST}; + NOT_UP_DIRECTIONS = new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.DOWN}; + HALF_SQRT_3 = (float)(Math.sqrt(3.0) / 2.0); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/utility/NotifyUtil.java b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/NotifyUtil.java new file mode 100644 index 0000000..d712be2 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/foundation/utility/NotifyUtil.java @@ -0,0 +1,155 @@ +package net.nuclearteam.createnuclear.foundation.utility; + +import net.createmod.catnip.lang.LangBuilder; +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket; +import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket; +import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Utility class for sending player notifications server-side. + * Supports Action Bar messages and full-screen title/subtitle overlays, + * with radius-based or server-wide targeting. + */ +public class NotifyUtil { + + /** + * Convenience overload that accepts a {@link LangBuilder} message, + * converting it to a {@link MutableComponent} before sending. + * + * @see #sendActionBar(Level, BlockPos, MutableComponent, ChatFormatting, int, boolean) + */ + public static void sendActionBar(Level level, BlockPos pos, LangBuilder message, ChatFormatting color, int radius, boolean warnAll) { + sendActionBar(level, pos, message.component(), color, radius, warnAll); + } + + /** + * Sends a bold colored message to the Action Bar (above the hotbar) of nearby players. + * No-ops on the client side. + * + * @param level The server-side world used to resolve players + * @param pos Center position used to compute the notification radius + * @param message The component text to display; will be styled bold with {@code color} + * @param color {@link ChatFormatting} color applied to the message + * @param radius Block radius around {@code pos} within which players are notified; + * ignored when {@code warnAll} is {@code true} + * @param warnAll If {@code true}, all online players receive the message regardless of position + */ + public static void sendActionBar(Level level, BlockPos pos, MutableComponent message, ChatFormatting color, int radius, boolean warnAll) { + if (level.isClientSide) return; + + // Build the text component in BOLD with the chosen color + Component actionBarComp = message.withStyle(color, ChatFormatting.BOLD); + + List targets = getTargetPlayers(level, pos, radius, warnAll); + + for (ServerPlayer player : targets) { + // The 'true' parameter routes the message to the Action Bar + player.displayClientMessage(actionBarComp, true); + } + } + + /** + * Convenience overload that accepts {@link LangBuilder} title and subtitle, + * converting them to {@link MutableComponent} before sending. + * + * @see #sendTitle(Level, BlockPos, MutableComponent, MutableComponent, ChatFormatting, int, boolean, int, int, int) + */ + public static void sendTitle(Level level, BlockPos pos, LangBuilder title, LangBuilder subtitle, ChatFormatting color, + int radius, boolean warnAll, int fadeIn, int stay, int fadeOut) { + sendTitle(level, pos, title.component(), subtitle.component(), color, radius, warnAll, fadeIn, stay, fadeOut); + } + + /** + * Sends a full-screen title and subtitle overlay to nearby players. + * The title is rendered bold with {@code color}; the subtitle is always white. + * No-ops on the client side. + * + * @param level The server-side world used to resolve players + * @param pos Center position used to compute the notification radius + * @param title Main title text displayed in large letters + * @param subtitle Subtitle text displayed below the title + * @param color {@link ChatFormatting} color applied to the title + * @param radius Block radius around {@code pos} within which players are notified; + * ignored when {@code warnAll} is {@code true} + * @param warnAll If {@code true}, all online players receive the message regardless of position + * @param fadeIn Ticks for the fade-in animation + * @param stay Ticks the title remains fully visible + * @param fadeOut Ticks for the fade-out animation + */ + public static void sendTitle(Level level, BlockPos pos, MutableComponent title, MutableComponent subtitle, ChatFormatting color, + int radius, boolean warnAll, int fadeIn, int stay, int fadeOut) { + + if (level.isClientSide) return; + + Component titleComp = title.withStyle(color, ChatFormatting.BOLD); + Component subtitleComp = subtitle.withStyle(ChatFormatting.WHITE); + + List targets = getTargetPlayers(level, pos, radius, warnAll); + + for (ServerPlayer serverPlayer : targets) { + serverPlayer.connection.send(new ClientboundSetTitlesAnimationPacket(fadeIn, stay, fadeOut)); + serverPlayer.connection.send(new ClientboundSetSubtitleTextPacket(subtitleComp)); + serverPlayer.connection.send(new ClientboundSetTitleTextPacket(titleComp)); + } + } + + /** + * Convenience overload that accepts {@link LangBuilder} title and subtitle, + * converting them to {@link MutableComponent} before sending. + * + * @see #quickAlert(Level, BlockPos, MutableComponent, MutableComponent, ChatFormatting, int, boolean) + */ + public static void quickAlert(Level level, BlockPos pos, LangBuilder title, LangBuilder sub, ChatFormatting color, int radius, boolean warnAll) { + quickAlert(level, pos, title.component(), sub.component(), color, radius, warnAll); + } + + + /** + * Shortcut for {@link #sendTitle} using default animation timings (fadeIn=10, stay=50, fadeOut=10). + * + * @param level The server-side world used to resolve players + * @param pos Center position used to compute the notification radius + * @param title Main title text + * @param sub Subtitle text + * @param color {@link ChatFormatting} color applied to the title + * @param radius Block radius around {@code pos}; ignored when {@code warnAll} is {@code true} + * @param warnAll If {@code true}, all online players receive the message + */ + public static void quickAlert(Level level, BlockPos pos, MutableComponent title, MutableComponent sub, ChatFormatting color, int radius, boolean warnAll) { + sendTitle(level, pos, title, sub, color, radius, warnAll, 10, 50, 10); + } + + /** + * Resolves the list of players to notify. + * Returns all online players if {@code warnAll} is {@code true}, + * otherwise returns only those within {@code radius} blocks of {@code pos}. + * + * @param level The server-side world + * @param pos Center position for the radius check + * @param radius Block radius for proximity filtering + * @param warnAll If {@code true}, bypasses the radius check + * @return Mutable list of matching {@link ServerPlayer} instances; empty if the server is unavailable + */ + private static List getTargetPlayers(Level level, BlockPos pos, int radius, boolean warnAll) { + if (level.getServer() == null) return List.of(); + + List allPlayers = level.getServer().getPlayerList().getPlayers(); + if (warnAll) return new ArrayList<>(allPlayers); + + AABB area = new AABB(pos).inflate(radius); + return allPlayers.stream() + .filter(p -> area.contains(p.getX(), p.getY(), p.getZ())) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/impl/registry/CreateNuclearRegistriesImpl.java b/src/main/java/net/nuclearteam/createnuclear/impl/registry/CreateNuclearRegistriesImpl.java new file mode 100644 index 0000000..5c5d6dd --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/impl/registry/CreateNuclearRegistriesImpl.java @@ -0,0 +1,36 @@ +package net.nuclearteam.createnuclear.impl.registry; + +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.registries.DataPackRegistryEvent; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.api.CreateNuclearRegistries; +import net.nuclearteam.createnuclear.api.multiblock.fluid.ReactorFluidType; +import net.nuclearteam.createnuclear.api.multiblock.rods.RodType; +import org.jetbrains.annotations.ApiStatus.Internal; + +/** + * Declares the mod's custom datapack registries. Without this, the registries have no codec + * registered, so datagen cannot clone them ("No cloner for createnuclear:fluids/type") and they + * would never be loaded from datapacks at runtime. + */ +@EventBusSubscriber(modid = CreateNuclear.MOD_ID) +public class CreateNuclearRegistriesImpl { + private CreateNuclearRegistriesImpl() {} + + @Internal + @SubscribeEvent + public static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) { + event.dataPackRegistry( + CreateNuclearRegistries.ROD_TYPE, + RodType.CODEC, + RodType.CODEC + ); + + event.dataPackRegistry( + CreateNuclearRegistries.FLUID_TYPE, + ReactorFluidType.CODEC, + ReactorFluidType.CODEC + ); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CBiomeRestore.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CBiomeRestore.java new file mode 100644 index 0000000..a91c2bb --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CBiomeRestore.java @@ -0,0 +1,23 @@ +package net.nuclearteam.createnuclear.infrastructure.config; + +import net.createmod.catnip.config.ConfigBase; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.nuclearteam.createnuclear.CNParticleTypes; + +@MethodsReturnNonnullByDefault +public class CBiomeRestore extends ConfigBase { + public final ConfigBool restoreInCircle = b(true, "restore_in_circle", Comments.restoreInCircle); + public final ConfigInt restoreRadiusChunks = i(4, 0, 32, "restore_radius_chunks", Comments.restoreRadiusChunks); + public final ConfigInt maxCharge = i(8, 1, 64, "maxCharge", Comments.maxCharge); + + @Override + public String getName() { + return "biomeRestore"; + } + + private static class Comments { + static String maxCharge = "Maximum number of charges the Biome Restore Cell can hold."; + static String restoreInCircle = "When enabled, restoring an irradiated biome also restores the surrounding chunks within restore_radius_chunks. When disabled, only the chunk being targeted is restored."; + static String restoreRadiusChunks = "Radius, in chunks, of the circular area restored around the targeted chunk. Only used when restore_in_circle is enabled."; + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCClient.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCClient.java index 0bbd899..77a2837 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCClient.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCClient.java @@ -1,4 +1,16 @@ package net.nuclearteam.createnuclear.infrastructure.config; -public class CNCClient { +import net.createmod.catnip.config.ConfigBase; + +public class CNCClient extends ConfigBase { + public final ConfigBool nuclearBombFlash = b(true, "nuclear_bomb_flash", Comments.nuclearBombFlash); + public final ConfigBool screenShaking = b(true, "screen_shake", Comments.screenShaking); + + @Override + public String getName() { return "client"; } + + private static class Comments { + static String nuclearBombFlash = "Enable the bright flash at the start of a nuclear explosion."; + static String screenShaking = "Enable screen shake effect during a nuclear explosion."; + } } diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCServer.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCServer.java index 6192b4d..5ae7b0b 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCServer.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNCServer.java @@ -1,4 +1,23 @@ package net.nuclearteam.createnuclear.infrastructure.config; -public class CNCServer { +import net.createmod.catnip.config.ConfigBase; + +public class CNCServer extends ConfigBase { + public final CRods rods = nested(0, CRods::new, Comments.rods); + public final CNotify notify = nested(0, CNotify::new, Comments.notify); + public final CRadiation radiation = nested(0, CRadiation::new, Comments.radiation); + public final CBiomeRestore biomeRestore = nested(0, CBiomeRestore::new, Comments.biomeRestore); + + @Override + public String getName() { + return "server"; + } + + private static class Comments { + static String rods = "Modify rod durations and related parameters."; + static String explode = "[WIP] Modify explosion parameters."; + static String radiation = "Enable or disable radiation effects emitted by mod items."; + static String notify = "Notification settings for reactor warnings."; + static String biomeRestore = "Control how irradiated biomes are restored back to their original biome."; + } } diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNConfigs.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNConfigs.java index 3b2518f..bca78d1 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNConfigs.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNConfigs.java @@ -15,18 +15,26 @@ import java.util.Map.Entry; import java.util.function.Supplier; -@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD) +@EventBusSubscriber() public class CNConfigs { private static final Map CONFIGS = new EnumMap<>(ModConfig.Type.class); private static CNCClient client; private static CNCCommon common; - private static CNCServer server; + public static CNCServer server; + + public static CNCClient client() { + return client; + } public static CNCCommon common() { return common; } + public static CNCServer server() { + return server; + } + public static ConfigBase byType(ModConfig.Type type) { return CONFIGS.get(type); } @@ -45,7 +53,9 @@ private static T register(Supplier factory, ModConfig. } public static void register(ModLoadingContext context, ModContainer container) { + client = register(CNCClient::new, ModConfig.Type.CLIENT); common = register(CNCCommon::new, ModConfig.Type.COMMON); + server = register(CNCServer::new, ModConfig.Type.SERVER); for (Entry pair : CONFIGS.entrySet()) container.registerConfig(pair.getKey(), pair.getValue().specification); diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNotify.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNotify.java new file mode 100644 index 0000000..e53a67c --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CNotify.java @@ -0,0 +1,19 @@ +package net.nuclearteam.createnuclear.infrastructure.config; + +import net.createmod.catnip.config.ConfigBase; + +public class CNotify extends ConfigBase { + + public final ConfigBool warnAllPlayers = b(false, "warn_all_players", Comments.warning); + public final ConfigInt warningDistance = i(100, 30, 5000, "warning_distance_blocks", Comments.rangeOfWarning); + + @Override + public String getName() { + return "Notify"; + } + + private static class Comments { + static String warning = "When true, broadcast a warning to nearby players before a reactor explosion."; + static String rangeOfWarning = "Distance (in blocks) to search for players to warn about impending explosion."; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CRadiation.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CRadiation.java new file mode 100644 index 0000000..892d91d --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/config/CRadiation.java @@ -0,0 +1,70 @@ +package net.nuclearteam.createnuclear.infrastructure.config; + +import net.createmod.catnip.config.ConfigBase; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.neoforged.neoforge.common.ModConfigSpec; +import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; +import java.util.List; + +@MethodsReturnNonnullByDefault +public class CRadiation extends ConfigBase { + public final ConfigBool enabledItemRadiation = b(true, "enabled_item_radiation", Comments.enabled); + public final ConfigInt radiationLevel1 = i(10, 0, 50, "radiation_level_1", Comments.radiationLevel1); + public final ConfigInt radiationLevel2 = i(25, 0, 50, "radiation_level_2", Comments.radiationLevel2); + public final ConfigInt radiationLevel3 = i(50, 0, 50, "radiation_level_3", Comments.radiationLevel3); + public final ConfigInt amplifierLevel0 = i(0, 0, 10, "amplifier_level_0", Comments.amplifierLevel0); + public final ConfigInt amplifierLevel1 = i(1, 0, 10, "amplifier_level_1", Comments.amplifierLevel1); + public final ConfigInt amplifierLevel2 = i(2, 0, 10, "amplifier_level_2", Comments.amplifierLevel2); + public final ConfiguredLists configuredLists = nested(0, ConfiguredLists::new, Comments.list); + + @Override + public String getName() { + return "Radiation"; + } + + private static class Comments { + static String enabled = "When enabled, certain items may emit radiation affecting players and nearby entities. Disable to turn off radiation effects without removing items."; + static String radiationLevel1 = "Minimum radiation value required to apply Radiation I. Values below this do not apply effects."; + static String radiationLevel2 = "Minimum radiation value required to apply Radiation II. Should be greater than radiation_level_1."; + static String radiationLevel3 = "Minimum radiation value required to apply Radiation III. Should be greater than radiation_level_2."; + static String amplifierLevel0 = "Effect amplifier for Radiation I (0 = level I)."; + static String amplifierLevel1 = "Effect amplifier for Radiation II (1 = level II)."; + static String amplifierLevel2 = "Effect amplifier for Radiation III (2 = level III)."; + static String list = "Lists related to radiation configuration, for example the entity blacklist."; + static String blackListEntity = + "List of entity IDs excluded from radiation effects. Use registry names like \"minecraft:armor_stand\".\n\n" + + "How to edit:\n" + + "- Add entries as namespace:entity_name strings.\n" + + "- Example: \"minecraft:armor_stand\"\n" + + "- Remove an entry to stop excluding it.\n"; + } + + public static class ConfiguredLists extends ConfigBase { + private static ConfigValue> ENTITY_BLACKLIST = null; + + @Override + public void registerAll(ModConfigSpec.Builder builder) { + + ENTITY_BLACKLIST = builder + .comment(Comments.blackListEntity) + .defineListAllowEmpty( + List.of("entity_blacklist"), + () -> List.of( + "minecraft:armor_stand", + "minecraft:item_frame", + "minecraft:glow_item_frame" + ), + obj -> obj instanceof String + ); + } + + @Override + public String getName() { + return "lists"; + } + + public List getEntityBlackList() { + return ENTITY_BLACKLIST.get(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearDatagen.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearDatagen.java index dc4c146..77fab59 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearDatagen.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearDatagen.java @@ -12,6 +12,7 @@ import net.minecraft.data.PackOutput; import net.neoforged.neoforge.common.data.ExistingFileHelper; import net.neoforged.neoforge.data.event.GatherDataEvent; +import net.nuclearteam.createnuclear.CNSoundEvents; import net.nuclearteam.createnuclear.CreateNuclear; import net.nuclearteam.createnuclear.foundation.advancement.CNAdvancement; import net.nuclearteam.createnuclear.foundation.data.recipe.CNMechanicalCraftingRecipeGen; @@ -50,7 +51,8 @@ public static void gatherData(GatherDataEvent event) { // generator.addProvider(event.includeServer(), new CNShapelessRecipeGen(output, lookupProvider)); - generator.addProvider(event.includeClient(), new CNAdvancement(output, lookupProvider)); + generator.addProvider(event.includeServer(), new CNAdvancement(output, lookupProvider)); + generator.addProvider(event.includeClient(), CNSoundEvents.provider(generator)); if (event.includeServer()) { CNRecipeProvider.registerAllProcessing(generator, output, lookupProvider); @@ -68,6 +70,7 @@ private static void addExtraRegistrateData() { provideDefaultLang("tooltips", langConsumer); provideDefaultLang("reactor", langConsumer); CNAdvancement.provideLang(langConsumer); + CNSoundEvents.provideLang(langConsumer); providePonderLang(langConsumer); }); } diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearRegistrateTags.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearRegistrateTags.java index f4f6602..9d8cf74 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearRegistrateTags.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/CreateNuclearRegistrateTags.java @@ -30,10 +30,6 @@ public static void addGenerators() { private static void genBlockTags(RegistrateTagsProvider provIn) { CreateTagsProvider prov = new CreateTagsProvider<>(provIn, Block::builtInRegistryHolder); - prov.tag(BlockTags.CAMPFIRES) - .add(CNBlocks.ENRICHING_CAMPFIRE.get()) - ; - prov.tag(BlockTags.FIRE) .add(CNBlocks.ENRICHING_FIRE.get()) ; diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/GeneratedEntriesProvider.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/GeneratedEntriesProvider.java index 7ec248a..7bf61c1 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/GeneratedEntriesProvider.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/data/GeneratedEntriesProvider.java @@ -9,9 +9,16 @@ import net.neoforged.neoforge.registries.NeoForgeRegistries; import net.nuclearteam.createnuclear.CNDamageTypes; import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.api.CreateNuclearRegistries; +import net.nuclearteam.createnuclear.content.multiblock.fluid.CNReactorFluidTypes; +import net.nuclearteam.createnuclear.content.rod.CNRodTypes; import net.nuclearteam.createnuclear.infrastructure.worldgen.CNBiomeModifiers; import net.nuclearteam.createnuclear.infrastructure.worldgen.CNConfiguredFeatures; import net.nuclearteam.createnuclear.infrastructure.worldgen.CNPlacedFeatures; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.CNBiomes; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.CNDensityFunctions; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.CNNoiseData; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.CNNoiseGeneratorSettings; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -23,6 +30,12 @@ public class GeneratedEntriesProvider extends DatapackBuiltinEntriesProvider { .add(Registries.CONFIGURED_FEATURE, (RegistryBootstrap) CNConfiguredFeatures::bootstrap) .add(Registries.PLACED_FEATURE, CNPlacedFeatures::bootstrap) .add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, CNBiomeModifiers::bootstrap) + .add(Registries.BIOME, CNBiomes::bootstrapRegistries) + .add(Registries.DENSITY_FUNCTION, CNDensityFunctions::bootstrapRegistries) + .add(Registries.NOISE_SETTINGS, CNNoiseGeneratorSettings::bootstrapRegistries) + .add(Registries.NOISE, CNNoiseData::bootstrapRegistries) + .add(CreateNuclearRegistries.ROD_TYPE, CNRodTypes::bootstrap) + .add(CreateNuclearRegistries.FLUID_TYPE, CNReactorFluidTypes::bootstrap) ; public GeneratedEntriesProvider(PackOutput output, CompletableFuture registries) { diff --git a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNCreateNuclearPonderTags.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/ponder/CNCreateNuclearPonderTags.java similarity index 83% rename from src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNCreateNuclearPonderTags.java rename to src/main/java/net/nuclearteam/createnuclear/infrastructure/ponder/CNCreateNuclearPonderTags.java index 2763f80..d1377d3 100644 --- a/src/main/java/net/nuclearteam/createnuclear/foundation/ponder/CNCreateNuclearPonderTags.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/ponder/CNCreateNuclearPonderTags.java @@ -1,4 +1,4 @@ -package net.nuclearteam.createnuclear.foundation.ponder; +package net.nuclearteam.createnuclear.infrastructure.ponder; import com.tterrag.registrate.util.entry.RegistryEntry; import net.createmod.catnip.registry.RegisteredObjectsHelper; @@ -8,6 +8,7 @@ import net.nuclearteam.createnuclear.CNBlocks; import net.nuclearteam.createnuclear.CreateNuclear; +import static com.simibubi.create.infrastructure.ponder.AllCreatePonderTags.DISPLAY_SOURCES; import static com.simibubi.create.infrastructure.ponder.AllCreatePonderTags.KINETIC_SOURCES; @@ -31,6 +32,10 @@ public static void register(PonderTagRegistrationHelper helper .add(CNBlocks.REACTOR_CONTROLLER) ; + HELPER.addToTag(DISPLAY_SOURCES) + .add(CNBlocks.REACTOR_CONTROLLER) + ; + } } diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/ponder/scenes/CNPonderReactorScenes.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/ponder/scenes/CNPonderReactorScenes.java new file mode 100644 index 0000000..755cb70 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/ponder/scenes/CNPonderReactorScenes.java @@ -0,0 +1,348 @@ +package net.nuclearteam.createnuclear.infrastructure.ponder.scenes; + +import net.createmod.catnip.math.Pointing; +import net.createmod.ponder.api.scene.SceneBuilder; +import net.createmod.ponder.api.scene.SceneBuildingUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.phys.Vec3; +import net.nuclearteam.createnuclear.CNBlocks; +import net.nuclearteam.createnuclear.CNItems; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlock; + +import java.util.HashMap; +import java.util.Map; + +public class CNPonderReactorScenes { + + // multiblock sizes and heights for tiers + private static final int S_T1 = 5; + private static final int H_T1 = 7; + private static final int S_T2 = 7; + private static final int H_T2 = 9; + private static final int S_T3 = 9; + private static final int H_T3 = 11; + + private static final int PEDESTAL_Y = 0; // pedestal layer (snow/concrete) + private static final int MULTIBLOCK_BASE_Y = 1; // multiblock starts at y=1 + private static final int MARGIN = 2; // Create margin + private static int plateSizeFor(int multiblockSize) { return multiblockSize + MARGIN * 2; } + + /** + * Simple container for storing all positions of interest for a multiblock. * The BlockPos values here must be in SCENE COORDINATES (0..plate-1 for X/Z, 1..H for Y). + */ + private record Positions(BlockPos controller, BlockPos input1, BlockPos input2, BlockPos liquidInput, + BlockPos alarm, BlockPos output) { + + /** + * All blocks that carry an explanatory callout, so a section-based reveal can show them per layer. + */ + BlockPos[] specials() { + return new BlockPos[]{controller, input1, input2, liquidInput, alarm, output}; + } + } + + /** Describes a single callout: its text and the direction to point at. */ + private record CalloutInfo(String text, Direction pointDirection, int fullDuration, int reducedDuration, int fullIdle, int reducedIdle) {} + + /** + * Static map of positions per multiblock size. + * Key = multiblockSize (5 for S_T1, 7 for S_T2, 9 for S_T3). + * Rule: x/z in 0..plate-1, y in 1..H; these values must match the structure's actual NBT layout. + */ + private static final Map STATIC_POS = new HashMap<>(); + static { + // Example for T1 (multiblockSize = 5) + // plate = 5 + 4 = 9 => valid x/z: 0..8; inner multiblock: 2..6 + STATIC_POS.put(5, new Positions( + new BlockPos(4, 4, 6), // controller + new BlockPos(3, 2, 6), // input1 + new BlockPos(3, 3, 6), // input2 + new BlockPos(3, 4, 6), // liquidInput + new BlockPos(5, 4, 6), // alarm + new BlockPos(5, 2, 6) // output + )); + + // Example for T2 (multiblockSize = 7) + STATIC_POS.put(7, new Positions( + new BlockPos(5, 5, 8), // controller + new BlockPos(3, 3, 8), // input1 + new BlockPos(3, 4, 8), // input2 + new BlockPos(3, 5, 8), // liquidInput + new BlockPos(7, 5, 8), // alarm + new BlockPos(7, 3, 8) // output + )); + + // Example for T3 (multiblockSize = 9) + STATIC_POS.put(9, new Positions( + new BlockPos(6, 6, 10), // controller + new BlockPos(5, 4, 10), // input1 + new BlockPos(5, 5, 10), // input2 + new BlockPos(5, 6, 10), // liquidInput + new BlockPos(7, 6, 10), // alarm + new BlockPos(7, 4, 10) // output + )); + } + + /** + * Precomputes a BlockPos -> CalloutInfo map for O(1) lookup per cell instead of + * iterating pos.specials() and comparing coordinates for every (x, y, z) visited. + */ + private static Map buildCallouts(Positions pos) { + Map callouts = new HashMap<>(); + callouts.put(pos.controller, new CalloutInfo( + "Controller: brain of the reactor, and the place where the blueprint goes to start it", + Direction.DOWN, + 130, 110, + 140, 120 + )); + + // Extend here if input1/input2/liquidInput/alarm/output ever need a callout + // during structure reveal (they currently don't have one — see ioPlacement() + // for their dedicated explanations). + return callouts; + } + + /** * Returns the static positions for the given size. * If no static entry exists, returns "reasonable" computed positions instead. */ + private static Positions positionsFor(int multiblockSize, int height) { + Positions p = STATIC_POS.get(multiblockSize); + if (p != null) return p; + + // fallback: simple centered calculation (avoids an NPE) + int plate = plateSizeFor(multiblockSize); + int offset = MARGIN; + int cx = offset + (multiblockSize / 2); + int cz = offset + (multiblockSize / 2); + int cy = MULTIBLOCK_BASE_Y + (height / 2); + return new Positions( + new BlockPos(cx, cy, cz), // controller + new BlockPos(cx, MULTIBLOCK_BASE_Y+1, offset + multiblockSize - 1), // input1 front + new BlockPos(cx, Math.min(MULTIBLOCK_BASE_Y+height-1, MULTIBLOCK_BASE_Y+2), offset + multiblockSize - 1), // input2 + new BlockPos(cx, cy, offset), // liquidInput back + new BlockPos(Math.max(offset, cx-1), cy, cz), // alarm + new BlockPos(offset + multiblockSize - 1, cy, cz) // output right + ); + } + + private static void showReactorStructure(SceneBuilder scene, SceneBuildingUtil util, int S, int H, int plate) { + showReactorStructure(scene, util, S, H, plate, false); + } + + /** + * @param bySections when true, each floor is revealed as a single render section instead of + * block-by-block. Block-by-block creates one {@code WorldSectionElement} per cell + * ({@code H * plate^2}, e.g. 1859 for T3) and they all keep rendering every frame, which + * is what makes the larger tiers lag past ~2/3 of the scene. T1/T2 stay block-by-block. + */ + private static void showReactorStructure(SceneBuilder scene, SceneBuildingUtil util, int S, int H, int plate, boolean bySections) { + Positions pos = positionsFor(S, H); + Map callouts = buildCallouts(pos); + + int minX = MARGIN; + int maxX = MARGIN + S - 1; + int minZ = MARGIN; + int maxZ = MARGIN + S - 1; + int minY = 1; + int maxY = H; + + scene.idle(20); + + for (int y = 1; y <= H; y++) { + scene.overlay().showText(bySections ? 25 : 8) + .text("Floor " + y) + .attachKeyFrame() + .placeNearTarget(); + + if (bySections) { + // One section for the whole floor: same visual build-up, a fraction of the draws. + scene.world().showSection(util.select().layer(y), Direction.NORTH); + // Block-by-block got its per-floor pacing from plate^2 * idle(4). With a single + // section per floor we must idle explicitly, otherwise callout-free floors flash + // by instantly and their "Floor N" labels overlap each other. + scene.idle(50); + for (Map.Entry entry : callouts.entrySet()) { + if (entry.getKey().getY() == y) { + showCallout(scene, util, entry.getKey(), entry.getValue(), bySections); + } + } + } else { + for (int x = 0; x < plate; x++) { + for (int z = 0; z < plate; z++) { + BlockPos cell = new BlockPos(x, y, z); + scene.world().showSection(util.select().position(x, y, z), Direction.NORTH); + scene.idle(4); + CalloutInfo info = callouts.get(cell); + if (info != null) { + showCallout(scene, util, cell, info, bySections); + } + } + } + } + } + + scene.idle(20); + scene.overlay() + .showText(110) + .text("To start the reactor you will need liquid and rods corresponding to the pattern, then right click the controller with the blueprint in hand"); + Vec3 topSide = util.vector().blockSurface(pos.controller, Direction.EAST); + scene.overlay() + .showControls(topSide, Pointing.UP, 60) + .withItem(CNItems.REACTOR_BLUEPRINT.asStack()) + .rightClick(); + scene.world().modifyBlock(pos.controller, s -> s.setValue(ReactorControllerBlock.ASSEMBLED, true), true); + + scene.idle(120); + scene.overlay() + .showText(120) + .text("Note: Check page 4 for I/O and Alarm blocks (placement & function).") + .attachKeyFrame() + .placeNearTarget(); + scene.idle(130); + } + + private static void showCallout(SceneBuilder scene, SceneBuildingUtil util, BlockPos at, CalloutInfo info, boolean reduced) { + scene.overlay().showText(reduced ? info.reducedDuration() : info.fullDuration()) + .text(info.text()) + .pointAt(util.vector().blockSurface(at, info.pointDirection())) + .attachKeyFrame() + .placeNearTarget(); + scene.idle(reduced ? info.reducedIdle() : info.fullIdle()); + } + + public static void t1(SceneBuilder scene, SceneBuildingUtil util) { + int S = S_T1; + int plate = plateSizeFor(S); + scene.title("reactor_t1","Reactor T1"); + scene.configureBasePlate(0, 0, plate); + scene.world().showSection(util.select().layer(0), Direction.UP); + scene.showBasePlate(); + scene.rotateCameraY(180); + scene.scaleSceneView(0.55f); + showReactorStructure(scene, util, S, H_T1, plate, true); + } + + public static void t2(SceneBuilder scene, SceneBuildingUtil util) { + int S = S_T2; + int plate = plateSizeFor(S); + scene.title("reactor_t2","Reactor T2"); + scene.configureBasePlate(0, 0, plate); + scene.world().showSection(util.select().layer(0), Direction.UP); + scene.showBasePlate(); + scene.rotateCameraY(180); + scene.scaleSceneView(0.45f); + showReactorStructure(scene, util, S, H_T2, plate, true); + } + + public static void t3(SceneBuilder scene, SceneBuildingUtil util) { + int S = S_T3; + int plate = plateSizeFor(S); + scene.title("reactor_t3","Reactor T3"); + scene.configureBasePlate(0, 0, plate); + scene.world().showSection(util.select().layer(0), Direction.UP); + scene.showBasePlate(); + scene.rotateCameraY(180); + scene.scaleSceneView(0.35f); + // T3 reveals floor-by-floor (section-based) so the largest tier doesn't lag; T1/T2 stay block-by-block. + showReactorStructure(scene, util, S, H_T3, plate, true); + } + + public static void ioPlacement(SceneBuilder scene, SceneBuildingUtil util) { + int S = S_T1; + int plate = plateSizeFor(S); + Positions pos = positionsFor(S, H_T1); + + scene.title("reactor_io_placement", "Reactor I/O: Function & Placement"); + scene.configureBasePlate(0, 0, plate); + scene.world().showSection(util.select().layer(0), Direction.UP); + scene.showBasePlate(); + scene.rotateCameraY(180); + scene.scaleSceneView(0.55f); + + // Populate the correct I/O blocks at the beginning because the schematic has old broken IDs + scene.world().modifyBlock(pos.input1, s -> CNBlocks.REACTOR_ROD_INPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.SOUTH), false); + scene.world().modifyBlock(pos.input2, s -> CNBlocks.REACTOR_ROD_INPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.SOUTH), false); + scene.world().modifyBlock(pos.liquidInput, s -> CNBlocks.REACTOR_FLUID_INPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.SOUTH), false); + scene.world().modifyBlock(pos.alarm, s -> CNBlocks.REACTOR_ALARM.get().defaultBlockState(), false); + scene.world().modifyBlock(pos.output, s -> CNBlocks.REACTOR_OUTPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.SOUTH), false); + + // Show the entire T1 structure instantly + scene.world().showSection(util.select().layersFrom(1), Direction.UP); + scene.idle(20); + + // Show Input 1 + scene.overlay().showText(150) + .text("Rod Input: Stores Heating or Cooling Rods. We need one Heating Rod (fuel) to heat the reactor, and one Cooling Rod for stability.") + .pointAt(util.vector().blockSurface(pos.input1, Direction.SOUTH)) + .attachKeyFrame() + .placeNearTarget(); + scene.idle(160); + + // Show Input 2 + scene.overlay().showText(150) + .text("Second Rod Input: This is why we need at least two inputs! One for the Heating Rod and one for the Cooling Rod to prevent explosions.") + .pointAt(util.vector().blockSurface(pos.input2, Direction.SOUTH)) + .attachKeyFrame() + .placeNearTarget(); + scene.idle(160); + + // Show Fluid Input + scene.overlay().showText(150) + .text("Fluid Input: Fluid is also required to cool the reactor and ensure it does not explode.") + .pointAt(util.vector().blockSurface(pos.liquidInput, Direction.SOUTH)) + .attachKeyFrame() + .placeNearTarget(); + scene.idle(160); + + // Show Alarm + scene.overlay().showText(150) + .text("Alarm (Optional): Emits a sound when the reactor becomes unstable and risks exploding.") + .pointAt(util.vector().blockSurface(pos.alarm, Direction.SOUTH)) + .attachKeyFrame() + .placeNearTarget(); + scene.idle(160); + + // Show Output + scene.overlay().showText(160) + .text("Output: Necessary to extract generated energy. You can place multiple outputs to dispatch the total energy between them.") + .pointAt(util.vector().blockSurface(pos.output, Direction.SOUTH)) + .attachKeyFrame() + .placeNearTarget(); + scene.idle(170); + + // --- Placement --- + scene.overlay().showText(120) + .text("These blocks can replace ANY Reactor Casing, as long as it is not on an edge.") + .attachKeyFrame() + .placeNearTarget(); + scene.idle(130); + + // Demonstrate moving the blocks to the roof (y=7 for T1) + BlockPos newIn1 = new BlockPos(3, 7, 3); + BlockPos newIn2 = new BlockPos(3, 7, 4); + BlockPos newLiq = new BlockPos(3, 7, 5); + BlockPos newAlm = new BlockPos(5, 7, 3); + BlockPos newOut = new BlockPos(5, 7, 5); + + // Replace old pos with casing + scene.world().modifyBlock(pos.input1, s -> CNBlocks.REACTOR_CASING.get().defaultBlockState(), true); + scene.world().modifyBlock(pos.input2, s -> CNBlocks.REACTOR_CASING.get().defaultBlockState(), true); + scene.world().modifyBlock(pos.liquidInput, s -> CNBlocks.REACTOR_CASING.get().defaultBlockState(), true); + scene.world().modifyBlock(pos.alarm, s -> CNBlocks.REACTOR_CASING.get().defaultBlockState(), true); + scene.world().modifyBlock(pos.output, s -> CNBlocks.REACTOR_CASING.get().defaultBlockState(), true); + + // Place I/O on roof + scene.world().modifyBlock(newIn1, s -> CNBlocks.REACTOR_ROD_INPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.UP), true); + scene.world().modifyBlock(newIn2, s -> CNBlocks.REACTOR_ROD_INPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.UP), true); + scene.world().modifyBlock(newLiq, s -> CNBlocks.REACTOR_FLUID_INPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.UP), true); + // Note: ReactorAlarm might not have FACING. Using default state. + scene.world().modifyBlock(newAlm, s -> CNBlocks.REACTOR_ALARM.get().defaultBlockState(), true); + scene.world().modifyBlock(newOut, s -> CNBlocks.REACTOR_OUTPUT.get().defaultBlockState().setValue(BlockStateProperties.FACING, Direction.UP), true); + + scene.overlay().showText(100) + .text("They can be grouped together on the roof or any other flat surface.") + .pointAt(util.vector().blockSurface(newIn2, Direction.UP)) + .placeNearTarget(); + scene.idle(110); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNBiomeModifiers.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNBiomeModifiers.java index 3d593d0..96db209 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNBiomeModifiers.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNBiomeModifiers.java @@ -1,6 +1,5 @@ package net.nuclearteam.createnuclear.infrastructure.worldgen; -import com.simibubi.create.infrastructure.worldgen.AllPlacedFeatures; import net.minecraft.core.Holder; import net.minecraft.core.HolderGetter; import net.minecraft.core.HolderSet; @@ -20,6 +19,8 @@ public class CNBiomeModifiers { public static final ResourceKey URANIUM_ORE = key("uranium_ore"), LEAD_ORE = key("lead_ore"), + THORIUM_ORE = key("thorium_ore"), + NITRATE_ORE = key("nitrate_ore"), STRIATED_ORES_OVERWORLD = key("striated_ores_overworld") ; @@ -34,10 +35,14 @@ public static void bootstrap(BootstrapContext ctx) { HolderGetter featureLookup = ctx.lookup(Registries.PLACED_FEATURE); Holder uraniumOre = featureLookup.getOrThrow(CNPlacedFeatures.URANIUM_ORE); Holder leadOre = featureLookup.getOrThrow(CNPlacedFeatures.LEAD_ORE); + Holder thoriumOre = featureLookup.getOrThrow(CNPlacedFeatures.THORIUM_ORE); + Holder nitrateOre = featureLookup.getOrThrow(CNPlacedFeatures.NITRATE_ORE); Holder striatedOresOverworld = featureLookup.getOrThrow(CNPlacedFeatures.STRIATED_ORES_OVERWORLD); ctx.register(URANIUM_ORE, addOre(isOverworld, uraniumOre)); ctx.register(LEAD_ORE, addOre(isOverworld, leadOre)); + ctx.register(THORIUM_ORE, addOre(isOverworld, thoriumOre)); + ctx.register(NITRATE_ORE, addOre(isOverworld, nitrateOre)); ctx.register(STRIATED_ORES_OVERWORLD, addOre(isOverworld, striatedOresOverworld)); } diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNConfiguredFeatures.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNConfiguredFeatures.java index 4ef17fa..bec2717 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNConfiguredFeatures.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNConfiguredFeatures.java @@ -23,6 +23,8 @@ public class CNConfiguredFeatures { public static final ResourceKey> URANIUM_ORE = key("uranium_ore"), LEAD_ORE = key("lead_ore"), + THORIUM_ORE = key("thorium_ore"), + NITRATE_ORE = key("nitrate_ore"), STRIATED_ORES_OVERWORLD = key("striated_ores_overworld") ; @@ -38,15 +40,25 @@ public static void bootstrap(BootstrapContext> ctx) { OreConfiguration.target(stoneOreReplaceable, CNBlocks.URANIUM_ORE.getDefaultState()), OreConfiguration.target(deepslateOreReplaceables, CNBlocks.DEEPSLATE_URANIUM_ORE.getDefaultState()) ); - register(ctx, URANIUM_ORE, Feature.ORE, new OreConfiguration(uraniumTargetStates, 7)); List leadTargetStates = List.of( OreConfiguration.target(stoneOreReplaceable, CNBlocks.LEAD_ORE.getDefaultState()), OreConfiguration.target(deepslateOreReplaceables, CNBlocks.DEEPSLATE_LEAD_ORE.getDefaultState()) ); + register(ctx, LEAD_ORE, Feature.ORE, new OreConfiguration(leadTargetStates, 10)); + + List thoriumTargetStates = List.of( + OreConfiguration.target(stoneOreReplaceable, CNBlocks.THORIUM_ORE.getDefaultState()), + OreConfiguration.target(deepslateOreReplaceables, CNBlocks.DEEPSLATE_THORIUM_ORE.getDefaultState()) + ); + register(ctx, THORIUM_ORE, Feature.ORE, new OreConfiguration(thoriumTargetStates, 16)); - register(ctx, LEAD_ORE, Feature.ORE, new OreConfiguration(leadTargetStates, 7)); + List nitrateTargetStates = List.of( + OreConfiguration.target(stoneOreReplaceable, CNBlocks.NITRATE_ORE.getDefaultState()), + OreConfiguration.target(deepslateOreReplaceables, CNBlocks.DEEPSLATE_NITRATE_ORE.getDefaultState()) + ); + register(ctx, NITRATE_ORE, Feature.ORE, new OreConfiguration(nitrateTargetStates, 10)); List overworldLayerPatterns = List.of( CNLayerPatterns.AUTUNITE.get() diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNPlacedFeatures.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNPlacedFeatures.java index 4647131..c1e5675 100644 --- a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNPlacedFeatures.java +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/CNPlacedFeatures.java @@ -19,6 +19,8 @@ public class CNPlacedFeatures { public static final ResourceKey URANIUM_ORE = key("uranium_ore"), LEAD_ORE = key("lead_ore"), + THORIUM_ORE = key("thorium_ore"), + NITRATE_ORE = key("nitrate_ore"), STRIATED_ORES_OVERWORLD = key("striated_ores_overworld") ; @@ -30,10 +32,14 @@ public static void bootstrap(BootstrapContext ctx) { HolderGetter> featureLookup = ctx.lookup(Registries.CONFIGURED_FEATURE); Holder> uraniumOre = featureLookup.getOrThrow(CNConfiguredFeatures.URANIUM_ORE); Holder> leadOre = featureLookup.getOrThrow(CNConfiguredFeatures.LEAD_ORE); + Holder> thoriumOre = featureLookup.getOrThrow(CNConfiguredFeatures.THORIUM_ORE); + Holder> nitrateOre = featureLookup.getOrThrow(CNConfiguredFeatures.NITRATE_ORE); Holder> striatedOresOverworld = featureLookup.getOrThrow(CNConfiguredFeatures.STRIATED_ORES_OVERWORLD); - register(ctx, URANIUM_ORE, uraniumOre, placementOres(CountPlacement.of(6), -64, 64)); - register(ctx, LEAD_ORE, leadOre, placementOres(CountPlacement.of(6), -64, 64)); + register(ctx, URANIUM_ORE, uraniumOre, placementOres(CountPlacement.of(7), -63, 16)); + register(ctx, LEAD_ORE, leadOre, placementOres(CountPlacement.of(10), -16, 112)); + register(ctx, THORIUM_ORE, thoriumOre, placementOres(CountPlacement.of(16), -16, 112)); + register(ctx, NITRATE_ORE, nitrateOre, placementOres(CountPlacement.of(6), -63, 64)); register(ctx, STRIATED_ORES_OVERWORLD, striatedOresOverworld, placement(RarityFilter.onAverageOnceEvery(18), -30, 70)); } diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationMapping.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationMapping.java new file mode 100644 index 0000000..210c5e3 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationMapping.java @@ -0,0 +1,8 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.biome.Biome; + +public record BiomeIrradiationMapping(TagKey sourceTag, ResourceKey target) { +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationMappings.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationMappings.java new file mode 100644 index 0000000..8a3a049 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationMappings.java @@ -0,0 +1,28 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceKey; +import net.minecraft.tags.BiomeTags; +import net.minecraft.world.level.biome.Biome; + +import java.util.List; + +public class BiomeIrradiationMappings { + private static final List MAPPINGS = List.of( + new BiomeIrradiationMapping(BiomeTags.IS_OVERWORLD, CNBiomes.Irradiated.PLAIN), + new BiomeIrradiationMapping(BiomeTags.IS_NETHER, CNBiomes.Irradiated.PLAIN), + new BiomeIrradiationMapping(BiomeTags.IS_END, CNBiomes.Irradiated.PLAIN) + ); + + private BiomeIrradiationMappings() {} + + public static ResourceKey resolveTarget(Holder currentBiome, ResourceKey fallback) { + for (BiomeIrradiationMapping mapping : MAPPINGS) { + if (currentBiome.is(mapping.sourceTag())) { + return mapping.target(); + } + } + + return fallback; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationService.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationService.java new file mode 100644 index 0000000..91158db --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/BiomeIrradiationService.java @@ -0,0 +1,120 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeResolver; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.infrastructure.config.CNConfigs; + +import java.util.ArrayList; +import java.util.List; + +public final class BiomeIrradiationService { + public static void circularArea(ServerLevel serverLevel, BlockPos center, ResourceKey defaultTarget, int radius) { + Registry biomeRegistry = serverLevel.registryAccess().registryOrThrow(Registries.BIOME); + + Holder currentAtCenter = serverLevel.getBiome(center); + ResourceKey targetAtCenter = BiomeIrradiationMappings.resolveTarget(currentAtCenter, defaultTarget); + if (currentAtCenter.is(targetAtCenter)) { + return; + } + + int minX = center.getX() - radius; + int maxX = center.getX() + radius; + int minZ = center.getZ() - radius; + int maxZ = center.getZ() + radius; + + double radiusSq = radius * radius; + BiomeResolver resolver = createCircularResolver(biomeRegistry, center, radiusSq, defaultTarget, serverLevel); + + List chunks = new ArrayList<>(); + for (int cz = SectionPos.blockToSectionCoord(minZ); cz < SectionPos.blockToSectionCoord(maxZ); ++cz) { + for (int cx = SectionPos.blockToSectionCoord(minX); cx < SectionPos.blockToSectionCoord(maxX); ++cx) { + ChunkAccess chunkAccess = serverLevel.getChunk(cx, cz, ChunkStatus.FULL, false); + if (chunkAccess != null) { + chunkAccess.fillBiomesFromNoise(resolver, serverLevel.getChunkSource().randomState().sampler()); + chunkAccess.setUnsaved(true); + chunks.add(chunkAccess); + } + } + } + + PersistentIrradiatedZones.get(serverLevel).addChunks(chunks.stream().map(ChunkAccess::getPos).toList()); + serverLevel.getChunkSource().chunkMap.resendBiomesForChunks(chunks); + } + + public static boolean restoreArea(ServerLevel serverLevel, BlockPos pos) { + ChunkPos centerChunk = new ChunkPos(pos); + PersistentIrradiatedZones zones = PersistentIrradiatedZones.get(serverLevel); + + List restored = new ArrayList<>(); + for (ChunkPos target : resolveRestoreTargets(centerChunk)) { + if (!zones.containsChunk(target)) continue; + + ChunkAccess chunkAccess = serverLevel.getChunk(target.x, target.z, ChunkStatus.FULL, false); + if (chunkAccess == null) continue; + + BiomeResolver resolver = (x, y, z, sample) -> serverLevel.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(x, y, z, sample); + + chunkAccess.fillBiomesFromNoise(resolver, serverLevel.getChunkSource().randomState().sampler()); + chunkAccess.setUnsaved(true); + restored.add(chunkAccess); + + zones.removeChunk(target); + } + + if (restored.isEmpty()) return false; + + serverLevel.getChunkSource().chunkMap.resendBiomesForChunks(restored); + + return true; + } + + private static List resolveRestoreTargets(ChunkPos center) { + if (!CNConfigs.server().biomeRestore.restoreInCircle.get()) { + return List.of(center); + } + + int radius = CNConfigs.server().biomeRestore.restoreRadiusChunks.get(); + List targets = new ArrayList<>(); + for (int dz = -radius; dz <= radius; dz++) { + for (int dx = -radius; dx <= radius; dx++) { + if (dx * dx + dz * dz <= radius * radius) { + targets.add(new ChunkPos(center.x + dx, center.z + dz)); + } + } + } + + return targets; + } + + private static BiomeResolver createCircularResolver(Registry biomeRegistry, BlockPos center, double radiusSq, ResourceKey defaultTarget, ServerLevel level) { + return (x, y, z, noise) -> { + int blockX = x << 2; + int blockZ = z << 2; + + double distX = blockX - center.getX(); + double distZ = blockZ - center.getZ(); + + Holder current = level.getBiome(new BlockPos(blockX, y << 2, blockZ)); + + if ((distX * distX) + (distZ * distZ) <= radiusSq) { + ResourceKey target = BiomeIrradiationMappings.resolveTarget(current, defaultTarget); + + return biomeRegistry.getHolderOrThrow(target); + } + + return current; + }; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNBiomes.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNBiomes.java new file mode 100644 index 0000000..6bd1c75 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNBiomes.java @@ -0,0 +1,29 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.HolderGetter; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.resources.ResourceKey; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.nuclearteam.createnuclear.CreateNuclear; +import org.jetbrains.annotations.NotNull; + +public class CNBiomes { + public static final class Irradiated { + public static final ResourceKey PLAIN = key("irradiated_land"); + } + + public static void bootstrapRegistries(BootstrapContext context) { + HolderGetter featureLookup = context.lookup(Registries.PLACED_FEATURE); + HolderGetter> carverLookup = context.lookup(Registries.CONFIGURED_CARVER); + HolderGetter soundLookup = context.lookup(Registries.SOUND_EVENT); + context.register(Irradiated.PLAIN, IrradiatedBiomes.createPlain(featureLookup, carverLookup, soundLookup)); + } + + public static @NotNull ResourceKey key(String id) { + return ResourceKey.create(Registries.BIOME, CreateNuclear.asResource(id)); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNDensityFunctions.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNDensityFunctions.java new file mode 100644 index 0000000..082a4d0 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNDensityFunctions.java @@ -0,0 +1,41 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.HolderGetter; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import net.minecraft.world.level.levelgen.synth.BlendedNoise; +import net.nuclearteam.createnuclear.CreateNuclear; + +public class CNDensityFunctions { + public static final class Irradiated { + public static final ResourceKey EROSION = createKey("irradiated/erosion"); + public static final ResourceKey FINAL_DENSITY = createKey("irradiated/final_density"); + } + + private static ResourceKey createKey(String id) { + return ResourceKey.create(Registries.DENSITY_FUNCTION, CreateNuclear.asResource(id)); + } + + public static void bootstrapRegistries(BootstrapContext context) { + context.register(Irradiated.EROSION, DensityFunctions.add( + DensityFunctions.yClampedGradient(0, 90, 1, -1), + BlendedNoise.createUnseeded(0.25, 0.375, 80.0, 160.0, 8.0) + )); + + context.register(Irradiated.FINAL_DENSITY, DensityFunctions.add( + DensityFunctions.yClampedGradient(0, 90, 1, -1), + BlendedNoise.createUnseeded(0.25, 0.375, 80.0, 160.0, 8.0) + )); + } + + private static DensityFunction registerAndWrap(BootstrapContext context, ResourceKey key, DensityFunction densityFunction) { + return new DensityFunctions.HolderHolder(context.register(key, densityFunction)); + } + + public static DensityFunction getFunction(HolderGetter densityFunctions, ResourceKey key) { + return new DensityFunctions.HolderHolder(densityFunctions.getOrThrow(key)); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNNoiseData.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNNoiseData.java new file mode 100644 index 0000000..a245d57 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNNoiseData.java @@ -0,0 +1,29 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.registries.Registries; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.levelgen.synth.NormalNoise.NoiseParameters; +import net.nuclearteam.createnuclear.CreateNuclear; + +public class CNNoiseData { + public static final ResourceKey EROSION = createKey("irradiated/erosion"); + + private static ResourceKey createKey(String id) { + return ResourceKey.create(Registries.NOISE, CreateNuclear.asResource(id)); + } + + public static void bootstrapRegistries(BootstrapContext context) { + register(context, EROSION, -9, 1.0, 1.0, 0.0, 1.0, 1.0); + } + + private static void register( + BootstrapContext context, + ResourceKey key, + int firstOctave, + double amplitude, + double... otherAmplitudes + ) { + context.register(key, new NoiseParameters(firstOctave, amplitude, otherAmplitudes)); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNNoiseGeneratorSettings.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNNoiseGeneratorSettings.java new file mode 100644 index 0000000..42e222c --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/CNNoiseGeneratorSettings.java @@ -0,0 +1,99 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.HolderGetter; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.OverworldBiomeBuilder; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.levelgen.*; +import net.minecraft.world.level.levelgen.synth.NormalNoise; +import net.nuclearteam.createnuclear.CNBlocks; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.surfacerule.IrradiatedSurfaceRules; +import org.jetbrains.annotations.NotNull; + +public class CNNoiseGeneratorSettings { + public static final ResourceKey IRRADIATED = key("irradiated_noise"); + + public static void bootstrapRegistries(BootstrapContext context) { + HolderGetter densityLookup = context.lookup(Registries.DENSITY_FUNCTION); + HolderGetter noiseLookup = context.lookup(Registries.NOISE); + + context.register(IRRADIATED, new NoiseGeneratorSettings( + NoiseSettings.create(-32, 256, 1, 2), + CNBlocks.STEEL_BLOCK.get().defaultBlockState(), + Blocks.AIR.defaultBlockState(), + CNNoiseGeneratorSettings.irradiated(densityLookup, noiseLookup), + IrradiatedSurfaceRules.DEFAULT_RULE, + new OverworldBiomeBuilder().spawnTarget(), + 32, + false, + false, + false, + false + )); + } + + public static NoiseRouter irradiated(HolderGetter densityLookup, HolderGetter noiseLookup) { + DensityFunction shiftX = CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.SHIFT_X); + DensityFunction shiftZ = CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.SHIFT_Z); + DensityFunction y = CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.Y); + return new NoiseRouter( + DensityFunctions.constant(1), // barrierNoise + DensityFunctions.zero(), // fluidLevelFloodednessNoise + DensityFunctions.zero(), // fluidLevelSpreadNoise + DensityFunctions.zero(), // lavaNoise + DensityFunctions.shiftedNoise2d( + shiftX, shiftZ, 0.25, noiseLookup.getOrThrow(Noises.TEMPERATURE) + ), // temperature + DensityFunctions.shiftedNoise2d( + shiftX, shiftZ, 0, noiseLookup.getOrThrow(Noises.VEGETATION) + ), // vegetation + CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.CONTINENTS), // continents + CNDensityFunctions.getFunction(densityLookup, CNDensityFunctions.Irradiated.EROSION), // erosion + CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.DEPTH), // depth + CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.RIDGES), // ridges + DensityFunctions.add( + DensityFunctions.constant(0.1171875), + DensityFunctions.mul( + DensityFunctions.yClampedGradient( + -30, -40, 0, 1 + ), + DensityFunctions.add( + DensityFunctions.constant(-0.1171875), + DensityFunctions.add( + DensityFunctions.constant(-0.078125), + DensityFunctions.mul( + DensityFunctions.yClampedGradient( + 240, 256, 1, 0 + ), + DensityFunctions.add( + DensityFunctions.constant(0.078125), + DensityFunctions.add( + DensityFunctions.constant(-0.703125), + DensityFunctions.mul( + DensityFunctions.constant(4), + DensityFunctions.mul( + CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.DEPTH), + DensityFunctions.cache2d(CNDensityFunctions.getFunction(densityLookup, NoiseRouterData.FACTOR)) + ).quarterNegative() + ) + ).clamp(-30, 64) + ) + ) + ) + ) + ) + ), // initialDensityWithoutJaggedness + DensityFunctions.blendDensity(CNDensityFunctions.getFunction(densityLookup, CNDensityFunctions.Irradiated.FINAL_DENSITY)), // finalDensity + DensityFunctions.zero(), // veinToggle + DensityFunctions.zero(), // veinRidged + DensityFunctions.zero() // veinGap + ); + } + + private static @NotNull ResourceKey key(String id) { + return ResourceKey.create(Registries.NOISE_SETTINGS, CreateNuclear.asResource(id)); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/IrradiatedBiomes.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/IrradiatedBiomes.java new file mode 100644 index 0000000..69cbc7c --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/IrradiatedBiomes.java @@ -0,0 +1,57 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.HolderGetter; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.nuclearteam.createnuclear.CNSoundEvents; +import net.nuclearteam.createnuclear.content.particles.IrradiatedParticlesData; + +public class IrradiatedBiomes { + public static Biome createPlain(HolderGetter featureLookup, HolderGetter> carverLookup, HolderGetter soundLookup) { + return IrradiatedBiomes.irradiated(featureLookup, carverLookup, soundLookup, new BiomeGenerationSettings.Builder(featureLookup, carverLookup)); + } + + public static Biome irradiated(HolderGetter featureGetter, HolderGetter> carverGetter, HolderGetter soundLookup, BiomeGenerationSettings.Builder generation) { + MobSpawnSettings.Builder spawnBuilder = new MobSpawnSettings.Builder(); + BiomeSpecialEffects.Builder effectBuilder = new BiomeSpecialEffects.Builder(); + + effectBuilder + // Water: murky green + .waterColor(0x3B5133) + .waterFogColor(0x0A0E0A) + + // Fog: sickly radioactive green (kept bright so it stays visible) + .fogColor(0x485E3E) + + // Sky: washed-out grey-green (still lets the sun/moon and day-night cycle show through) + .skyColor(0x324132) + + // Grass: sulfur yellow / scorched + .grassColorOverride(0x8C8F5B) + + // Foliage: withered olive + .foliageColorOverride(0x565E3E) + + .ambientParticle(new AmbientParticleSettings(new IrradiatedParticlesData(), 0.025F)) + .ambientMoodSound(AmbientMoodSettings.LEGACY_CAVE_SETTINGS) + // Plays continuously for as long as the player stands in the biome, under the + // Ambient/Environment volume slider. Deliberately NOT also set as .backgroundMusic(): + // that would start a second, unsynchronised copy of the same file in the MUSIC + // category, and the two would phase against each other. + .ambientLoopSound(soundLookup.getOrThrow(ResourceKey.create(Registries.SOUND_EVENT, CNSoundEvents.BIOME_WASTELAND.getId()))) + ; + + return new Biome.BiomeBuilder() + .mobSpawnSettings(spawnBuilder.build()) + .hasPrecipitation(false) + .temperature(2.0f) + .downfall(0f) + .specialEffects(effectBuilder.build()) + .generationSettings(generation.build()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/PersistentIrradiatedZones.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/PersistentIrradiatedZones.java new file mode 100644 index 0000000..87d299d --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/PersistentIrradiatedZones.java @@ -0,0 +1,81 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.datafix.DataFixTypes; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.saveddata.SavedData; +import net.nuclearteam.createnuclear.CreateNuclear; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class PersistentIrradiatedZones extends SavedData { + private static final String DATA = String.join("_", CreateNuclear.MOD_ID, "irradiated", "zones"); + + private final Set chunks = new HashSet<>(); + + public static PersistentIrradiatedZones get(ServerLevel level) { + return level.getDataStorage().computeIfAbsent(factory(), DATA); + } + + private static SavedData.Factory factory() { + return new SavedData.Factory<>(PersistentIrradiatedZones::new, PersistentIrradiatedZones::load, DataFixTypes.LEVEL); + } + + public void addChunks(Iterable newChunks) { + boolean changed = false; + for (ChunkPos pos : newChunks) { + changed |= chunks.add(pos); + } + + if (changed) setDirty(); + } + + public boolean isInsideAnyZone(BlockPos pos) { + return chunks.contains(new ChunkPos(pos)); + } + + public boolean containsChunk(ChunkPos pos) { + return chunks.contains(pos); + } + + public void removeChunk(ChunkPos pos) { + if (chunks.remove(pos)) setDirty(); + } + + public static PersistentIrradiatedZones load(CompoundTag nbt, HolderLookup.Provider registries) { + PersistentIrradiatedZones data = new PersistentIrradiatedZones(); + data.readFrom(nbt); + + return data; + } + + public void readFrom(CompoundTag nbt) { + chunks.clear(); + ListTag list = nbt.getList("chunks", Tag.TAG_COMPOUND); + list.forEach(tag -> { + CompoundTag e = (CompoundTag) tag; + + chunks.add(new ChunkPos(e.getInt("x"), e.getInt("z"))); + }); + } + + @Override + public @NotNull CompoundTag save(CompoundTag nbt, HolderLookup.@NotNull Provider registries) { + ListTag list = new ListTag(); + chunks.forEach(pos -> { + CompoundTag e = new CompoundTag(); + e.putInt("x", pos.x); + e.putInt("z", pos.z); + list.add(e); + }); + nbt.put("chunks", list); + return nbt; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/surfacerule/BiomeTagRule.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/surfacerule/BiomeTagRule.java new file mode 100644 index 0000000..b25ed40 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/surfacerule/BiomeTagRule.java @@ -0,0 +1,47 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome.surfacerule; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.registries.Registries; +import net.minecraft.tags.TagKey; +import net.minecraft.util.KeyDispatchDataCodec; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.levelgen.SurfaceRules; +import net.neoforged.neoforge.registries.RegisterEvent; +import net.nuclearteam.createnuclear.CreateNuclear; +import org.jetbrains.annotations.NotNull; + +public record BiomeTagRule(@NotNull TagKey tag) implements SurfaceRules.ConditionSource { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(TagKey.codec(Registries.BIOME).fieldOf("tag").forGetter(rule -> rule.tag)).apply(instance, BiomeTagRule::new)); + + /** + * A custom {@link SurfaceRules.ConditionSource} is only usable once its codec lives in the + * {@code MATERIAL_CONDITION} dispatch registry; without this, (de)serialising any rule that + * contains a BiomeTagRule fails with "Unregistered type in dispatch codec". + */ + public static void register(@NotNull RegisterEvent event) { + event.register(Registries.MATERIAL_CONDITION, helper -> + helper.register(CreateNuclear.asResource("biome_tag"), CODEC)); + } + + @Override + public KeyDispatchDataCodec codec() { + return KeyDispatchDataCodec.of(BiomeTagRule.CODEC); + } + + @Override + public SurfaceRules.Condition apply(SurfaceRules.Context context) { + return new Predicate(context); + } + + private class Predicate extends SurfaceRules.LazyYCondition { + protected Predicate(SurfaceRules.Context context) { + super(context); + } + + @Override + protected boolean compute() { + return this.context.biome.get().is(BiomeTagRule.this.tag); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/surfacerule/IrradiatedSurfaceRules.java b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/surfacerule/IrradiatedSurfaceRules.java new file mode 100644 index 0000000..04cfb27 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/infrastructure/worldgen/biome/surfacerule/IrradiatedSurfaceRules.java @@ -0,0 +1,75 @@ +package net.nuclearteam.createnuclear.infrastructure.worldgen.biome.surfacerule; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.levelgen.SurfaceRules; +import net.minecraft.world.level.levelgen.SurfaceRules.ConditionSource; +import net.minecraft.world.level.levelgen.SurfaceRules.RuleSource; +import net.minecraft.world.level.levelgen.VerticalAnchor; +import net.nuclearteam.createnuclear.CNBlocks; +import net.nuclearteam.createnuclear.CreateNuclear; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.CNBiomes; +import net.nuclearteam.createnuclear.infrastructure.worldgen.biome.CNNoiseData; +import org.jetbrains.annotations.NotNull; + +public class IrradiatedSurfaceRules { + private static final ConditionSource IS_IRRADIATED_PLAIN = biome(CNBiomes.Irradiated.PLAIN); + + private static final RuleSource BEDROCK = block(Blocks.BEDROCK); + private static final RuleSource ENRICHED_SOUL_SOIL = block(CNBlocks.ENRICHED_SOUL_SOIL.get()); + private static final RuleSource LEAD_BLOCK = block(CNBlocks.LEAD_BLOCK.get()); + private static final RuleSource LEAD_ORE = block(CNBlocks.LEAD_ORE.get()); + private static final RuleSource RAW_LEAD_BLOCK = block(CNBlocks.RAW_LEAD_BLOCK.get()); + + private static final RuleSource IRRADIATED_PLAIN_MATERIAL = + SurfaceRules.ifTrue(IS_IRRADIATED_PLAIN, RAW_LEAD_BLOCK); + private static final RuleSource SURFACE_GENERATION = SurfaceRules.sequence( + SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, IRRADIATED_PLAIN_MATERIAL), + SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, IRRADIATED_PLAIN_MATERIAL) + ); + + public static final RuleSource DEFAULT_RULE = createDefaultRule(); + + public static @NotNull RuleSource createDefaultRule() { + return SurfaceRules.sequence( + SurfaceRules.ifTrue( + SurfaceRules.isBiome(CNBiomes.Irradiated.PLAIN), + SurfaceRules.sequence( + SurfaceRules.ifTrue( + SurfaceRules.noiseCondition(CNNoiseData.EROSION, 0.035, 0.0465), + LEAD_ORE + ), + SurfaceRules.ifTrue( + SurfaceRules.noiseCondition(CNNoiseData.EROSION, 0.039, 0.0545), + LEAD_BLOCK + ), + SurfaceRules.ifTrue( + SurfaceRules.noiseCondition(CNNoiseData.EROSION, 0.0545, 0.069), + RAW_LEAD_BLOCK + ) + ) + ), + + SurfaceRules.ifTrue(SurfaceRules.verticalGradient("bedrock_floor", VerticalAnchor.bottom(), VerticalAnchor.aboveBottom(5)), BEDROCK), + SurfaceRules.ifTrue(SurfaceRules.abovePreliminarySurface(), SURFACE_GENERATION), + SurfaceRules.ifTrue(SurfaceRules.verticalGradient("enriched", VerticalAnchor.absolute(-4), VerticalAnchor.absolute(4)), ENRICHED_SOUL_SOIL) + ); + } + + private static @NotNull RuleSource block(@NotNull Block block) { + return SurfaceRules.state(block.defaultBlockState()); + } + + public static @NotNull ConditionSource biome(@NotNull TagKey biome) { + return new BiomeTagRule(biome); + } + + @SafeVarargs + public static @NotNull ConditionSource biome(@NotNull ResourceKey @NotNull ... keys) { + return SurfaceRules.isBiome(keys); + } + +} \ No newline at end of file diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 79226f6..feb192a 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1,3 +1,12 @@ public net.minecraft.world.item.crafting.RecipeManager f_44007_ # recipes public net.minecraft.world.item.alchemy.PotionBrewing m_43513_(Lnet/minecraft/world/item/alchemy/Potion;Lnet/minecraft/world/item/Item;Lnet/minecraft/world/item/alchemy/Potion;)V + +public net.minecraft.world.level.levelgen.SurfaceRules$Context +public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition +public net.minecraft.world.level.levelgen.SurfaceRules$Condition +public net.minecraft.world.level.levelgen.SurfaceRules$Context biome +public net.minecraft.world.level.levelgen.NoiseRouterData SHIFT_X +public net.minecraft.world.level.levelgen.NoiseRouterData Y +public net.minecraft.world.level.levelgen.NoiseRouterData SHIFT_Z +public net.minecraft.client.Camera move(DDD)V \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/lang/default/interface.json b/src/main/resources/assets/createnuclear/lang/default/interface.json index 1fc72ee..8b659cd 100644 --- a/src/main/resources/assets/createnuclear/lang/default/interface.json +++ b/src/main/resources/assets/createnuclear/lang/default/interface.json @@ -6,6 +6,10 @@ "emi.category.createnuclear.fan_enriched": "Bulk Enriching", "create.recipe.fan_enriched.fan": "Fan behind Enriched Fire", + "createnuclear.snow_powder.fan.recipe": "Cryogenic fan", + "emi.category.createnuclear.snow_powder": "Cryogenic fan", + "create.recipe.fan_snow_powder.fan": "Fan behind Powder Snow", + "gui.createnuclear.reactor_controller.title": "Reactor Controller", "gui.createnuclear.reactor_input.title": "Reactor Input Storage", @@ -17,9 +21,75 @@ "createnuclear.tooltip.heatLevel.danger": "Danger", "createnuclear.generic.unit.heat": "Q", - "createnuclear.tooltip.item.uranium.rod": "Uranium Rod: ", - "createnuclear.tooltip.item.graphene.rod": "Graphite Rod: ", + "createnuclear.tooltip.item.fuel.rod": "Fuel Rod: ", + "createnuclear.tooltip.item.cooled.rod": "Cooler Rod: ", "createnuclear.tooltip.item.unknown.rod": "Unknown: ", - "createnuclear.gui.reactor_controller.info_header.title": "Heat value" + "createnuclear.tooltip.item.empty.rod": "Input Items: ", + "createnuclear.gui.reactor_controller.info_header.title": "Heat value", + + "createnuclear.generic.unit.countCooled.rod": "Count Cooled Rod: %d", + "createnuclear.generic.unit.countFuel.rod": "Count Fuel Rod: %d", + "createnuclear.generic.unit.count.rod": "Counts %s Rod", + "createnuclear.display_source.reactor_count_rod": "Count Rod in Pattern", + "createnuclear.display_source.reactor_controller_count_rod.fuel": "Fuel", + "createnuclear.display_source.reactor_controller_count_rod.cooled": "Cooled", + + "createnuclear.generic.unit.heat.value": "Q", + "createnuclear.generic.unit.heat.rotation": "RPM", + "createnuclear.generic.unit.heat.type": "Heat Level: %s", + "createnuclear.generic.unit.fluid.value": "mB", + "createnuclear.display_source.heat.value": "Q", + "createnuclear.display_source.heat.rotation": "RPM", + "createnuclear.display_source.heat.type": "Heat level", + "createnuclear.display_source.reactor_summary": "Reactor Summary", + "createnuclear.display_source.heat": "Reactor Heat", + "createnuclear.display_source.liquid_level": "Reactor Liquid Level", + "createnuclear.display_source.fuel": "Reactor Fuel", + "createnuclear.display_source.cooler": "Reactor Cooler", + "createnuclear.display_source.size": "Reactor Size", + "createnuclear.display_source.reactor_summary.no_controller": "Reactor not found", + "createnuclear.display_source.reactor_summary.status": "Status:", + "createnuclear.display_source.reactor_summary.active": "ACTIVE", + "createnuclear.display_source.reactor_summary.idle": "IDLE", + "createnuclear.display_source.reactor_summary.temperature": "Temp:", + "createnuclear.display_source.reactor_summary.fuel": "Rods:", + "createnuclear.display_source.reactor_summary.coolant": "Coolant:", + "createnuclear.display_source.reactor.not_enough_space": "Not enough space ", + "createnuclear.display_source.reactor.for_reactor_status": "for reactor status", + "createnuclear.display_source.reactor.no_controller": "No Reactor Controller found", + "createnuclear.display_source.reactor.status": "Status: ", + "createnuclear.display_source.reactor.active": "ACTIVE", + "createnuclear.display_source.reactor.idle": "IDLE", + "createnuclear.display_source.reactor.size": "Tier: ", + "createnuclear.display_source.reactor.fuel": "Fuel: ", + "createnuclear.display_source.reactor.cooler": "Cooler: ", + "createnuclear.display_source.reactor.heat": "Heat: ", + "createnuclear.display_source.reactor.mode.value": "Values", + "createnuclear.display_source.reactor.mode.percent": "Percentages", + "createnuclear.display_source.reactor.mode.gauge": "Gauges", + "createnuclear.display_source.reactor.size.small": "Small", + "createnuclear.display_source.reactor.size.medium": "Medium", + "createnuclear.display_source.reactor.size.large": "Large", + "createnuclear.display_source.reactor.mode.normal": "Normal", + "createnuclear.display_source.reactor.fluid": "Fluid: ", + + "createnuclear.generic.unit.liquid": "WIP", + "createnuclear.display_source.liquid": "Liquid Level", + "createnuclear.tooltip.fluid": "Fluid: %s ", + "createnuclear.tooltip.fluid.none": "Fluid: None ", + "createnuclear.tooltip.fluid.amount": "%d mb", + + "createnuclear.generic.unit.seconds": "s", + "createnuclear.notification.reactor.meltdown_in": "CORE MELTDOWN IN ", + "createnuclear.notification.reactor.overheating": "WARNING: CORE OVERHEATING", + "createnuclear.notification.reactor.critical_failure": "CRITICAL FAILURE", + "createnuclear.notification.reactor.imminent_explosion": "IMMINENT EXPLOSION", + "createnuclear.notification.reactor.stabilized": "CORE STABILIZED", + "createnuclear.notification.reactor.destroyed": "REACTOR DESTROYED", + "createnuclear.notification.reactor.meltdown_finished": "Critical meltdown completed", + "createnuclear.notification.reactor.disassembled": "Reactor has been disassembled", + "createnuclear.notification.reactor.assembled": "Reactor has been assembled", + "death.attack.createnuclear.radiation": "%1$s died from radiation", + "death.attack.createnuclear.fan_radiation": "%1$s died from radiation blast" } diff --git a/src/main/resources/assets/createnuclear/lang/default/irradiated.json b/src/main/resources/assets/createnuclear/lang/default/irradiated.json new file mode 100644 index 0000000..fab98a1 --- /dev/null +++ b/src/main/resources/assets/createnuclear/lang/default/irradiated.json @@ -0,0 +1,3 @@ +{ + "irradiated.taming.wip": "This creature cannot be tamed yet - irradiated mobs are still a work in progress" +} diff --git a/src/main/resources/assets/createnuclear/lang/default/potion.json b/src/main/resources/assets/createnuclear/lang/default/potion.json index 9134804..af7439e 100644 --- a/src/main/resources/assets/createnuclear/lang/default/potion.json +++ b/src/main/resources/assets/createnuclear/lang/default/potion.json @@ -15,5 +15,17 @@ "item.minecraft.tipped_arrow.effect.potion_of_radiation_1": "Arrow of Radiation", "item.minecraft.tipped_arrow.effect.potion_of_radiation_augment_1": "Arrow of Radiation", - "item.minecraft.tipped_arrow.effect.potion_of_radiation_2": "Arrow of Radiation" + "item.minecraft.tipped_arrow.effect.potion_of_radiation_2": "Arrow of Radiation", + + "effect.createnuclear.iodine": "Iodine", + + "item.minecraft.potion.effect.potion_of_iodine": "Potion of Iodine", + "item.minecraft.splash_potion.effect.potion_of_iodine": "Splash Potion of Iodine", + "item.minecraft.lingering_potion.effect.potion_of_iodine": "Lingering Potion of Iodine", + "item.minecraft.tipped_arrow.effect.potion_of_iodine": "Arrow of Iodine", + + "item.minecraft.potion.effect.potion_of_iodine_augment": "Potion of Iodine", + "item.minecraft.splash_potion.effect.potion_of_iodine_augment": "Splash Potion of Iodine", + "item.minecraft.lingering_potion.effect.potion_of_iodine_augment": "Lingering Potion of Iodine", + "item.minecraft.tipped_arrow.effect.potion_of_iodine_augment": "Arrow of Iodine" } \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/lang/default/tooltips.json b/src/main/resources/assets/createnuclear/lang/default/tooltips.json index 94b20cf..c081563 100644 --- a/src/main/resources/assets/createnuclear/lang/default/tooltips.json +++ b/src/main/resources/assets/createnuclear/lang/default/tooltips.json @@ -8,6 +8,10 @@ "block.createnuclear.reactor_input.tooltip.condition1": "When the Reactor is assembled", "block.createnuclear.reactor_input.tooltip.behaviour1": "Insert _uranium_ and _graphene_ rods in large quantities to power _the reactor_ ", + "block.createnuclear.reactor_fluid_input.tooltip.summary": "_The Reactor Fluid Input_ is a _block_ that can be used to insert _Water_ or _Liquid Nitrogen_ into the Reactor.", + "block.createnuclear.reactor_fluid_input.tooltip.condition1": "When the reactor is assembled", + "block.createnuclear.reactor_fluid_input.tooltip.behaviour1": "Insert _Water_ or _Liquid Nitrogen_ in large quantities to power _The Reactor_ and avoid _overheating_.", + "block.createnuclear.reactor_controller.tooltip.summary": " The _Reactor Controller_ is the _most important_ block for operating the reactor", "block.createnuclear.reactor_controller.tooltip.condition1": "When the reactor is assembled", @@ -34,14 +38,24 @@ "item.createnuclear.uranium_rod.tooltip.summary": "_Stick_ to be used in the _reactor input_ to operate it.", "item.createnuclear.reactor_blueprint_item.tooltip.summary": "It allows you to configure your own pattern.", - "item.createnuclear.reactor_blueprint_item.tooltip.condition1": "Filled with graphite and uranium rods", + "item.createnuclear.reactor_blueprint_item.tooltip.condition1": "Filled with cooling and fuel rods", "item.createnuclear.reactor_blueprint_item.tooltip.behaviour1": "_Right_ click to fill it.", "item.createnuclear.reactor_blueprint_item.tooltip.condition2": "The reactor must be assembled", "item.createnuclear.reactor_blueprint_item.tooltip.behaviour2": "_Right_ click on the reactor controller to open it.", + "item.createnuclear.reactor_blueprint.tooltip": "Used to design and visualize the reactor's interior layout.", + "item.createnuclear.reactor_blueprint.tooltip_hint": "§7Press §eShift + Right Click §7to with the blueprint in hand to get the Wiki link.", + "item.createnuclear.reactor_blueprint.chat_info": "Follow the link to open the documentation:", + "item.createnuclear.reactor_blueprint.wiki_link": "[Open Official Wiki]", "block.createnuclear.enriched_soul_soil.tooltip.summary": "The _only_ block who create _enriched fire_.Can be used to _craft_ enriched campfire or just to make a _green fire_.", + "createnuclear.tooltip.statRod": "Rod Stat Pattern:", + "createnuclear.tooltip.baseRodHeat": "Base Rod Heat: %d Q", + "createnuclear.tooltip.proximityRodHeat": "Proximity Rod Heat: %.2f Q", + "createnuclear.tooltip.rodTimer": "Rod Duration: %d tick", + "createnuclear.tooltip.typeRod": "Rod Type: %s", + "createnuclear.tooltip.cloth.color": "Color of armor piece: %s", "tag.item.c.coal_dusts": "Coal Dusts", @@ -59,5 +73,7 @@ "tag.item.trinkets.head.face": "Head Face", - "overlay.event_message": "⚠ Warning ⚠ %s timer" + "overlay.event_message": "⚠ Warning ⚠ %s timer", + + "attribute.name.createnuclear.generic.irradiated_resistance": "Radiation Resistance" } \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/lang/en_US.json b/src/main/resources/assets/createnuclear/lang/en_US.json index f7cde70..1cce5e8 100644 --- a/src/main/resources/assets/createnuclear/lang/en_US.json +++ b/src/main/resources/assets/createnuclear/lang/en_US.json @@ -23,19 +23,17 @@ "createnuclear.enriched.fan.recipe": "Bulk Enriched", "createnuclear.generic.unit.heat": "Q", "createnuclear.gui.reactor_controller.info_header.title": "Heat value", - "createnuclear.ponder.reactor.header": "Setup from Reactor", - "createnuclear.ponder.reactor.text_1": "Floor 1", - "createnuclear.ponder.reactor.text_10": "Floor 7", - "createnuclear.ponder.reactor.text_2": "Reactor Output: This is where the block outputs the energy (SU) generated from production.", - "createnuclear.ponder.reactor.text_3": "Floor 2", - "createnuclear.ponder.reactor.text_4": "Floor 3", - "createnuclear.ponder.reactor.text_5": "Floor 4", - "createnuclear.ponder.reactor.text_6": "Reactor Input: A block that stores uranium rods and graphite rods to operate the reactor.", - "createnuclear.ponder.reactor.text_7": "Reactor Controller: The most important block for operating the reactor; it handles all the calculations needed to generate energy.", - "createnuclear.ponder.reactor.text_8": "Floor 5", - "createnuclear.ponder.reactor.text_9": "Floor 6", - "createnuclear.ponder.reactor_blueprint.header": "Enable controller", - "createnuclear.ponder.reactor_blueprint.text_1": "Reactor Blueprint: The most important item; it allows you to configure the reactor according to specific patterns.", + "createnuclear.ponder.reactor.title.t1": "Reactor T1 — Construction", + "createnuclear.ponder.reactor.title.t2": "Reactor T2 — Construction", + "createnuclear.ponder.reactor.title.t3": "Reactor T3 — Construction", + "createnuclear.ponder.reactor.floor": "Floor ", + "createnuclear.ponder.reactor.input1": "Input: stores uranium/graphite. One per rod.", + "createnuclear.ponder.reactor.input2": "Input 2: secondary input (place on an edge, not on the frame).", + "createnuclear.ponder.reactor.liquid_input": "Liquid input: cooling fluid inlet. Place on an edge.", + "createnuclear.ponder.reactor.alarm": "Alarm: place on an edge (not on the frame). Triggers on overheating.", + "createnuclear.ponder.reactor.output": "Output: produces energy (SU).", + "createnuclear.ponder.reactor.controller": "Controller: central block that manages the reactor.", + "createnuclear.ponder.reactor.use_blueprint": "Use the Reactor Blueprint to assemble the multiblock.", "createnuclear.tooltip.heatLevel": "Heat Level", "createnuclear.tooltip.heatLevel.caution": "Caution", "createnuclear.tooltip.heatLevel.danger": "Danger", @@ -45,6 +43,7 @@ "createnuclear.tooltip.item.graphene.rod": "Graphene Rod: ", "createnuclear.tooltip.item.unknown.rod": "Unknown: ", "createnuclear.tooltip.item.uranium.rod": "Uranium Rod: ", + "effect.createnuclear.iodine": "Iodine", "effect.createnuclear.radiation": "Radiation", "emi.category.createnuclear.fan_enriched": "Bulk Enriched", "entity.createnuclear.irradiated_cat": "Irradiated Cat", @@ -133,15 +132,23 @@ "item.createnuclear.yellow_anti_radiation_leggings": "Yellow Anti Radiation Leggings", "item.createnuclear.yellow_cloth": "Yellow Cloth", "item.createnuclear.yellowcake": "Yellowcake", + "item.minecraft.lingering_potion.effect.potion_of_iodine": "Lingering Potion of Iodine", + "item.minecraft.lingering_potion.effect.potion_of_iodine_augment": "Lingering Potion of Iodine", "item.minecraft.lingering_potion.effect.potion_of_radiation_1": "Lingering Potion of Radiation", "item.minecraft.lingering_potion.effect.potion_of_radiation_2": "Lingering Potion of Radiation", "item.minecraft.lingering_potion.effect.potion_of_radiation_augment_1": "Lingering Potion of Radiation", + "item.minecraft.potion.effect.potion_of_iodine": "Potion of Iodine", + "item.minecraft.potion.effect.potion_of_iodine_augment": "Potion of Iodine", "item.minecraft.potion.effect.potion_of_radiation_1": "Potion of Radiation", "item.minecraft.potion.effect.potion_of_radiation_2": "Potion of Radiation", "item.minecraft.potion.effect.potion_of_radiation_augment_1": "Potion of Radiation", + "item.minecraft.splash_potion.effect.potion_of_iodine": "Splash Potion of Iodine", + "item.minecraft.splash_potion.effect.potion_of_iodine_augment": "Splash Potion of Iodine", "item.minecraft.splash_potion.effect.potion_of_radiation_1": "Splash Potion of Radiation", "item.minecraft.splash_potion.effect.potion_of_radiation_2": "Splash Potion of Radiation", "item.minecraft.splash_potion.effect.potion_of_radiation_augment_1": "Splash Potion of Radiation", + "item.minecraft.tipped_arrow.effect.potion_of_iodine": "Arrow of Iodine", + "item.minecraft.tipped_arrow.effect.potion_of_iodine_augment": "Arrow of Iodine", "item.minecraft.tipped_arrow.effect.potion_of_radiation_1": "Arrow of Radiation", "item.minecraft.tipped_arrow.effect.potion_of_radiation_2": "Arrow of Radiation", "item.minecraft.tipped_arrow.effect.potion_of_radiation_augment_1": "Arrow of Radiation", @@ -165,5 +172,6 @@ "tag.item.createnuclear.fuel": "Fuel", "tag.item.minecraft.all.campfires": "All Campfires", "tag.item.minecraft.campfires": "Campfires", - "tag.item.trinkets.head.face": "Head Face" + "tag.item.trinkets.head.face": "Head Face", + "item.createnuclear.raw_thorium": "Raw Thorium" } \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/lang/fr_fr.json b/src/main/resources/assets/createnuclear/lang/fr_fr.json index 702c09e..d6ee210 100644 --- a/src/main/resources/assets/createnuclear/lang/fr_fr.json +++ b/src/main/resources/assets/createnuclear/lang/fr_fr.json @@ -136,6 +136,7 @@ "createnuclear.tooltip.item.graphene.rod": "Barre de graphite : ", "createnuclear.tooltip.item.unknown.rod": "Inconnue : ", "createnuclear.tooltip.item.uranium.rod": "Barre d'uranium : ", + "effect.createnuclear.iodine": "Iode", "effect.createnuclear.radiation": "Radiation", "emi.category.createnuclear.fan_enriched": "Enrichi en bloc", "entity.createnuclear.irradiated_cat": "Chat irradié", @@ -234,15 +235,23 @@ "item.createnuclear.yellow_anti_radiation_leggings": "Jambières anti-radiations jaune", "item.createnuclear.yellow_cloth": "Tissu jaune", "item.createnuclear.yellowcake": "Yellowcake", + "item.minecraft.lingering_potion.effect.potion_of_iodine": "Potion d'iode persistante", + "item.minecraft.lingering_potion.effect.potion_of_iodine_augment": "Potion d'iode persistante", "item.minecraft.lingering_potion.effect.potion_of_radiation_1": "Potion de radiation persistante", "item.minecraft.lingering_potion.effect.potion_of_radiation_2": "Potion de radiation persistante", "item.minecraft.lingering_potion.effect.potion_of_radiation_augment_1": "Potion de radiation persistante", + "item.minecraft.potion.effect.potion_of_iodine": "Potion d'iode", + "item.minecraft.potion.effect.potion_of_iodine_augment": "Potion d'iode", "item.minecraft.potion.effect.potion_of_radiation_1": "Potion de radiation", "item.minecraft.potion.effect.potion_of_radiation_2": "Potion de radiation", "item.minecraft.potion.effect.potion_of_radiation_augment_1": "Potion de radiation", + "item.minecraft.splash_potion.effect.potion_of_iodine": "Potion d'iode jetable", + "item.minecraft.splash_potion.effect.potion_of_iodine_augment": "Potion d'iode jetable", "item.minecraft.splash_potion.effect.potion_of_radiation_1": "Potion de radiation jetable", "item.minecraft.splash_potion.effect.potion_of_radiation_2": "Potion de radiation jetable", "item.minecraft.splash_potion.effect.potion_of_radiation_augment_1": "Potion de radiation jetable", + "item.minecraft.tipped_arrow.effect.potion_of_iodine": "Flèche d'iode", + "item.minecraft.tipped_arrow.effect.potion_of_iodine_augment": "Flèche d'iode", "item.minecraft.tipped_arrow.effect.potion_of_radiation_1": "Flèche irradiée", "item.minecraft.tipped_arrow.effect.potion_of_radiation_2": "Flèche irradiée", "item.minecraft.tipped_arrow.effect.potion_of_radiation_augment_1": "Flèche irradiée", @@ -267,5 +276,6 @@ "tag.item.createnuclear.fuel": "Carburant", "tag.item.minecraft.all.campfires": "Tous les feux de camps", "tag.item.minecraft.campfires": "Feu de camps", - "tag.item.trinkets.head.face": "Face avant" + "tag.item.trinkets.head.face": "Face avant", + "item.createnuclear.raw_thorium": "Thorium brut" } \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block.json b/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block.json index 90260a8..95840f6 100644 --- a/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block.json +++ b/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block.json @@ -1,4 +1,5 @@ { + "render_type": "cutout_mipped", "parent": "minecraft:block/template_campfire", "textures": { "fire": "createnuclear:block/enriching/campfire/enriching_flame", diff --git a/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block_off.json b/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block_off.json index 9dc5476..7bfa232 100644 --- a/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block_off.json +++ b/src/main/resources/assets/createnuclear/models/block/enriching/campfire/block_off.json @@ -1,4 +1,5 @@ { + "render_type": "cutout_mipped", "parent": "block/block", "display": { "head": { diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/alarm/block.json b/src/main/resources/assets/createnuclear/models/block/reactor/alarm/block.json new file mode 100644 index 0000000..0db3279 --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/block/reactor/alarm/block.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/controller/item.json b/src/main/resources/assets/createnuclear/models/block/reactor/controller/item.json index 7a0ca23..fa132ac 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/controller/item.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/controller/item.json @@ -2,9 +2,9 @@ "credit": "Made with Blockbench", "parent": "block/cube", "textures": { - "1": "createnuclear:block/reactor/controller/controller_panel", + "1": "createnuclear:block/reactor/controller/controller_panel_on", "2": "createnuclear:block/reactor/casing/reactor_casing", - "particle": "createnuclear:block/reactor/controller/controller_panel" + "particle": "createnuclear:block/reactor/controller/controller_panel_on" }, "elements": [ { diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/fluid_input.json b/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/fluid_input.json new file mode 100644 index 0000000..92c6a85 --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/fluid_input.json @@ -0,0 +1,92 @@ +{ + "render_type": "cutout_mipped", + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "0": "createnuclear:block/reactor/casing/reactor_casing", + "1": "createnuclear:block/reactor/fluid_input/input", + "2": "createnuclear:block/reactor/casing/reactor_casing", + "particle": "createnuclear:block/reactor/casing/reactor_casing" + }, + "elements": [ + { + "name": "right", + "from": [14, 0, 15], + "to": [16, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11.5]}, + "faces": { + "east": {"uv": [0, 0, 1, 16], "texture": "#0"}, + "south": {"uv": [14, 0, 16, 16], "texture": "#1"}, + "west": {"uv": [13, 0, 14, 16], "texture": "#1"}, + "up": {"uv": [14, 0, 16, 1], "rotation": 180, "texture": "#2"}, + "down": {"uv": [14, 15, 16, 16], "rotation": 180, "texture": "#2"} + } + }, + { + "name": "left", + "from": [0, 0, 15], + "to": [2, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11.5]}, + "faces": { + "north": {"uv": [0, 0, 2, 16], "texture": "#1"}, + "east": {"uv": [2, 0, 3, 16], "texture": "#1"}, + "south": {"uv": [0, 0, 2, 16], "texture": "#1"}, + "west": {"uv": [15, 0, 16, 16], "texture": "#0"}, + "up": {"uv": [0, 0, 2, 1], "rotation": 180, "texture": "#2"}, + "down": {"uv": [0, 15, 2, 16], "rotation": 180, "texture": "#2"} + } + }, + { + "name": "down", + "from": [2, 0, 15], + "to": [14, 2, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11.5]}, + "faces": { + "south": {"uv": [2, 14, 14, 16], "texture": "#1"}, + "up": {"uv": [2, 14, 14, 15], "rotation": 180, "texture": "#1"}, + "down": {"uv": [2, 15, 14, 16], "rotation": 180, "texture": "#2"} + } + }, + { + "name": "up", + "from": [2, 14, 15], + "to": [14, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11.5]}, + "faces": { + "south": {"uv": [2, 0, 14, 2], "texture": "#1"}, + "up": {"uv": [2, 0, 14, 1], "rotation": 180, "texture": "#2"}, + "down": {"uv": [2, 1, 14, 2], "rotation": 180, "texture": "#1"} + } + }, + { + "name": "core", + "from": [0, 0, 0], + "to": [16, 16, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11.5]}, + "faces": { + "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, + "east": {"uv": [1, 0, 16, 16], "texture": "#0"}, + "south": {"uv": [0, 0, 16, 16], "texture": "#1"}, + "west": {"uv": [0, 0, 15, 16], "texture": "#0"}, + "up": {"uv": [0, 1, 16, 16], "rotation": 180, "texture": "#2"}, + "down": {"uv": [0, 0, 16, 15], "rotation": 180, "texture": "#2"} + } + } + ], + "groups": [ + { + "name": "block", + "origin": [8, 8, 8], + "color": 0, + "children": [ + { + "name": "north", + "origin": [8, 8, 8], + "color": 0, + "children": [0, 1, 2, 3] + }, + 4 + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/fluid_input_vertical.json b/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/fluid_input_vertical.json new file mode 100644 index 0000000..f233a3b --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/fluid_input_vertical.json @@ -0,0 +1,250 @@ +{ + "render_type": "cutout_mipped", + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "0": "createnuclear:block/reactor/fluid_input/input", + "1": "createnuclear:block/reactor/casing/reactor_casing", + "2": "createnuclear:block/reactor/casing/reactor_casing", + "particle": "createnuclear:block/reactor/input/input" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 14, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [2, 0, 16, 16], "rotation": 90, "texture": "#1"}, + "east": {"uv": [0, 0, 14, 16], "rotation": 270, "texture": "#2"}, + "south": {"uv": [2, 0, 16, 16], "rotation": 90, "texture": "#1"}, + "west": {"uv": [2, 0, 16, 16], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0, 16, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#2"} + } + }, + { + "from": [12, 14, 0], + "to": [16, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 12, 2, 16], "rotation": 90, "texture": "#1"}, + "east": {"uv": [14, 0, 16, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 0, 2, 4], "rotation": 90, "texture": "#1"}, + "west": {"uv": [3, 0, 5, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [0, 0, 4, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [12, 0, 16, 16], "texture": "#0"} + } + }, + { + "from": [0, 14, 0], + "to": [4, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 0, 2, 4], "rotation": 90, "texture": "#1"}, + "east": {"uv": [11, 0, 13, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 12, 2, 16], "rotation": 90, "texture": "#1"}, + "west": {"uv": [0, 0, 2, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [12, 0, 16, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [0, 0, 4, 16], "texture": "#0"} + } + }, + { + "from": [4, 14, 0], + "to": [5, 16, 7], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 4, 2, 5], "rotation": 90, "texture": "#1"}, + "east": {"uv": [10, 9, 12, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [4, 9, 6, 10], "rotation": 90, "texture": "#0"}, + "west": {"uv": [10, 9, 12, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [11, 9, 12, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [11, 9, 12, 16], "texture": "#0"} + } + }, + { + "from": [11, 14, 0], + "to": [12, 16, 7], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 11, 2, 12], "rotation": 90, "texture": "#1"}, + "east": {"uv": [4, 9, 6, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [4, 9, 6, 10], "rotation": 90, "texture": "#0"}, + "west": {"uv": [4, 9, 6, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [4, 9, 5, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [4, 9, 5, 16], "texture": "#0"} + } + }, + { + "from": [11, 14, 9], + "to": [12, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [4, 6, 6, 7], "rotation": 90, "texture": "#0"}, + "east": {"uv": [4, 0, 6, 7], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 11, 2, 12], "rotation": 90, "texture": "#1"}, + "west": {"uv": [4, 0, 6, 7], "rotation": 90, "texture": "#0"}, + "up": {"uv": [4, 0, 5, 7], "rotation": 180, "texture": "#0"}, + "down": {"uv": [4, 0, 5, 7], "texture": "#0"} + } + }, + { + "from": [4, 14, 9], + "to": [5, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [4, 6, 6, 7], "rotation": 90, "texture": "#0"}, + "east": {"uv": [10, 0, 12, 7], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 4, 2, 5], "rotation": 90, "texture": "#1"}, + "west": {"uv": [10, 0, 12, 7], "rotation": 90, "texture": "#0"}, + "up": {"uv": [11, 0, 12, 7], "rotation": 180, "texture": "#0"}, + "down": {"uv": [11, 0, 12, 7], "texture": "#0"} + } + }, + { + "from": [5, 14, 10], + "to": [6, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [5, 5, 7, 6], "rotation": 90, "texture": "#0"}, + "east": {"uv": [9, 0, 11, 6], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 6, 2, 7], "rotation": 90, "texture": "#1"}, + "west": {"uv": [9, 0, 11, 6], "rotation": 90, "texture": "#0"}, + "up": {"uv": [10, 0, 11, 6], "rotation": 180, "texture": "#0"}, + "down": {"uv": [10, 0, 11, 6], "texture": "#0"} + } + }, + { + "from": [10, 14, 10], + "to": [11, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [5, 5, 7, 6], "rotation": 90, "texture": "#0"}, + "east": {"uv": [5, 0, 7, 6], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 10, 2, 11], "rotation": 90, "texture": "#1"}, + "west": {"uv": [5, 0, 7, 6], "rotation": 90, "texture": "#0"}, + "up": {"uv": [5, 0, 6, 6], "rotation": 180, "texture": "#0"}, + "down": {"uv": [5, 0, 6, 6], "texture": "#0"} + } + }, + { + "from": [10, 14, 0], + "to": [11, 16, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 10, 2, 11], "rotation": 90, "texture": "#1"}, + "east": {"uv": [5, 10, 7, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [5, 10, 7, 11], "rotation": 90, "texture": "#0"}, + "west": {"uv": [5, 10, 7, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [5, 10, 6, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [5, 10, 6, 16], "texture": "#0"} + } + }, + { + "from": [9, 14, 0], + "to": [10, 16, 5], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 9, 2, 10], "rotation": 90, "texture": "#1"}, + "east": {"uv": [6, 11, 8, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [6, 11, 8, 12], "rotation": 90, "texture": "#0"}, + "west": {"uv": [6, 11, 8, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [6, 11, 7, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [6, 11, 7, 16], "texture": "#0"} + } + }, + { + "from": [6, 14, 0], + "to": [7, 16, 5], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 6, 2, 7], "rotation": 90, "texture": "#1"}, + "east": {"uv": [8, 11, 10, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [6, 11, 8, 12], "rotation": 90, "texture": "#0"}, + "west": {"uv": [8, 11, 10, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [9, 11, 10, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [9, 11, 10, 16], "texture": "#0"} + } + }, + { + "from": [6, 14, 11], + "to": [7, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [6, 4, 8, 5], "rotation": 90, "texture": "#0"}, + "east": {"uv": [8, 0, 10, 5], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 7, 2, 8], "rotation": 90, "texture": "#1"}, + "west": {"uv": [8, 0, 10, 5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [9, 0, 10, 5], "rotation": 180, "texture": "#0"}, + "down": {"uv": [9, 0, 10, 5], "texture": "#0"} + } + }, + { + "from": [9, 14, 11], + "to": [10, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [6, 4, 8, 5], "rotation": 90, "texture": "#0"}, + "east": {"uv": [6, 0, 8, 5], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 9, 2, 10], "rotation": 90, "texture": "#1"}, + "west": {"uv": [6, 0, 8, 5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [6, 0, 7, 5], "rotation": 180, "texture": "#0"}, + "down": {"uv": [6, 0, 7, 5], "texture": "#0"} + } + }, + { + "from": [7, 14, 12], + "to": [9, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [3, 7, 5, 9], "rotation": 90, "texture": "#0"}, + "east": {"uv": [7, 0, 9, 4], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 7, 2, 9], "rotation": 90, "texture": "#1"}, + "west": {"uv": [7, 0, 9, 4], "rotation": 90, "texture": "#0"}, + "up": {"uv": [7, 0, 9, 4], "rotation": 180, "texture": "#0"}, + "down": {"uv": [7, 0, 9, 4], "texture": "#0"} + } + }, + { + "from": [7, 14, 0], + "to": [9, 16, 4], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 7, 2, 9], "rotation": 90, "texture": "#1"}, + "east": {"uv": [7, 12, 9, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [3, 7, 5, 9], "rotation": 90, "texture": "#0"}, + "west": {"uv": [7, 12, 9, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [7, 12, 9, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [7, 12, 9, 16], "texture": "#0"} + } + }, + { + "from": [5, 14, 0], + "to": [6, 16, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 5, 2, 6], "rotation": 90, "texture": "#1"}, + "east": {"uv": [9, 10, 11, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [5, 10, 7, 11], "rotation": 90, "texture": "#0"}, + "west": {"uv": [9, 10, 11, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [10, 10, 11, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [10, 10, 11, 16], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "group", + "origin": [0, 0, 0], + "color": 0, + "children": [ + 0, + { + "name": "face", + "origin": [8, 8, 8], + "color": 0, + "children": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/item.json b/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/item.json new file mode 100644 index 0000000..ddb1e1b --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/block/reactor/fluid_input/item.json @@ -0,0 +1,92 @@ +{ + "render_type": "cutout_mipped", + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "0": "createnuclear:block/reactor/casing/reactor_casing", + "1": "createnuclear:block/reactor/fluid_input/input", + "2": "createnuclear:block/reactor/casing/reactor_casing", + "particle": "createnuclear:block/reactor/casing/reactor_casing" + }, + "elements": [ + { + "name": "right", + "from": [0, 0, 0], + "to": [2, 16, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 4.5]}, + "faces": { + "north": {"uv": [14, 0, 16, 16], "texture": "#1"}, + "east": {"uv": [13, 0, 14, 16], "texture": "#1"}, + "west": {"uv": [0, 0, 1, 16], "texture": "#0"}, + "up": {"uv": [14, 0, 16, 1], "texture": "#2"}, + "down": {"uv": [14, 15, 16, 16], "texture": "#2"} + } + }, + { + "name": "left", + "from": [14, 0, 0], + "to": [16, 16, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 4.5]}, + "faces": { + "north": {"uv": [0, 0, 2, 16], "texture": "#1"}, + "east": {"uv": [15, 0, 16, 16], "texture": "#0"}, + "south": {"uv": [0, 0, 2, 16], "texture": "#1"}, + "west": {"uv": [2, 0, 3, 16], "texture": "#1"}, + "up": {"uv": [0, 0, 2, 1], "texture": "#2"}, + "down": {"uv": [0, 15, 2, 16], "texture": "#2"} + } + }, + { + "name": "down", + "from": [2, 0, 0], + "to": [14, 2, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 4.5]}, + "faces": { + "north": {"uv": [2, 14, 14, 16], "texture": "#1"}, + "up": {"uv": [2, 14, 14, 15], "texture": "#1"}, + "down": {"uv": [2, 15, 14, 16], "texture": "#2"} + } + }, + { + "name": "up", + "from": [2, 14, 0], + "to": [14, 16, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 4.5]}, + "faces": { + "north": {"uv": [2, 0, 14, 2], "texture": "#1"}, + "up": {"uv": [2, 0, 14, 1], "texture": "#2"}, + "down": {"uv": [2, 1, 14, 2], "texture": "#1"} + } + }, + { + "name": "core", + "from": [0, 0, 1], + "to": [16, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 4.5]}, + "faces": { + "north": {"uv": [0, 0, 16, 16], "texture": "#1"}, + "east": {"uv": [0, 0, 15, 16], "texture": "#0"}, + "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, + "west": {"uv": [1, 0, 16, 16], "texture": "#0"}, + "up": {"uv": [0, 1, 16, 16], "texture": "#2"}, + "down": {"uv": [0, 0, 16, 15], "texture": "#2"} + } + } + ], + "groups": [ + { + "name": "block", + "origin": [8, 8, 8], + "color": 0, + "children": [ + { + "name": "north", + "origin": [8, 8, 8], + "color": 0, + "children": [0, 1, 2, 3] + }, + 4 + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_bottom.json b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_bottom.json index e315bae..d2d868c 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_bottom.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_bottom.json @@ -1,25 +1,23 @@ { + "render_type": "cutout_mipped", + "format_version": "1.21.11", "credit": "Made with Blockbench", "parent": "block/block", "textures": { - "0": "createnuclear:block/reactor/casing/casing_inner", - "1": "createnuclear:block/reactor/casing/casing_top", "2": "createnuclear:block/reactor/frame/window", "3": "createnuclear:block/reactor/casing/reactor_casing", - "4": "createnuclear:fluid/uranium_still", - "5": "createnuclear:fluid/uranium_flow", - "particle": "createnuclear:block/reactor/casing/casing_inner" + "particle": "createnuclear:block/reactor/casing/reactor_casing" }, "elements": [ { "name": "SideRight", "from": [1, 4, 0], "to": [4, 16, 1], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, -23]}, + "rotation": {"angle": 0, "axis": "y", "origin": [8, 10, 0.5]}, "faces": { "north": {"uv": [12, 5, 15, 12], "texture": "#3", "cullface": "north"}, "east": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "north"}, - "south": {"uv": [1, 0, 4, 12], "texture": "#3", "cullface": "north"}, + "south": {"uv": [1, 3, 4, 15], "texture": "#3", "cullface": "north"}, "west": {"uv": [4, 0, 5, 12], "texture": "#3"}, "down": {"uv": [1, 12, 4, 11], "texture": "#3"} } @@ -28,11 +26,11 @@ "name": "SideLeft", "from": [12, 4, 0], "to": [15, 16, 1], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, -23]}, + "rotation": {"angle": 0, "axis": "y", "origin": [8, 10, 0.5]}, "faces": { "north": {"uv": [1, 5, 4, 12], "texture": "#3", "cullface": "north"}, "east": {"uv": [11, 0, 12, 12], "texture": "#3"}, - "south": {"uv": [12, 0, 15, 12], "texture": "#3", "cullface": "north"}, + "south": {"uv": [12, 3, 15, 15], "texture": "#3", "cullface": "north"}, "west": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "north"}, "down": {"uv": [12, 11, 15, 12], "texture": "#3"} } @@ -52,10 +50,11 @@ "name": "SideRight", "from": [15, 4, 1], "to": [16, 16, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [15.5, 10, 8]}, "faces": { "east": {"uv": [12, 5, 15, 12], "texture": "#3", "cullface": "east"}, "south": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "east"}, - "west": {"uv": [1, 0, 4, 12], "texture": "#3", "cullface": "east"}, + "west": {"uv": [1, 3, 4, 15], "texture": "#3", "cullface": "east"}, "down": {"uv": [11, 2, 12, 5], "texture": "#3"} } }, @@ -63,10 +62,11 @@ "name": "SideLeft", "from": [15, 4, 12], "to": [16, 16, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [15.5, 10, 8]}, "faces": { "north": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "east"}, "east": {"uv": [1, 5, 4, 12], "texture": "#3", "cullface": "east"}, - "west": {"uv": [12, 0, 15, 12], "texture": "#3", "cullface": "east"}, + "west": {"uv": [12, 3, 15, 15], "texture": "#3", "cullface": "east"}, "down": {"uv": [12, 9, 13, 12], "texture": "#3"} } }, @@ -83,8 +83,9 @@ "name": "SideRight", "from": [12, 4, 15], "to": [15, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 10, 15.5]}, "faces": { - "north": {"uv": [1, 0, 4, 12], "texture": "#3", "cullface": "south"}, + "north": {"uv": [1, 3, 4, 15], "texture": "#3", "cullface": "south"}, "south": {"uv": [12, 5, 15, 12], "texture": "#3", "cullface": "south"}, "west": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "south"}, "down": {"uv": [12, 11, 15, 12], "texture": "#3"} @@ -94,8 +95,9 @@ "name": "SideLeft", "from": [1, 4, 15], "to": [4, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 10, 15.5]}, "faces": { - "north": {"uv": [12, 0, 15, 12], "texture": "#3", "cullface": "south"}, + "north": {"uv": [12, 3, 15, 15], "texture": "#3", "cullface": "south"}, "east": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "south"}, "south": {"uv": [1, 5, 4, 12], "texture": "#3", "cullface": "south"}, "down": {"uv": [1, 11, 4, 12], "texture": "#3"} @@ -114,9 +116,10 @@ "name": "SideRight", "from": [0, 4, 12], "to": [1, 16, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [0.5, 10, 8]}, "faces": { "north": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "west"}, - "east": {"uv": [1, 0, 4, 12], "texture": "#3", "cullface": "west"}, + "east": {"uv": [1, 3, 4, 15], "texture": "#3", "cullface": "west"}, "west": {"uv": [12, 5, 15, 12], "texture": "#3", "cullface": "west"}, "down": {"uv": [11, 9, 12, 12], "texture": "#3"} } @@ -125,8 +128,9 @@ "name": "SideLeft", "from": [0, 4, 1], "to": [1, 16, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [0.5, 10, 8]}, "faces": { - "east": {"uv": [12, 0, 15, 12], "texture": "#3", "cullface": "west"}, + "east": {"uv": [12, 3, 15, 15], "texture": "#3", "cullface": "west"}, "south": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "west"}, "west": {"uv": [1, 5, 4, 12], "texture": "#3", "cullface": "west"}, "down": {"uv": [4, 9, 5, 12], "texture": "#3"} @@ -187,21 +191,8 @@ "east": {"uv": [0, 12, 16, 16], "texture": "#3", "cullface": "east"}, "south": {"uv": [0, 12, 16, 16], "texture": "#3", "cullface": "south"}, "west": {"uv": [0, 12, 16, 16], "texture": "#3", "cullface": "west"}, - "up": {"uv": [0, 0, 16, 16], "texture": "#0"}, - "down": {"uv": [0, 0, 16, 16], "texture": "#1", "cullface": "down"} - } - }, - { - "name": "fluid", - "from": [1, 4, 1], - "to": [15, 16, 15], - "faces": { - "north": {"uv": [0, 0, 7, 6], "texture": "#5"}, - "east": {"uv": [0, 0, 7, 6], "texture": "#5"}, - "south": {"uv": [0, 0, 7, 6], "texture": "#5"}, - "west": {"uv": [0, 0, 7, 6], "texture": "#5"}, - "up": {"uv": [0, 0, 14, 14], "texture": "#4"}, - "down": {"uv": [0, 0, 7, 7], "texture": "#4"} + "up": {"uv": [0, 0, 16, 16], "texture": "#3"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#3", "cullface": "down"} } } ], diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_middle.json b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_middle.json index 639116c..da9f55d 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_middle.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_middle.json @@ -1,10 +1,10 @@ { + "render_type": "cutout_mipped", + "format_version": "1.21.11", "credit": "Made with Blockbench", "parent": "block/block", "textures": { "0": "createnuclear:block/reactor/frame/window", - "1": "createnuclear:fluid/uranium_flow", - "2": "createnuclear:fluid/uranium_still", "3": "createnuclear:block/reactor/casing/reactor_casing", "particle": "createnuclear:block/reactor/frame/window" }, @@ -52,22 +52,22 @@ "name": "SideRight", "from": [1, 0, 0], "to": [4, 16, 1], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, -23]}, + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0.5]}, "faces": { "north": {"uv": [12, 5, 15, 11], "texture": "#3", "cullface": "north"}, "east": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "north"}, - "south": {"uv": [1, 0, 4, 16], "texture": "#3", "cullface": "north"} + "south": {"uv": [1, 3, 4, 13], "texture": "#3", "cullface": "north"} } }, { "name": "SideLeft", "from": [12, 0, 0], "to": [15, 16, 1], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, -23]}, + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0.5]}, "faces": { "north": {"uv": [1, 5, 4, 11], "texture": "#3", "cullface": "north"}, "east": {"uv": [11, 0, 12, 16], "texture": "#3"}, - "south": {"uv": [12, 0, 16, 16], "texture": "#3", "cullface": "north"}, + "south": {"uv": [12, 3, 16, 13], "texture": "#3", "cullface": "north"}, "west": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "north"} } }, @@ -84,21 +84,23 @@ "name": "SideRight", "from": [15, 0, 1], "to": [16, 16, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [15.5, 8, 8]}, "faces": { "east": {"uv": [12, 5, 15, 11], "texture": "#3", "cullface": "east"}, "south": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "east"}, - "west": {"uv": [1, 0, 4, 16], "texture": "#3", "cullface": "east"} + "west": {"uv": [1, 3, 4, 13], "texture": "#3", "cullface": "east"} } }, { "name": "SideLeft", "from": [15, 0, 12], "to": [16, 16, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [15.5, 8, 8]}, "faces": { "north": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "east"}, "east": {"uv": [1, 5, 4, 11], "texture": "#3", "cullface": "east"}, "south": {"uv": [11, 0, 12, 16], "texture": "#3"}, - "west": {"uv": [12, 0, 15, 16], "texture": "#3", "cullface": "east"} + "west": {"uv": [12, 3, 15, 13], "texture": "#3", "cullface": "east"} } }, { @@ -113,8 +115,9 @@ "name": "SideRight", "from": [12, 0, 15], "to": [15, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 15.5]}, "faces": { - "north": {"uv": [1, 0, 4, 16], "texture": "#3", "cullface": "south"}, + "north": {"uv": [1, 3, 4, 13], "texture": "#3", "cullface": "south"}, "east": {"uv": [4, 0, 5, 16], "texture": "#3"}, "south": {"uv": [12, 5, 15, 11], "texture": "#3", "cullface": "south"}, "west": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "south"} @@ -124,8 +127,9 @@ "name": "SideLeft", "from": [1, 0, 15], "to": [4, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 15.5]}, "faces": { - "north": {"uv": [12, 0, 15, 16], "texture": "#3", "cullface": "south"}, + "north": {"uv": [12, 3, 15, 13], "texture": "#3", "cullface": "south"}, "east": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "south"}, "south": {"uv": [1, 5, 4, 11], "texture": "#3", "cullface": "south"}, "west": {"uv": [4, 0, 5, 16], "texture": "#3"} @@ -143,9 +147,10 @@ "name": "SideRight", "from": [0, 0, 12], "to": [1, 16, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [0.5, 8, 8]}, "faces": { "north": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "west"}, - "east": {"uv": [1, 0, 4, 16], "texture": "#3", "cullface": "west"}, + "east": {"uv": [1, 3, 4, 13], "texture": "#3", "cullface": "west"}, "west": {"uv": [12, 5, 15, 11], "texture": "#3", "cullface": "west"} } }, @@ -153,8 +158,9 @@ "name": "SideLeft", "from": [0, 0, 1], "to": [1, 16, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [0.5, 8, 8]}, "faces": { - "east": {"uv": [12, 0, 15, 16], "texture": "#3", "cullface": "west"}, + "east": {"uv": [12, 3, 15, 13], "texture": "#3", "cullface": "west"}, "south": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "west"}, "west": {"uv": [1, 5, 4, 11], "texture": "#3", "cullface": "west"} } @@ -166,19 +172,6 @@ "north": {"uv": [15, 5, 16, 11], "texture": "#3"}, "west": {"uv": [0, 5, 1, 11], "texture": "#3"} } - }, - { - "name": "Liquid", - "from": [1, 0, 1], - "to": [15, 16, 15], - "faces": { - "north": {"uv": [0, 0, 7, 8], "texture": "#1"}, - "east": {"uv": [0, 0, 7, 8], "texture": "#1"}, - "south": {"uv": [0, 0, 7, 8], "texture": "#1"}, - "west": {"uv": [0, 0, 7, 8], "texture": "#1"}, - "up": {"uv": [0, 0, 14, 14], "texture": "#2"}, - "down": {"uv": [0, 0, 14, 14], "texture": "#2"} - } } ], "groups": [ diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_none.json b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_none.json index 8fa0e68..5b2688e 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_none.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_none.json @@ -1,14 +1,13 @@ { + "render_type": "cutout_mipped", "credit": "Made with Blockbench", "parent": "block/block", "textures": { - "4": "createnuclear:fluid/uranium_still", - "5": "createnuclear:fluid/uranium_flow", "6": "createnuclear:block/reactor/casing/reactor_casing", - "8": "createnuclear:block/reactor/casing/casing_inner", - "9": "createnuclear:block/reactor/casing/casing_top", + "8": "createnuclear:block/reactor/casing/reactor_casing", + "9": "createnuclear:block/reactor/casing/reactor_casing", "10": "createnuclear:block/reactor/frame/window_single", - "particle": "createnuclear:fluid/uranium_still" + "particle": "createnuclear:block/reactor/casing/reactor_casing" }, "elements": [ { @@ -157,20 +156,6 @@ "up": {"uv": [0, 0, 16, 16], "texture": "#8"}, "down": {"uv": [0, 0, 16, 16], "texture": "#9", "cullface": "down"} } - }, - { - "name": "Liquid", - "from": [1, 2.9, 1], - "to": [15, 9.9, 15], - "rotation": {"angle": 0, "axis": "y", "origin": [7.5, 6.4, 8]}, - "faces": { - "north": {"uv": [0, 0, 7, 3.5], "texture": "#5"}, - "east": {"uv": [0, 0, 7, 3.5], "texture": "#5"}, - "south": {"uv": [0, 0, 7, 3.5], "texture": "#5"}, - "west": {"uv": [0, 0, 7, 3.5], "texture": "#5"}, - "up": {"uv": [0, 0, 14, 14], "texture": "#4"}, - "down": {"uv": [0, 0, 14, 14], "texture": "#4"} - } } ], "display": { diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_top.json b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_top.json index e8d897c..92d91a1 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_top.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/frame/frame_top.json @@ -1,14 +1,12 @@ { + "render_type": "cutout_mipped", + "format_version": "1.21.11", "credit": "Made with Blockbench", "parent": "block/block", "textures": { - "0": "createnuclear:block/reactor/casing/casing_inner", - "1": "createnuclear:block/reactor/casing/casing_top", "2": "createnuclear:block/reactor/frame/window", "3": "createnuclear:block/reactor/casing/reactor_casing", - "4": "createnuclear:fluid/uranium_flow", - "5": "createnuclear:fluid/uranium_still", - "particle": "createnuclear:block/reactor/casing/casing_inner" + "particle": "createnuclear:block/reactor/casing/reactor_casing" }, "elements": [ { @@ -56,7 +54,7 @@ "faces": { "north": {"uv": [12, 4, 15, 11], "texture": "#3", "cullface": "north"}, "east": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "north"}, - "south": {"uv": [1, 4, 4, 16], "texture": "#3", "cullface": "north"} + "south": {"uv": [1, 1, 4, 13], "texture": "#3", "cullface": "north"} } }, { @@ -66,7 +64,7 @@ "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, -23]}, "faces": { "north": {"uv": [1, 4, 4, 11], "texture": "#3", "cullface": "north"}, - "south": {"uv": [12, 4, 15, 16], "texture": "#3", "cullface": "north"}, + "south": {"uv": [12, 1, 15, 13], "texture": "#3", "cullface": "north"}, "west": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "north"} } }, @@ -85,7 +83,7 @@ "faces": { "east": {"uv": [12, 4, 15, 11], "texture": "#3", "cullface": "east"}, "south": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "east"}, - "west": {"uv": [1, 4, 4, 16], "texture": "#3", "cullface": "east"} + "west": {"uv": [1, 1, 4, 13], "texture": "#3", "cullface": "east"} } }, { @@ -95,7 +93,7 @@ "faces": { "north": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "east"}, "east": {"uv": [1, 4, 4, 11], "texture": "#3", "cullface": "east"}, - "west": {"uv": [12, 4, 15, 16], "texture": "#3", "cullface": "east"} + "west": {"uv": [12, 1, 15, 13], "texture": "#3", "cullface": "east"} } }, { @@ -111,7 +109,7 @@ "from": [12, 0, 15], "to": [15, 12, 16], "faces": { - "north": {"uv": [1, 4, 4, 16], "texture": "#3", "cullface": "south"}, + "north": {"uv": [1, 1, 4, 13], "texture": "#3", "cullface": "south"}, "south": {"uv": [12, 4, 15, 11], "texture": "#3", "cullface": "south"}, "west": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "south"} } @@ -121,7 +119,7 @@ "from": [1, 0, 15], "to": [4, 12, 16], "faces": { - "north": {"uv": [12, 4, 15, 16], "texture": "#3", "cullface": "south"}, + "north": {"uv": [12, 1, 15, 13], "texture": "#3", "cullface": "south"}, "east": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "south"}, "south": {"uv": [1, 4, 4, 11], "texture": "#3", "cullface": "south"} } @@ -141,7 +139,7 @@ "to": [1, 12, 15], "faces": { "north": {"uv": [11, 4, 12, 12], "texture": "#3", "cullface": "west"}, - "east": {"uv": [1, 4, 4, 16], "texture": "#3", "cullface": "west"}, + "east": {"uv": [1, 1, 4, 13], "texture": "#3", "cullface": "west"}, "west": {"uv": [12, 4, 15, 11], "texture": "#3", "cullface": "west"} } }, @@ -150,7 +148,7 @@ "from": [0, 0, 1], "to": [1, 12, 4], "faces": { - "east": {"uv": [12, 4, 15, 16], "texture": "#3", "cullface": "west"}, + "east": {"uv": [12, 1, 15, 13], "texture": "#3", "cullface": "west"}, "south": {"uv": [4, 4, 5, 12], "texture": "#3", "cullface": "west"}, "west": {"uv": [1, 4, 4, 11], "texture": "#3", "cullface": "west"} } @@ -167,27 +165,14 @@ "name": "Top", "from": [0, 12, 0], "to": [16, 16, 16], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, -23]}, + "rotation": {"angle": 0, "axis": "y", "origin": [8, 14, 8]}, "faces": { "north": {"uv": [0, 0, 16, 4], "texture": "#3", "cullface": "north"}, "east": {"uv": [0, 0, 16, 4], "texture": "#3", "cullface": "east"}, "south": {"uv": [0, 0, 16, 4], "texture": "#3", "cullface": "south"}, "west": {"uv": [0, 0, 16, 4], "texture": "#3", "cullface": "west"}, - "up": {"uv": [0, 0, 16, 16], "texture": "#1", "cullface": "up"}, - "down": {"uv": [0, 0, 16, 16], "texture": "#0"} - } - }, - { - "name": "fluid", - "from": [1, 0, 1], - "to": [15, 9, 15], - "faces": { - "north": {"uv": [0, 0, 7, 4.5], "texture": "#4"}, - "east": {"uv": [0, 0, 7, 4.5], "texture": "#4"}, - "south": {"uv": [0, 0, 7, 4.5], "texture": "#4"}, - "west": {"uv": [0, 0, 7, 4.5], "texture": "#4"}, - "up": {"uv": [0, 0, 14, 14], "texture": "#5"}, - "down": {"uv": [0, 0, 14, 14], "texture": "#5"} + "up": {"uv": [0, 0, 16, 16], "texture": "#3", "cullface": "up"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#3"} } } ], diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/output/item.json b/src/main/resources/assets/createnuclear/models/block/reactor/output/item.json index 3b55684..e6357ae 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/output/item.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/output/item.json @@ -3,7 +3,7 @@ "parent": "block/block", "textures": { "0": "createnuclear:block/reactor/output/output", - "1": "createnuclear:block/reactor/casing/casing_top", + "1": "createnuclear:block/reactor/casing/reactor_casing", "2": "createnuclear:block/reactor/casing/reactor_casing", "1_1": "create:block/axis_top", "1_0": "create:block/axis" diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/output/output.json b/src/main/resources/assets/createnuclear/models/block/reactor/output/output.json index afeb503..76cd34b 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/output/output.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/output/output.json @@ -3,7 +3,7 @@ "parent": "block/block", "textures": { "0": "createnuclear:block/reactor/output/output", - "1": "createnuclear:block/reactor/casing/casing_top", + "1": "createnuclear:block/reactor/casing/reactor_casing", "2": "createnuclear:block/reactor/casing/reactor_casing", "particle": "createnuclear:block/reactor/output/output" }, diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/output/output_vertical.json b/src/main/resources/assets/createnuclear/models/block/reactor/output/output_vertical.json index 03ad747..8cf59b3 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/output/output_vertical.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/output/output_vertical.json @@ -3,7 +3,7 @@ "parent": "block/block", "textures": { "0": "createnuclear:block/reactor/output/output", - "1": "createnuclear:block/reactor/casing/casing_top", + "1": "createnuclear:block/reactor/casing/reactor_casing", "2": "createnuclear:block/reactor/casing/reactor_casing", "particle": "createnuclear:block/reactor/output/output" }, diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/input/item.json b/src/main/resources/assets/createnuclear/models/block/reactor/rod_input/item.json similarity index 95% rename from src/main/resources/assets/createnuclear/models/block/reactor/input/item.json rename to src/main/resources/assets/createnuclear/models/block/reactor/rod_input/item.json index 7cb6aab..44a5bfd 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/input/item.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/rod_input/item.json @@ -3,8 +3,8 @@ "parent": "block/block", "textures": { "0": "createnuclear:block/reactor/casing/reactor_casing", - "1": "createnuclear:block/reactor/input/input", - "2": "createnuclear:block/reactor/casing/casing_top", + "1": "createnuclear:block/reactor/rod_input/rod_input", + "2": "createnuclear:block/reactor/casing/reactor_casing", "particle": "createnuclear:block/reactor/casing/reactor_casing" }, "elements": [ diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/input/block.json b/src/main/resources/assets/createnuclear/models/block/reactor/rod_input/rod_input.json similarity index 95% rename from src/main/resources/assets/createnuclear/models/block/reactor/input/block.json rename to src/main/resources/assets/createnuclear/models/block/reactor/rod_input/rod_input.json index ba6c0e1..e51c477 100644 --- a/src/main/resources/assets/createnuclear/models/block/reactor/input/block.json +++ b/src/main/resources/assets/createnuclear/models/block/reactor/rod_input/rod_input.json @@ -3,8 +3,8 @@ "parent": "block/block", "textures": { "0": "createnuclear:block/reactor/casing/reactor_casing", - "1": "createnuclear:block/reactor/input/input", - "2": "createnuclear:block/reactor/casing/casing_top", + "1": "createnuclear:block/reactor/rod_input/rod_input", + "2": "createnuclear:block/reactor/casing/reactor_casing", "particle": "createnuclear:block/reactor/casing/reactor_casing" }, "elements": [ diff --git a/src/main/resources/assets/createnuclear/models/block/reactor/rod_input/rod_input_vertical.json b/src/main/resources/assets/createnuclear/models/block/reactor/rod_input/rod_input_vertical.json new file mode 100644 index 0000000..754f8f5 --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/block/reactor/rod_input/rod_input_vertical.json @@ -0,0 +1,249 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "0": "createnuclear:block/reactor/rod_input/rod_input", + "1": "createnuclear:block/reactor/casing/reactor_casing", + "2": "createnuclear:block/reactor/casing/reactor_casing", + "particle": "createnuclear:block/reactor/rod_input/rod_input" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 14, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [2, 0, 16, 16], "rotation": 90, "texture": "#1"}, + "east": {"uv": [0, 0, 14, 16], "rotation": 270, "texture": "#2"}, + "south": {"uv": [2, 0, 16, 16], "rotation": 90, "texture": "#1"}, + "west": {"uv": [2, 0, 16, 16], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0, 16, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#2"} + } + }, + { + "from": [12, 14, 0], + "to": [16, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 12, 2, 16], "rotation": 90, "texture": "#1"}, + "east": {"uv": [14, 0, 16, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 0, 2, 4], "rotation": 90, "texture": "#1"}, + "west": {"uv": [3, 0, 5, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [0, 0, 4, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [12, 0, 16, 16], "texture": "#0"} + } + }, + { + "from": [0, 14, 0], + "to": [4, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 0, 2, 4], "rotation": 90, "texture": "#1"}, + "east": {"uv": [11, 0, 13, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 12, 2, 16], "rotation": 90, "texture": "#1"}, + "west": {"uv": [0, 0, 2, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [12, 0, 16, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [0, 0, 4, 16], "texture": "#0"} + } + }, + { + "from": [4, 14, 0], + "to": [5, 16, 7], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 4, 2, 5], "rotation": 90, "texture": "#1"}, + "east": {"uv": [10, 9, 12, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [4, 9, 6, 10], "rotation": 90, "texture": "#0"}, + "west": {"uv": [10, 9, 12, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [11, 9, 12, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [11, 9, 12, 16], "texture": "#0"} + } + }, + { + "from": [11, 14, 0], + "to": [12, 16, 7], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 11, 2, 12], "rotation": 90, "texture": "#1"}, + "east": {"uv": [4, 9, 6, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [4, 9, 6, 10], "rotation": 90, "texture": "#0"}, + "west": {"uv": [4, 9, 6, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [4, 9, 5, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [4, 9, 5, 16], "texture": "#0"} + } + }, + { + "from": [11, 14, 9], + "to": [12, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [4, 6, 6, 7], "rotation": 90, "texture": "#0"}, + "east": {"uv": [4, 0, 6, 7], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 11, 2, 12], "rotation": 90, "texture": "#1"}, + "west": {"uv": [4, 0, 6, 7], "rotation": 90, "texture": "#0"}, + "up": {"uv": [4, 0, 5, 7], "rotation": 180, "texture": "#0"}, + "down": {"uv": [4, 0, 5, 7], "texture": "#0"} + } + }, + { + "from": [4, 14, 9], + "to": [5, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [4, 6, 6, 7], "rotation": 90, "texture": "#0"}, + "east": {"uv": [10, 0, 12, 7], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 4, 2, 5], "rotation": 90, "texture": "#1"}, + "west": {"uv": [10, 0, 12, 7], "rotation": 90, "texture": "#0"}, + "up": {"uv": [11, 0, 12, 7], "rotation": 180, "texture": "#0"}, + "down": {"uv": [11, 0, 12, 7], "texture": "#0"} + } + }, + { + "from": [5, 14, 10], + "to": [6, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [5, 5, 7, 6], "rotation": 90, "texture": "#0"}, + "east": {"uv": [9, 0, 11, 6], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 6, 2, 7], "rotation": 90, "texture": "#1"}, + "west": {"uv": [9, 0, 11, 6], "rotation": 90, "texture": "#0"}, + "up": {"uv": [10, 0, 11, 6], "rotation": 180, "texture": "#0"}, + "down": {"uv": [10, 0, 11, 6], "texture": "#0"} + } + }, + { + "from": [10, 14, 10], + "to": [11, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [5, 5, 7, 6], "rotation": 90, "texture": "#0"}, + "east": {"uv": [5, 0, 7, 6], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 10, 2, 11], "rotation": 90, "texture": "#1"}, + "west": {"uv": [5, 0, 7, 6], "rotation": 90, "texture": "#0"}, + "up": {"uv": [5, 0, 6, 6], "rotation": 180, "texture": "#0"}, + "down": {"uv": [5, 0, 6, 6], "texture": "#0"} + } + }, + { + "from": [10, 14, 0], + "to": [11, 16, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 10, 2, 11], "rotation": 90, "texture": "#1"}, + "east": {"uv": [5, 10, 7, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [5, 10, 7, 11], "rotation": 90, "texture": "#0"}, + "west": {"uv": [5, 10, 7, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [5, 10, 6, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [5, 10, 6, 16], "texture": "#0"} + } + }, + { + "from": [9, 14, 0], + "to": [10, 16, 5], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 9, 2, 10], "rotation": 90, "texture": "#1"}, + "east": {"uv": [6, 11, 8, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [6, 11, 8, 12], "rotation": 90, "texture": "#0"}, + "west": {"uv": [6, 11, 8, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [6, 11, 7, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [6, 11, 7, 16], "texture": "#0"} + } + }, + { + "from": [6, 14, 0], + "to": [7, 16, 5], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 6, 2, 7], "rotation": 90, "texture": "#1"}, + "east": {"uv": [8, 11, 10, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [6, 11, 8, 12], "rotation": 90, "texture": "#0"}, + "west": {"uv": [8, 11, 10, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [9, 11, 10, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [9, 11, 10, 16], "texture": "#0"} + } + }, + { + "from": [6, 14, 11], + "to": [7, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [6, 4, 8, 5], "rotation": 90, "texture": "#0"}, + "east": {"uv": [8, 0, 10, 5], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 7, 2, 8], "rotation": 90, "texture": "#1"}, + "west": {"uv": [8, 0, 10, 5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [9, 0, 10, 5], "rotation": 180, "texture": "#0"}, + "down": {"uv": [9, 0, 10, 5], "texture": "#0"} + } + }, + { + "from": [9, 14, 11], + "to": [10, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [6, 4, 8, 5], "rotation": 90, "texture": "#0"}, + "east": {"uv": [6, 0, 8, 5], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 9, 2, 10], "rotation": 90, "texture": "#1"}, + "west": {"uv": [6, 0, 8, 5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [6, 0, 7, 5], "rotation": 180, "texture": "#0"}, + "down": {"uv": [6, 0, 7, 5], "texture": "#0"} + } + }, + { + "from": [7, 14, 12], + "to": [9, 16, 16], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [3, 7, 5, 9], "rotation": 90, "texture": "#0"}, + "east": {"uv": [7, 0, 9, 4], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 7, 2, 9], "rotation": 90, "texture": "#1"}, + "west": {"uv": [7, 0, 9, 4], "rotation": 90, "texture": "#0"}, + "up": {"uv": [7, 0, 9, 4], "rotation": 180, "texture": "#0"}, + "down": {"uv": [7, 0, 9, 4], "texture": "#0"} + } + }, + { + "from": [7, 14, 0], + "to": [9, 16, 4], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 7, 2, 9], "rotation": 90, "texture": "#1"}, + "east": {"uv": [7, 12, 9, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [3, 7, 5, 9], "rotation": 90, "texture": "#0"}, + "west": {"uv": [7, 12, 9, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [7, 12, 9, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [7, 12, 9, 16], "texture": "#0"} + } + }, + { + "from": [5, 14, 0], + "to": [6, 16, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 5]}, + "faces": { + "north": {"uv": [0, 5, 2, 6], "rotation": 90, "texture": "#1"}, + "east": {"uv": [9, 10, 11, 16], "rotation": 270, "texture": "#0"}, + "south": {"uv": [5, 10, 7, 11], "rotation": 90, "texture": "#0"}, + "west": {"uv": [9, 10, 11, 16], "rotation": 90, "texture": "#0"}, + "up": {"uv": [10, 10, 11, 16], "rotation": 180, "texture": "#0"}, + "down": {"uv": [10, 10, 11, 16], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "group", + "origin": [0, 0, 0], + "color": 0, + "children": [ + 0, + { + "name": "face", + "origin": [8, 8, 8], + "color": 0, + "children": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_boots/item.json b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_boots/item.json new file mode 100644 index 0000000..9e793c1 --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_boots/item.json @@ -0,0 +1,112 @@ +{ + "format_version": "1.21.11", + "credit": "Made with Blockbench", + "textures": { + "14": "createnuclear:item/default_anti_radiation_suit" + }, + "elements": [ + { + "name": "power_boot", + "from": [7.45, 0.2, 6.45], + "to": [11.95, 4.7, 10.95], + "rotation": {"angle": 0, "axis": "y", "origin": [7.8633, 12.0833, 9.7667]}, + "faces": { + "north": {"uv": [11.5, 7.16667, 10.83333, 7.83333], "texture": "#14"}, + "east": {"uv": [12.16667, 7.16667, 11.5, 7.83333], "texture": "#14"}, + "south": {"uv": [12.83333, 7.16667, 12.16667, 7.83333], "texture": "#14"}, + "west": {"uv": [10.83333, 7.16667, 10.16667, 7.83333], "texture": "#14"}, + "up": {"uv": [10.83333, 7.16667, 11.5, 6.5], "texture": "#14"}, + "down": {"uv": [11.5, 6.5, 12.16667, 7.16667], "texture": "#14"} + } + }, + { + "name": "power_boot", + "from": [3.55, 0.2, 6.45], + "to": [8.05, 4.7, 10.95], + "rotation": {"angle": 0, "axis": "y", "origin": [4, 7.75, 10]}, + "faces": { + "north": {"uv": [10.83333, 7.16667, 11.5, 7.83333], "texture": "#14"}, + "east": {"uv": [10.16667, 7.16667, 10.83333, 7.83333], "texture": "#14"}, + "south": {"uv": [12.16667, 7.16667, 12.83333, 7.83333], "texture": "#14"}, + "west": {"uv": [11.5, 7.16667, 12.16667, 7.83333], "texture": "#14"}, + "up": {"uv": [11.5, 7.16667, 10.83333, 6.5], "texture": "#14"}, + "down": {"uv": [12.16667, 6.5, 11.5, 7.16667], "texture": "#14"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [95, 0, 0], + "translation": [0, 0.5, 2.75], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [95, 0, 0], + "translation": [0, 0.5, 2.75], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "rotation": [-180, 32.75, 180], + "translation": [0, 3.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "rotation": [-180, 32.75, 180], + "translation": [0, 3.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "rotation": [85.82, -0.73, 3.41], + "translation": [0, -0.75, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [-146, 45, 180], + "scale": [0.5, 0.5, 0.5] + }, + "head": { + "translation": [0, -37.25, -4], + "scale": [0.5, 0.5, 0.5] + } + }, + "groups": [ + { + "name": "black_anti_radiation_boots", + "origin": [8, 8, 8], + "scope": 0, + "color": 0, + "children": [ + { + "name": "right_leg", + "origin": [9.67, -2.43333, 8.76667], + "scope": 0, + "color": 0, + "children": [ + { + "name": "right_boot", + "origin": [9.7, -5.3, 8.7], + "scope": 0, + "color": 0, + "children": [0] + } + ] + }, + { + "name": "left_leg", + "origin": [5.78333, -2.43333, 8.76667], + "scope": 0, + "color": 0, + "children": [ + { + "name": "left_boot", + "origin": [5.8, -5.3, 8.7], + "scope": 0, + "color": 0, + "children": [1] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_chestplate/item.json b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_chestplate/item.json new file mode 100644 index 0000000..e11f962 --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_chestplate/item.json @@ -0,0 +1,185 @@ +{ + "format_version": "1.21.11", + "credit": "Made with Blockbench", + "textures": { + "14": "createnuclear:item/default_anti_radiation_suit" + }, + "elements": [ + { + "name": "power_chestplate_inner", + "from": [3.7, 0.75, 6.2], + "to": [11.7, 12.75, 11.2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 17.75, 9]}, + "faces": { + "north": {"uv": [0.83333, 4, 2.16667, 6], "texture": "#14"}, + "east": {"uv": [0, 4, 0.83333, 6], "texture": "#14"}, + "south": {"uv": [3, 4, 4.33333, 6], "texture": "#14"}, + "west": {"uv": [2.16667, 4, 3, 6], "texture": "#14"}, + "up": {"uv": [2.16667, 4, 0.83333, 3.16667], "texture": "#14"}, + "down": {"uv": [3.5, 3.16667, 2.16667, 4], "texture": "#14"} + } + }, + { + "name": "power_chestplate_outer", + "from": [3.4, 0.75, 5.9], + "to": [12.4, 13.75, 11.9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 17.75, 9]}, + "faces": { + "north": {"uv": [1, 1, 2.5, 3.16667], "texture": "#14"}, + "east": {"uv": [0, 1, 1, 3.16667], "texture": "#14"}, + "south": {"uv": [3.5, 1, 5, 3.16667], "texture": "#14"}, + "west": {"uv": [2.5, 1, 3.5, 3.16667], "texture": "#14"}, + "up": {"uv": [2.5, 1, 1, 0], "texture": "#14"}, + "down": {"uv": [4, 0, 2.5, 1], "texture": "#14"} + } + }, + { + "name": "power_pants_inner", + "from": [3.7, 0.75, 6.3], + "to": [11.7, 12.75, 11.3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 17.75, 9]}, + "faces": { + "north": {"uv": [0.83333, 6.83333, 2.16667, 8.83333], "texture": "#14"}, + "east": {"uv": [0, 6.83333, 0.83333, 8.83333], "texture": "#14"}, + "south": {"uv": [3, 6.83333, 4.33333, 8.83333], "texture": "#14"}, + "west": {"uv": [2.16667, 6.83333, 3, 8.83333], "texture": "#14"}, + "up": {"uv": [2.16667, 6.83333, 0.83333, 6], "texture": "#14"}, + "down": {"uv": [3.5, 6, 2.16667, 6.83333], "texture": "#14"} + } + }, + { + "name": "power_pants_outer", + "from": [3.5, 0.75, 6], + "to": [12.5, 13.75, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 17.75, 9]}, + "faces": { + "north": {"uv": [11.66667, 1, 13.16667, 3.16667], "texture": "#14"}, + "east": {"uv": [10.66667, 1, 11.66667, 3.16667], "texture": "#14"}, + "south": {"uv": [14.16667, 1, 15.66667, 3.16667], "texture": "#14"}, + "west": {"uv": [13.16667, 1, 14.16667, 3.16667], "texture": "#14"}, + "up": {"uv": [13.16667, 1, 11.66667, 0], "texture": "#14"}, + "down": {"uv": [14.66667, 0, 13.16667, 1], "texture": "#14"} + } + }, + { + "name": "power_right_arm_inner", + "from": [11.45, 0.3, 6.25], + "to": [16.35, 13.2, 11.15], + "rotation": {"angle": 0, "axis": "y", "origin": [13.9, 6.75, 8.7]}, + "faces": { + "north": {"uv": [8.83333, 6, 8.16667, 8], "texture": "#14"}, + "east": {"uv": [9.5, 6, 8.83333, 8], "texture": "#14"}, + "south": {"uv": [10.16667, 6, 9.5, 8], "texture": "#14"}, + "west": {"uv": [8.16667, 6, 7.5, 8], "texture": "#14"}, + "up": {"uv": [8.16667, 6, 8.83333, 5.33333], "texture": "#14"}, + "down": {"uv": [8.83333, 5.33333, 9.5, 6], "texture": "#14"} + } + }, + { + "name": "power_right_arm_outer", + "from": [11.35, 0.2, 6.15], + "to": [16.45, 13.3, 11.25], + "rotation": {"angle": 0, "axis": "y", "origin": [13.9, 6.75, 8.7]}, + "faces": { + "north": {"uv": [6.16667, 6, 5.5, 8], "texture": "#14"}, + "east": {"uv": [6.83333, 6, 6.16667, 8], "texture": "#14"}, + "south": {"uv": [7.5, 6, 6.83333, 8], "texture": "#14"}, + "west": {"uv": [5.5, 6, 4.83333, 8], "texture": "#14"}, + "up": {"uv": [5.5, 6, 6.16667, 5.33333], "texture": "#14"}, + "down": {"uv": [6.16667, 5.33333, 6.83333, 6], "texture": "#14"} + } + }, + { + "name": "power_left_arm_inner", + "from": [-0.7, 0.35, 6.3], + "to": [4.1, 13.15, 11.1], + "rotation": {"angle": 0, "axis": "y", "origin": [1.7, 6.75, 8.7]}, + "faces": { + "north": {"uv": [8.16667, 6, 8.83333, 8], "texture": "#14"}, + "east": {"uv": [7.5, 6, 8.16667, 8], "texture": "#14"}, + "south": {"uv": [9.5, 6, 10.16667, 8], "texture": "#14"}, + "west": {"uv": [8.83333, 6, 9.5, 8], "texture": "#14"}, + "up": {"uv": [8.83333, 6, 8.16667, 5.33333], "texture": "#14"}, + "down": {"uv": [9.5, 5.33333, 8.83333, 6], "texture": "#14"} + } + }, + { + "name": "power_left_arm_outer", + "from": [-0.8, 0.25, 6.2], + "to": [4.2, 13.25, 11.2], + "rotation": {"angle": 0, "axis": "y", "origin": [1.7, 6.75, 8.7]}, + "faces": { + "north": {"uv": [5.5, 6, 6.16667, 8], "texture": "#14"}, + "east": {"uv": [4.83333, 6, 5.5, 8], "texture": "#14"}, + "south": {"uv": [6.83333, 6, 7.5, 8], "texture": "#14"}, + "west": {"uv": [6.16667, 6, 6.83333, 8], "texture": "#14"}, + "up": {"uv": [6.16667, 6, 5.5, 5.33333], "texture": "#14"}, + "down": {"uv": [6.83333, 5.33333, 6.16667, 6], "texture": "#14"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [95.07, -88.51, 4.51], + "translation": [0.5, -2, -1.75], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [95.07, -88.51, 4.51], + "translation": [0.5, -2, -1.75], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "rotation": [90, 0, 0], + "translation": [0, -0.25, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [-152.1, 49.9, 178.86], + "scale": [0.5, 0.5, 0.5] + }, + "head": { + "translation": [0, -12, -1.25] + }, + "fixed": { + "scale": [0.5, 0.5, 0.5] + } + }, + "groups": [ + { + "name": "black_anti_radiation_chestplate", + "origin": [8, 8, 8], + "scope": 0, + "color": 0, + "children": [ + { + "name": "body", + "origin": [7.825, 18.25, 8.85], + "scope": 0, + "color": 0, + "children": [0, 1, 2, 3] + }, + { + "name": "right_arm", + "origin": [13.8, 19.5, 8.8], + "scope": 0, + "color": 0, + "children": [4, 5] + }, + { + "name": "left_arm", + "origin": [1.8, 19.5, 8.8], + "scope": 0, + "color": 0, + "children": [6, 7] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_helmet/item.json b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_helmet/item.json new file mode 100644 index 0000000..2eaf62e --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_helmet/item.json @@ -0,0 +1,87 @@ +{ + "format_version": "1.21.11", + "credit": "Made with Blockbench", + "texture_size": [96, 96], + "parent": "block/block", + "textures": { + "layer0": "createnuclear:item/default_anti_radiation_suit", + "particle": "createnuclear:item/default_anti_radiation_suit" + }, + "elements": [ + { + "name": "power_helmet_inner", + "from": [3.8, 0.6, 4.8], + "to": [12.2, 9, 13.2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 4.65, 9]}, + "faces": { + "north": {"uv": [6.33333, 1.33333, 7.66667, 2.66667], "texture": "#layer0"}, + "east": {"uv": [5, 1.33333, 6.33333, 2.66667], "texture": "#layer0"}, + "south": {"uv": [9, 1.33333, 10.33333, 2.66667], "texture": "#layer0"}, + "west": {"uv": [7.66667, 1.33333, 9, 2.66667], "texture": "#layer0"}, + "up": {"uv": [7.66667, 1.33333, 6.33333, 0], "texture": "#layer0"}, + "down": {"uv": [9, 0, 7.66667, 1.33333], "texture": "#layer0"} + } + }, + { + "name": "power_helm_outer", + "from": [3.5, 0, 4.5], + "to": [12.5, 9, 13.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 4.65, 9]}, + "faces": { + "north": {"uv": [6.33333, 4, 7.66667, 5.33333], "texture": "#layer0"}, + "east": {"uv": [5, 4, 6.33333, 5.33333], "texture": "#layer0"}, + "south": {"uv": [9, 4, 10.33333, 5.33333], "texture": "#layer0"}, + "west": {"uv": [7.66667, 4, 9, 5.33333], "texture": "#layer0"}, + "up": {"uv": [7.66667, 4, 6.33333, 2.66667], "texture": "#layer0"}, + "down": {"uv": [9, 2.66667, 7.66667, 4], "texture": "#layer0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [-87, 0, 0], + "translation": [0, -2.45, 0], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [-87, 0, 0], + "translation": [0, -2.45, 0], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "rotation": [-90, 0, 0], + "translation": [0, 4, 0], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "rotation": [-90, 0, 0], + "translation": [0, 4, 0], + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "translation": [0, 2, 0], + "scale": [0.25, 0.25, 0.25] + }, + "gui": { + "rotation": [30, 135, 0], + "scale": [0.625, 0.625, 0.625] + } + }, + "groups": [ + { + "name": "black_anti_radiation_helmet", + "origin": [8, 8, 8], + "scope": 0, + "color": 0, + "children": [ + { + "name": "head", + "origin": [8, 36, 9], + "scope": 0, + "color": 0, + "children": [0, 1] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_leggings/item.json b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_leggings/item.json new file mode 100644 index 0000000..bf99f1d --- /dev/null +++ b/src/main/resources/assets/createnuclear/models/item/default_anti_radiation_leggings/item.json @@ -0,0 +1,120 @@ +{ + "format_version": "1.21.11", + "credit": "Made with Blockbench", + "textures": { + "14": "createnuclear:item/default_anti_radiation_suit" + }, + "elements": [ + { + "name": "power_leg_inner", + "from": [7.71, -2, 6.9], + "to": [11.71, 10, 10.9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 5, 9]}, + "faces": { + "north": {"uv": [6.16667, 8.66667, 5.5, 10.66667], "texture": "#14"}, + "east": {"uv": [6.83333, 8.66667, 6.16667, 10.66667], "texture": "#14"}, + "south": {"uv": [7.5, 8.66667, 6.83333, 10.66667], "texture": "#14"}, + "west": {"uv": [5.5, 8.66667, 4.83333, 10.66667], "texture": "#14"}, + "up": {"uv": [5.5, 8.66667, 6.16667, 8], "texture": "#14"}, + "down": {"uv": [6.16667, 8, 6.83333, 8.66667], "texture": "#14"} + } + }, + { + "name": "power_leg_outer", + "from": [7.35, -2.25, 6.45], + "to": [11.85, 10.25, 10.95], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 5, 9]}, + "faces": { + "north": {"uv": [8.83333, 8.66667, 8.16667, 10.66667], "texture": "#14"}, + "east": {"uv": [9.5, 8.66667, 8.83333, 10.66667], "texture": "#14"}, + "south": {"uv": [10.16667, 8.66667, 9.5, 10.66667], "texture": "#14"}, + "west": {"uv": [8.16667, 8.66667, 7.5, 10.66667], "texture": "#14"}, + "up": {"uv": [8.16667, 8.66667, 8.83333, 8], "texture": "#14"}, + "down": {"uv": [8.83333, 8, 9.5, 8.66667], "texture": "#14"} + } + }, + { + "name": "power_leg_inner", + "from": [3.75, -2, 6.9], + "to": [7.75, 10, 10.9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 5, 9]}, + "faces": { + "north": {"uv": [5.5, 8.66667, 6.16667, 10.66667], "texture": "#14"}, + "east": {"uv": [4.83333, 8.66667, 5.5, 10.66667], "texture": "#14"}, + "south": {"uv": [6.83333, 8.66667, 7.5, 10.66667], "texture": "#14"}, + "west": {"uv": [6.16667, 8.66667, 6.83333, 10.66667], "texture": "#14"}, + "up": {"uv": [6.16667, 8.66667, 5.5, 8], "texture": "#14"}, + "down": {"uv": [6.83333, 8, 6.16667, 8.66667], "texture": "#14"} + } + }, + { + "name": "power_leg_outer", + "from": [3.55, -2.25, 6.45], + "to": [8.05, 10.25, 10.95], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 5, 9]}, + "faces": { + "north": {"uv": [8.16667, 8.66667, 8.83333, 10.66667], "texture": "#14"}, + "east": {"uv": [7.5, 8.66667, 8.16667, 10.66667], "texture": "#14"}, + "south": {"uv": [9.5, 8.66667, 10.16667, 10.66667], "texture": "#14"}, + "west": {"uv": [8.83333, 8.66667, 9.5, 10.66667], "texture": "#14"}, + "up": {"uv": [8.83333, 8.66667, 8.16667, 8], "texture": "#14"}, + "down": {"uv": [9.5, 8, 8.83333, 8.66667], "texture": "#14"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [1.5, 0, 0], + "translation": [0.25, -0.5, -1], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [1.5, 0, 0], + "translation": [0.25, -0.5, -1], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "rotation": [90, 0, 0] + }, + "gui": { + "rotation": [-151.96, 54.21, 174.44], + "scale": [0.5, 0.5, 0.5] + }, + "head": { + "translation": [0, -29.5, -2.5] + }, + "fixed": { + "scale": [0.5, 0.5, 0.5] + } + }, + "groups": [ + { + "name": "black_anti_radiation_leggings", + "origin": [8, 8, 8], + "scope": 0, + "color": 0, + "children": [ + { + "name": "right_leg", + "origin": [9.67, -2.43333, 8.76667], + "scope": 0, + "color": 0, + "children": [0, 1] + }, + { + "name": "left_leg", + "origin": [5.78333, -2.43333, 8.76667], + "scope": 0, + "color": 0, + "children": [2, 3] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/particles/irradiated_particles.json b/src/main/resources/assets/createnuclear/particles/irradiated_particles.json new file mode 100644 index 0000000..4c09429 --- /dev/null +++ b/src/main/resources/assets/createnuclear/particles/irradiated_particles.json @@ -0,0 +1,5 @@ +{ + "textures": [ + "createnuclear:irradiated_particles" + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud.json b/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud.json new file mode 100644 index 0000000..6e3e151 --- /dev/null +++ b/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud.json @@ -0,0 +1,20 @@ +{ + "textures": [ + "minecraft:explosion_0", + "minecraft:explosion_1", + "minecraft:explosion_2", + "minecraft:explosion_3", + "minecraft:explosion_4", + "minecraft:explosion_5", + "minecraft:explosion_6", + "minecraft:explosion_7", + "minecraft:explosion_8", + "minecraft:explosion_9", + "minecraft:explosion_10", + "minecraft:explosion_11", + "minecraft:explosion_12", + "minecraft:explosion_13", + "minecraft:explosion_14", + "minecraft:explosion_15" + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud_explosion.json b/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud_explosion.json new file mode 100644 index 0000000..6e3e151 --- /dev/null +++ b/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud_explosion.json @@ -0,0 +1,20 @@ +{ + "textures": [ + "minecraft:explosion_0", + "minecraft:explosion_1", + "minecraft:explosion_2", + "minecraft:explosion_3", + "minecraft:explosion_4", + "minecraft:explosion_5", + "minecraft:explosion_6", + "minecraft:explosion_7", + "minecraft:explosion_8", + "minecraft:explosion_9", + "minecraft:explosion_10", + "minecraft:explosion_11", + "minecraft:explosion_12", + "minecraft:explosion_13", + "minecraft:explosion_14", + "minecraft:explosion_15" + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud_smoke.json b/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud_smoke.json new file mode 100644 index 0000000..2712610 --- /dev/null +++ b/src/main/resources/assets/createnuclear/particles/nuclear_mushroom_cloud_smoke.json @@ -0,0 +1,12 @@ +{ + "textures": [ + "minecraft:generic_7", + "minecraft:generic_6", + "minecraft:generic_5", + "minecraft:generic_4", + "minecraft:generic_3", + "minecraft:generic_2", + "minecraft:generic_1", + "minecraft:generic_0" + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t1_ponder.nbt b/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t1_ponder.nbt new file mode 100644 index 0000000..44cd6ae Binary files /dev/null and b/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t1_ponder.nbt differ diff --git a/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t2_ponder.nbt b/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t2_ponder.nbt new file mode 100644 index 0000000..d3b8229 Binary files /dev/null and b/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t2_ponder.nbt differ diff --git a/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t3_ponder.nbt b/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t3_ponder.nbt new file mode 100644 index 0000000..9676d99 Binary files /dev/null and b/src/main/resources/assets/createnuclear/ponder/reactor/reactor_t3_ponder.nbt differ diff --git a/src/main/resources/assets/createnuclear/ponder/reactor/setup.nbt b/src/main/resources/assets/createnuclear/ponder/reactor/setup.nbt index 227cf19..5e25912 100644 Binary files a/src/main/resources/assets/createnuclear/ponder/reactor/setup.nbt and b/src/main/resources/assets/createnuclear/ponder/reactor/setup.nbt differ diff --git a/src/main/resources/assets/createnuclear/sounds/alarm/alarm.ogg b/src/main/resources/assets/createnuclear/sounds/alarm/alarm.ogg new file mode 100644 index 0000000..34b8230 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/alarm/alarm.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/biomes/wasteland.ogg b/src/main/resources/assets/createnuclear/sounds/biomes/wasteland.ogg new file mode 100644 index 0000000..d1947d6 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/biomes/wasteland.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/explosion/large_nuclear_explosion.ogg b/src/main/resources/assets/createnuclear/sounds/explosion/large_nuclear_explosion.ogg new file mode 100644 index 0000000..312530d Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/explosion/large_nuclear_explosion.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/explosion/main.ogg b/src/main/resources/assets/createnuclear/sounds/explosion/main.ogg new file mode 100644 index 0000000..0038654 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/explosion/main.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/explosion/nuclear_explosion.ogg b/src/main/resources/assets/createnuclear/sounds/explosion/nuclear_explosion.ogg new file mode 100644 index 0000000..4efa58c Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/explosion/nuclear_explosion.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/explosion/nuclear_explosion_rumble.ogg b/src/main/resources/assets/createnuclear/sounds/explosion/nuclear_explosion_rumble.ogg new file mode 100644 index 0000000..835045f Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/explosion/nuclear_explosion_rumble.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/explosion/ringing.ogg b/src/main/resources/assets/createnuclear/sounds/explosion/ringing.ogg new file mode 100644 index 0000000..d6a5ce4 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/explosion/ringing.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/explosion/rumble.ogg b/src/main/resources/assets/createnuclear/sounds/explosion/rumble.ogg new file mode 100644 index 0000000..bde039e Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/explosion/rumble.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/explosion/shockwave.ogg b/src/main/resources/assets/createnuclear/sounds/explosion/shockwave.ogg new file mode 100644 index 0000000..4703edd Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/explosion/shockwave.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/geiger/high.ogg b/src/main/resources/assets/createnuclear/sounds/geiger/high.ogg new file mode 100644 index 0000000..88ca8b7 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/geiger/high.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/geiger/low.ogg b/src/main/resources/assets/createnuclear/sounds/geiger/low.ogg new file mode 100644 index 0000000..7140009 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/geiger/low.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/geiger/medium.ogg b/src/main/resources/assets/createnuclear/sounds/geiger/medium.ogg new file mode 100644 index 0000000..8d6e064 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/geiger/medium.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/reacteur/activation.ogg b/src/main/resources/assets/createnuclear/sounds/reacteur/activation.ogg new file mode 100644 index 0000000..b043866 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/reacteur/activation.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/reacteur/assemble_deassemble/motor_assemble.ogg b/src/main/resources/assets/createnuclear/sounds/reacteur/assemble_deassemble/motor_assemble.ogg new file mode 100644 index 0000000..3d7ac04 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/reacteur/assemble_deassemble/motor_assemble.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/reacteur/assemble_deassemble/motor_disassemble.ogg b/src/main/resources/assets/createnuclear/sounds/reacteur/assemble_deassemble/motor_disassemble.ogg new file mode 100644 index 0000000..8e1db4c Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/reacteur/assemble_deassemble/motor_disassemble.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/reacteur/running.ogg b/src/main/resources/assets/createnuclear/sounds/reacteur/running.ogg new file mode 100644 index 0000000..761f9df Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/reacteur/running.ogg differ diff --git a/src/main/resources/assets/createnuclear/sounds/reacteur/shut_off.ogg b/src/main/resources/assets/createnuclear/sounds/reacteur/shut_off.ogg new file mode 100644 index 0000000..249f1f2 Binary files /dev/null and b/src/main/resources/assets/createnuclear/sounds/reacteur/shut_off.ogg differ diff --git a/src/main/resources/assets/createnuclear/textures/block/deepslate_nitrate_ore.png b/src/main/resources/assets/createnuclear/textures/block/deepslate_nitrate_ore.png new file mode 100644 index 0000000..6027926 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/deepslate_nitrate_ore.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/deepslate_thorium_ore.png b/src/main/resources/assets/createnuclear/textures/block/deepslate_thorium_ore.png new file mode 100644 index 0000000..875565b Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/deepslate_thorium_ore.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/nitrate_ore.png b/src/main/resources/assets/createnuclear/textures/block/nitrate_ore.png new file mode 100644 index 0000000..3eda18f Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/nitrate_ore.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_basketweave.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_basketweave.png index 18b2c25..a6e2fc1 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_basketweave.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_basketweave.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_bond.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_bond.png index 6aac6f0..90d0531 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_bond.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_bond.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_indented.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_indented.png index 007c894..77f6814 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_indented.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/alternatives/autunite_cut_indented.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/brick/autunite_cut_brick.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/brick/autunite_cut_brick.png index 34a296d..630f7cd 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/brick/autunite_cut_brick.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/brick/autunite_cut_brick.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap.png index 007c894..77f6814 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap_connected.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap_connected.png index 02274e1..59c887b 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap_connected.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cap/autunite_cut_cap_connected.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cut/autunite_cut.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cut/autunite_cut.png index 3cfc1ae..a18e994 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cut/autunite_cut.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/cut/autunite_cut.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered.png index 3f27e32..36f6613 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered_connected.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered_connected.png index 0f9be1b..102073b 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered_connected.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/layered/autunite_cut_layered_connected.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_0.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_0.png index 90aab08..bd3544d 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_0.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_0.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_1.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_1.png index 823d624..ce80542 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_1.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_1.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_2.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_2.png index 97597db..4598170 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_2.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_2.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_3.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_3.png index 14f577a..bb391e7 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_3.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/natural/autunite_3.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar.png index d614ad1..6187054 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar_connected.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar_connected.png index 46b045f..56d263a 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar_connected.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/pillar/autunite_cut_pillar_connected.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/polished/autunite_cut_polished.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/polished/autunite_cut_polished.png index 3980d01..8504258 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/polished/autunite_cut_polished.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/polished/autunite_cut_polished.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/slab/autunite_cut_slab.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/slab/autunite_cut_slab.png index 2914c80..9c1c53d 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/slab/autunite_cut_slab.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/slab/autunite_cut_slab.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/small_brick/autunite_cut_small_brick.png b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/small_brick/autunite_cut_small_brick.png index 1964313..fa05afc 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/small_brick/autunite_cut_small_brick.png and b/src/main/resources/assets/createnuclear/textures/block/palettes/stone_types/small_brick/autunite_cut_small_brick.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/raw_thorium_block.png b/src/main/resources/assets/createnuclear/textures/block/raw_thorium_block.png new file mode 100644 index 0000000..88c8ce6 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/raw_thorium_block.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor/casing/reactor_casing_connected.png b/src/main/resources/assets/createnuclear/textures/block/reactor/casing/reactor_casing_connected.png index 10473d8..5f6f8ba 100644 Binary files a/src/main/resources/assets/createnuclear/textures/block/reactor/casing/reactor_casing_connected.png and b/src/main/resources/assets/createnuclear/textures/block/reactor/casing/reactor_casing_connected.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_off.png b/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_off.png new file mode 100644 index 0000000..354c206 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_off.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_on.png b/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_on.png new file mode 100644 index 0000000..33a3fdc Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_on.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_standby.png b/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_standby.png new file mode 100644 index 0000000..7b9c729 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/reactor/controller/controller_panel_standby.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor/fluid_input/fluid_input.png b/src/main/resources/assets/createnuclear/textures/block/reactor/fluid_input/fluid_input.png new file mode 100644 index 0000000..e041d3d Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/reactor/fluid_input/fluid_input.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor/fluid_input/input.png b/src/main/resources/assets/createnuclear/textures/block/reactor/fluid_input/input.png new file mode 100644 index 0000000..e041d3d Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/reactor/fluid_input/input.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor/input/input.png b/src/main/resources/assets/createnuclear/textures/block/reactor/rod_input/rod_input.png similarity index 100% rename from src/main/resources/assets/createnuclear/textures/block/reactor/input/input.png rename to src/main/resources/assets/createnuclear/textures/block/reactor/rod_input/rod_input.png diff --git a/src/main/resources/assets/createnuclear/textures/block/reactor_alarm.png b/src/main/resources/assets/createnuclear/textures/block/reactor_alarm.png new file mode 100644 index 0000000..b57826a Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/reactor_alarm.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/thorium_block.png b/src/main/resources/assets/createnuclear/textures/block/thorium_block.png new file mode 100644 index 0000000..35b943f Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/thorium_block.png differ diff --git a/src/main/resources/assets/createnuclear/textures/block/thorium_ore.png b/src/main/resources/assets/createnuclear/textures/block/thorium_ore.png new file mode 100644 index 0000000..0a903ef Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/block/thorium_ore.png differ diff --git a/src/main/resources/assets/createnuclear/textures/entity/irradiated_cow.png b/src/main/resources/assets/createnuclear/textures/entity/irradiated_cow.png new file mode 100644 index 0000000..cada498 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/entity/irradiated_cow.png differ diff --git a/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_flow.png b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_flow.png new file mode 100644 index 0000000..646a785 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_flow.png differ diff --git a/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_flow.png.mcmeta b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_flow.png.mcmeta new file mode 100644 index 0000000..f6730e3 --- /dev/null +++ b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_flow.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 4 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_still.png b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_still.png new file mode 100644 index 0000000..a232dff Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_still.png differ diff --git a/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_still.png.mcmeta b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_still.png.mcmeta new file mode 100644 index 0000000..b84e69f --- /dev/null +++ b/src/main/resources/assets/createnuclear/textures/fluid/nitrogen_still.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 4 + } +} diff --git a/src/main/resources/assets/createnuclear/textures/fluid/thorium_flow.png b/src/main/resources/assets/createnuclear/textures/fluid/thorium_flow.png new file mode 100644 index 0000000..d29c920 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/fluid/thorium_flow.png differ diff --git a/src/main/resources/assets/createnuclear/textures/fluid/thorium_flow.png.mcmeta b/src/main/resources/assets/createnuclear/textures/fluid/thorium_flow.png.mcmeta new file mode 100644 index 0000000..f6730e3 --- /dev/null +++ b/src/main/resources/assets/createnuclear/textures/fluid/thorium_flow.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 4 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/createnuclear/textures/fluid/thorium_still.png b/src/main/resources/assets/createnuclear/textures/fluid/thorium_still.png new file mode 100644 index 0000000..6535408 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/fluid/thorium_still.png differ diff --git a/src/main/resources/assets/createnuclear/textures/fluid/thorium_still.png.mcmeta b/src/main/resources/assets/createnuclear/textures/fluid/thorium_still.png.mcmeta new file mode 100644 index 0000000..b84e69f --- /dev/null +++ b/src/main/resources/assets/createnuclear/textures/fluid/thorium_still.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 4 + } +} diff --git a/src/main/resources/assets/createnuclear/textures/gui/advancements/backgrounds/background_advancement.png b/src/main/resources/assets/createnuclear/textures/gui/advancements/backgrounds/background_advancement.png new file mode 100644 index 0000000..d869a98 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/gui/advancements/backgrounds/background_advancement.png differ diff --git a/src/main/resources/assets/createnuclear/textures/gui/icons.png b/src/main/resources/assets/createnuclear/textures/gui/icons.png new file mode 100644 index 0000000..b971365 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/gui/icons.png differ diff --git a/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_full.png b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_full.png new file mode 100644 index 0000000..cc7463e Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_full.png differ diff --git a/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_half.png b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_half.png new file mode 100644 index 0000000..893d9f9 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_half.png differ diff --git a/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_hardcore_full.png b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_hardcore_full.png new file mode 100644 index 0000000..647ee4d Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_hardcore_full.png differ diff --git a/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_hardcore_half.png b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_hardcore_half.png new file mode 100644 index 0000000..0e8c3f8 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/gui/sprites/hud/heart/radiation_hardcore_half.png differ diff --git a/src/main/resources/assets/createnuclear/textures/gui/storage-slot.png b/src/main/resources/assets/createnuclear/textures/gui/storage-slot.png index eb8699d..39a4e50 100644 Binary files a/src/main/resources/assets/createnuclear/textures/gui/storage-slot.png and b/src/main/resources/assets/createnuclear/textures/gui/storage-slot.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/anti_radiation_boots.png b/src/main/resources/assets/createnuclear/textures/item/armors/anti_radiation_boots.png deleted file mode 100644 index 5e2aafa..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/anti_radiation_boots.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/black_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/black_anti_radiation_chestplate.png deleted file mode 100644 index 2e376a1..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/black_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/blue_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/blue_anti_radiation_chestplate.png deleted file mode 100644 index fb0136a..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/blue_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/brown_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/brown_anti_radiation_chestplate.png deleted file mode 100644 index d5f3bb1..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/brown_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/cyan_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/cyan_anti_radiation_chestplate.png deleted file mode 100644 index 59af829..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/cyan_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/gray_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/gray_anti_radiation_chestplate.png deleted file mode 100644 index 7981b59..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/gray_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/green_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/green_anti_radiation_chestplate.png deleted file mode 100644 index 707258c..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/green_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/light_blue_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/light_blue_anti_radiation_chestplate.png deleted file mode 100644 index c943950..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/light_blue_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/light_gray_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/light_gray_anti_radiation_chestplate.png deleted file mode 100644 index 6f37af5..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/light_gray_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/lime_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/lime_anti_radiation_chestplate.png deleted file mode 100644 index 6f8da44..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/lime_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/magenta_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/magenta_anti_radiation_chestplate.png deleted file mode 100644 index b0bb709..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/magenta_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/orange_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/orange_anti_radiation_chestplate.png deleted file mode 100644 index b83a217..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/orange_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/pink_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/pink_anti_radiation_chestplate.png deleted file mode 100644 index c6b096c..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/pink_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/purple_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/purple_anti_radiation_chestplate.png deleted file mode 100644 index 4e21121..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/purple_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/red_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/red_anti_radiation_chestplate.png deleted file mode 100644 index 0d73ee6..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/red_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/white_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/white_anti_radiation_chestplate.png deleted file mode 100644 index 581bbbd..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/white_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/yellow_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/yellow_anti_radiation_chestplate.png deleted file mode 100644 index 8429f5d..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/chestplates/yellow_anti_radiation_chestplate.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_boots.png b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_boots.png new file mode 100644 index 0000000..b1fa59c Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_boots.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_chestplate.png b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_chestplate.png new file mode 100644 index 0000000..380c01e Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_chestplate.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_helmet.png new file mode 100644 index 0000000..3431a84 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_helmet.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_leggings.png new file mode 100644 index 0000000..9f56680 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/armors/default_anti_radiation_leggings.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/black_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/black_anti_radiation_helmet.png deleted file mode 100644 index 98a0676..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/black_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/blue_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/blue_anti_radiation_helmet.png deleted file mode 100644 index 5aa8e97..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/blue_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/brown_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/brown_anti_radiation_helmet.png deleted file mode 100644 index f809bf5..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/brown_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/cyan_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/cyan_anti_radiation_helmet.png deleted file mode 100644 index 0e93c48..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/cyan_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/gray_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/gray_anti_radiation_helmet.png deleted file mode 100644 index 0cbaa9e..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/gray_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/green_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/green_anti_radiation_helmet.png deleted file mode 100644 index 69cc070..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/green_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/light_blue_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/light_blue_anti_radiation_helmet.png deleted file mode 100644 index 1b9a7c1..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/light_blue_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/light_gray_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/light_gray_anti_radiation_helmet.png deleted file mode 100644 index c426809..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/light_gray_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/lime_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/lime_anti_radiation_helmet.png deleted file mode 100644 index 28333da..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/lime_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/magenta_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/magenta_anti_radiation_helmet.png deleted file mode 100644 index 79eccd8..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/magenta_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/orange_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/orange_anti_radiation_helmet.png deleted file mode 100644 index a754982..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/orange_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/pink_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/pink_anti_radiation_helmet.png deleted file mode 100644 index 26955cd..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/pink_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/purple_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/purple_anti_radiation_helmet.png deleted file mode 100644 index cb9f6ae..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/purple_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/red_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/red_anti_radiation_helmet.png deleted file mode 100644 index baadfee..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/red_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/white_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/white_anti_radiation_helmet.png deleted file mode 100644 index 9528357..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/white_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/yellow_anti_radiation_helmet.png b/src/main/resources/assets/createnuclear/textures/item/armors/helmets/yellow_anti_radiation_helmet.png deleted file mode 100644 index 518fb04..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/helmets/yellow_anti_radiation_helmet.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/black_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/black_anti_radiation_leggings.png deleted file mode 100644 index de76aae..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/black_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/blue_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/blue_anti_radiation_leggings.png deleted file mode 100644 index 92a087c..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/blue_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/brown_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/brown_anti_radiation_leggings.png deleted file mode 100644 index 4935e7a..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/brown_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/cyan_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/cyan_anti_radiation_leggings.png deleted file mode 100644 index 7aaed84..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/cyan_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/gray_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/gray_anti_radiation_leggings.png deleted file mode 100644 index 01e1a84..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/gray_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/green_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/green_anti_radiation_leggings.png deleted file mode 100644 index ab34ea4..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/green_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/light_blue_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/light_blue_anti_radiation_leggings.png deleted file mode 100644 index 5230cf8..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/light_blue_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/light_gray_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/light_gray_anti_radiation_leggings.png deleted file mode 100644 index 29edbb2..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/light_gray_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/lime_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/lime_anti_radiation_leggings.png deleted file mode 100644 index ed28ad8..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/lime_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/magenta_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/magenta_anti_radiation_leggings.png deleted file mode 100644 index e0db666..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/magenta_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/orange_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/orange_anti_radiation_leggings.png deleted file mode 100644 index 90e25f1..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/orange_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/pink_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/pink_anti_radiation_leggings.png deleted file mode 100644 index f0c7414..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/pink_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/purple_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/purple_anti_radiation_leggings.png deleted file mode 100644 index a2fa6ec..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/purple_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/red_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/red_anti_radiation_leggings.png deleted file mode 100644 index 8f4ac05..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/red_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/white_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/white_anti_radiation_leggings.png deleted file mode 100644 index 1763c81..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/white_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/yellow_anti_radiation_leggings.png b/src/main/resources/assets/createnuclear/textures/item/armors/leggings/yellow_anti_radiation_leggings.png deleted file mode 100644 index 63178c7..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/item/armors/leggings/yellow_anti_radiation_leggings.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/empty.png b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/empty.png new file mode 100644 index 0000000..fd63d51 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/empty.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/full.png b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/full.png new file mode 100644 index 0000000..f9298c5 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/full.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/half.png b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/half.png new file mode 100644 index 0000000..1f22e80 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/half.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/quarter.png b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/quarter.png new file mode 100644 index 0000000..5203618 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/quarter.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/three_quarters.png b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/three_quarters.png new file mode 100644 index 0000000..fc46c18 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/biome_irradiation_extractor/three_quarters.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/cooled_nitrogen_concentrate.png b/src/main/resources/assets/createnuclear/textures/item/cooled_nitrogen_concentrate.png new file mode 100644 index 0000000..51e11a2 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/cooled_nitrogen_concentrate.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/graphene.png b/src/main/resources/assets/createnuclear/textures/item/graphene.png index 78a9cd7..0992167 100644 Binary files a/src/main/resources/assets/createnuclear/textures/item/graphene.png and b/src/main/resources/assets/createnuclear/textures/item/graphene.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/nitrate.png b/src/main/resources/assets/createnuclear/textures/item/nitrate.png new file mode 100644 index 0000000..bb2f958 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/nitrate.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/nitrogen_bucket.png b/src/main/resources/assets/createnuclear/textures/item/nitrogen_bucket.png new file mode 100644 index 0000000..691532b Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/nitrogen_bucket.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/nitrogen_concentrate.png b/src/main/resources/assets/createnuclear/textures/item/nitrogen_concentrate.png new file mode 100644 index 0000000..446db53 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/nitrogen_concentrate.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/raw_thorium.png b/src/main/resources/assets/createnuclear/textures/item/raw_thorium.png new file mode 100644 index 0000000..231eff2 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/raw_thorium.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/reinforced_glass_bottle.png b/src/main/resources/assets/createnuclear/textures/item/reinforced_glass_bottle.png new file mode 100644 index 0000000..2cfcd77 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/reinforced_glass_bottle.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/thorium_bucket.png b/src/main/resources/assets/createnuclear/textures/item/thorium_bucket.png new file mode 100644 index 0000000..80a9407 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/thorium_bucket.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/thorium_dust.png b/src/main/resources/assets/createnuclear/textures/item/thorium_dust.png new file mode 100644 index 0000000..799029c Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/thorium_dust.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/thorium_ingot.png b/src/main/resources/assets/createnuclear/textures/item/thorium_ingot.png new file mode 100644 index 0000000..0a430f3 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/thorium_ingot.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/thorium_nugget.png b/src/main/resources/assets/createnuclear/textures/item/thorium_nugget.png new file mode 100644 index 0000000..276e377 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/thorium_nugget.png differ diff --git a/src/main/resources/assets/createnuclear/textures/item/thorium_rod.png b/src/main/resources/assets/createnuclear/textures/item/thorium_rod.png new file mode 100644 index 0000000..ff27f25 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/item/thorium_rod.png differ diff --git a/src/main/resources/assets/createnuclear/textures/misc/bomb_flash.png b/src/main/resources/assets/createnuclear/textures/misc/bomb_flash.png new file mode 100644 index 0000000..12e3f0c Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/misc/bomb_flash.png differ diff --git a/src/main/resources/assets/createnuclear/textures/mob_effect/iodine.png b/src/main/resources/assets/createnuclear/textures/mob_effect/iodine.png new file mode 100644 index 0000000..276fa23 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/mob_effect/iodine.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit.png new file mode 100644 index 0000000..6204089 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit_layer_1.png deleted file mode 100644 index 6346835..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit_layer_2.png deleted file mode 100644 index 86f1b18..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/black_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit.png new file mode 100644 index 0000000..9122ba8 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit_layer_1.png deleted file mode 100644 index 3a5ec07..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit_layer_2.png deleted file mode 100644 index 34c8ec2..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/blue_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit.png new file mode 100644 index 0000000..cd2c4e9 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit_layer_1.png deleted file mode 100644 index fa70b27..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit_layer_2.png deleted file mode 100644 index 62ee17e..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/brown_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit.png new file mode 100644 index 0000000..4f3a193 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit_layer_1.png deleted file mode 100644 index 3dc08ee..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit_layer_2.png deleted file mode 100644 index 0217d65..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/cyan_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/default_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/default_anti_radiation_suit.png new file mode 100644 index 0000000..b8a89e1 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/default_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit.png new file mode 100644 index 0000000..e5038e3 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit_layer_1.png deleted file mode 100644 index de67016..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit_layer_2.png deleted file mode 100644 index 869018c..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/gray_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit.png new file mode 100644 index 0000000..8eb3abd Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit_layer_1.png deleted file mode 100644 index 7bd7d6e..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit_layer_2.png deleted file mode 100644 index 7ff7725..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/green_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit.png new file mode 100644 index 0000000..4584c6c Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit_layer_1.png deleted file mode 100644 index 76de36b..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit_layer_2.png deleted file mode 100644 index 734f850..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/light_blue_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit.png new file mode 100644 index 0000000..2b2c858 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit_layer_1.png deleted file mode 100644 index eb35140..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit_layer_2.png deleted file mode 100644 index 1df7534..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/light_gray_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit.png new file mode 100644 index 0000000..376edba Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit_layer_1.png deleted file mode 100644 index 24dab4e..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit_layer_2.png deleted file mode 100644 index dfdef32..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/lime_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit.png new file mode 100644 index 0000000..91c1a8c Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit_layer_1.png deleted file mode 100644 index 980f1fc..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit_layer_2.png deleted file mode 100644 index 5f3dc23..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/magenta_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit.png new file mode 100644 index 0000000..c8b3d0c Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit_layer_1.png deleted file mode 100644 index 777bcf1..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit_layer_2.png deleted file mode 100644 index 4066a8c..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/orange_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit.png new file mode 100644 index 0000000..de04539 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit_layer_1.png deleted file mode 100644 index 5eeba4c..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit_layer_2.png deleted file mode 100644 index 67051d5..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/pink_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit.png new file mode 100644 index 0000000..d2a0244 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit_layer_1.png deleted file mode 100644 index 39fd597..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit_layer_2.png deleted file mode 100644 index a8d8dc5..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/purple_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit.png new file mode 100644 index 0000000..2b0d6b8 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit_layer_1.png deleted file mode 100644 index 4ac5d3a..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit_layer_2.png deleted file mode 100644 index 5687e57..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/red_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit.png new file mode 100644 index 0000000..741fad4 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit_layer_1.png deleted file mode 100644 index 4e433f5..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit_layer_2.png deleted file mode 100644 index 1bbdc3e..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/white_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit.png b/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit.png new file mode 100644 index 0000000..2caad2b Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit.png differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit_layer_1.png b/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit_layer_1.png deleted file mode 100644 index d13022a..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit_layer_1.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit_layer_2.png b/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit_layer_2.png deleted file mode 100644 index ab3b52d..0000000 Binary files a/src/main/resources/assets/createnuclear/textures/models/armor/yellow_anti_radiation_suit_layer_2.png and /dev/null differ diff --git a/src/main/resources/assets/createnuclear/textures/particle/irradiated_particles.png b/src/main/resources/assets/createnuclear/textures/particle/irradiated_particles.png new file mode 100644 index 0000000..c3e8018 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/particle/irradiated_particles.png differ diff --git a/src/main/resources/assets/createnuclear/textures/particle/nuclear_mushroom_cloud.png b/src/main/resources/assets/createnuclear/textures/particle/nuclear_mushroom_cloud.png new file mode 100644 index 0000000..2904e00 Binary files /dev/null and b/src/main/resources/assets/createnuclear/textures/particle/nuclear_mushroom_cloud.png differ diff --git a/src/main/resources/assets/minecraft/atlases/blocks.json b/src/main/resources/assets/minecraft/atlases/blocks.json index 0b97649..fcd17e3 100644 --- a/src/main/resources/assets/minecraft/atlases/blocks.json +++ b/src/main/resources/assets/minecraft/atlases/blocks.json @@ -1,5 +1,13 @@ { "sources": [ + { + "type": "single", + "resource": "createnuclear:fluid/thorium_flow" + }, + { + "type": "single", + "resource": "createnuclear:fluid/thorium_still" + }, { "type": "single", "resource": "createnuclear:fluid/uranium_flow" @@ -7,6 +15,14 @@ { "type": "single", "resource": "createnuclear:fluid/uranium_still" + }, + { + "type": "single", + "resource": "createnuclear:fluid/nitrogen_flow" + }, + { + "type": "single", + "resource": "createnuclear:fluid/nitrogen_still" } ] } \ No newline at end of file diff --git a/src/main/resources/createnuclear.neoforge.mixins.json b/src/main/resources/createnuclear.neoforge.mixins.json index 02224e2..9add340 100644 --- a/src/main/resources/createnuclear.neoforge.mixins.json +++ b/src/main/resources/createnuclear.neoforge.mixins.json @@ -5,9 +5,12 @@ "package": "net.nuclearteam.createnuclear.foundation.mixin", "compatibilityLevel": "JAVA_21", "mixins": [ - "BaseFireBlockMixin" + "BaseFireBlockMixin", + "SmithingTransformRecipeMixin" ], "client": [ + "client.RadiationHeartMixin", + "client.AntiRadiationArmorTextureMixin" ], "injectors": { "defaultRequire": 1