diff options
Diffstat (limited to 'defaults')
42 files changed, 2808 insertions, 0 deletions
diff --git a/defaults/build.gradle b/defaults/build.gradle new file mode 100644 index 0000000..2e88283 --- /dev/null +++ b/defaults/build.gradle @@ -0,0 +1,13 @@ +archivesBaseName = "citresewn-defaults" + +dependencies { + implementation project(path: ":", configuration: "namedElements") +} + +loom { + accessWidenerPath = file("src/main/resources/citresewn-defaults.accesswidener") + + runConfigs.configureEach { + ideConfigGenerated = true + } +}
\ No newline at end of file diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamage.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamage.java new file mode 100644 index 0000000..c5c31f9 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamage.java @@ -0,0 +1,46 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; + +import java.util.Set; + +public class ConditionDamage extends IntegerCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionDamage> CONTAINER = new CITConditionContainer<>(ConditionDamage.class, ConditionDamage::new, + "damage"); + + protected Integer mask = null; + + public ConditionDamage() { + super(true, false, true); + } + + @Override + protected int getValue(CITContext context) { + int value = context.stack.isDamageable() ? context.stack.getDamage() : 0; + if (mask != null) + value &= mask; + return value; + } + + @Override + protected int getPercentageTotalValue(CITContext context) { + return context.stack.isDamageable() ? context.stack.getItem().getMaxDamage() : 0; + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionDamageMask.class); + } + + @Override + public <T extends CITCondition> T modifySibling(T sibling) { + if (sibling instanceof ConditionDamageMask damageMask) + this.mask = damageMask.getMask(); + return null; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamageMask.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamageMask.java new file mode 100644 index 0000000..6adde08 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionDamageMask.java @@ -0,0 +1,38 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; + +import java.util.Set; + +public class ConditionDamageMask extends IntegerCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionDamageMask> CONTAINER = new CITConditionContainer<>(ConditionDamageMask.class, ConditionDamageMask::new, + "damage_mask", "damageMask"); + + public ConditionDamageMask() { + super(false, false, false); + } + + @Override + protected int getValue(CITContext context) { + return 0; + } + + @Override + public boolean test(CITContext context) { + return true; + } + + public int getMask() { + return this.min; + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionDamage.class); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantmentLevels.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantmentLevels.java new file mode 100644 index 0000000..da6b4f4 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantmentLevels.java @@ -0,0 +1,62 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.util.Identifier; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.ListCondition; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class ConditionEnchantmentLevels extends ListCondition<ConditionEnchantmentLevels.EnchantmentLevelCondition> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionEnchantmentLevels> CONTAINER = new CITConditionContainer<>(ConditionEnchantmentLevels.class, ConditionEnchantmentLevels::new, + "enchantment_levels", "enchantmentLevels"); + + protected Set<Identifier> enchantments = null; + + public ConditionEnchantmentLevels() { + super(EnchantmentLevelCondition.class, EnchantmentLevelCondition::new); + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionEnchantments.class); + } + + @Override + public <T extends CITCondition> T modifySibling(T sibling) { + if (sibling instanceof ConditionEnchantments conditionEnchantments) { + if (enchantments == null) { + enchantments = new HashSet<>(); + for (EnchantmentLevelCondition subCondition : this.conditions) + subCondition.enchantments = enchantments; + } + enchantments.addAll(Arrays.asList(conditionEnchantments.getEnchantments())); + } + + return sibling; + } + + protected static class EnchantmentLevelCondition extends IntegerCondition { + protected Set<Identifier> enchantments = null; + + protected EnchantmentLevelCondition() { + super(true, false, false); + } + + @Override + public boolean test(CITContext context) { + for (Map.Entry<Identifier, Integer> entry : context.enchantments().entrySet()) + if ((enchantments == null || enchantments.contains(entry.getKey())) && entry.getValue() != null && (range ? min <= entry.getValue() && entry.getValue() <= max : entry.getValue() == min)) + return true; + + return false; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantments.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantments.java new file mode 100644 index 0000000..301f9c1 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionEnchantments.java @@ -0,0 +1,59 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IdentifierCondition; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.ListCondition; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue; + +import java.util.Set; + +public class ConditionEnchantments extends ListCondition<ConditionEnchantments.EnchantmentCondition> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionEnchantments> CONTAINER = new CITConditionContainer<>(ConditionEnchantments.class, ConditionEnchantments::new, + "enchantments", "enchantmentIDs"); + + public ConditionEnchantments() { + super(EnchantmentCondition.class, EnchantmentCondition::new); + } + + public Identifier[] getEnchantments() { + Identifier[] enchantments = new Identifier[this.conditions.length]; + + for (int i = 0; i < this.conditions.length; i++) + enchantments[i] = this.conditions[i].getValue(null); + + return enchantments; + } + + @Override + public Set<Class<? extends CITCondition>> siblingConditions() { + return Set.of(ConditionEnchantmentLevels.class); + } + + protected static class EnchantmentCondition extends IdentifierCondition { + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + super.load(value, properties); + + if (!Registry.ENCHANTMENT.containsId(this.value)) + warn(this.value + " is not in the enchantment registry", value, properties); + } + + @Override + public boolean test(CITContext context) { + return context.enchantments().containsKey(this.value); + } + + @Override + protected Identifier getValue(CITContext context) { + return this.value; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionHand.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionHand.java new file mode 100644 index 0000000..0405cbc --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionHand.java @@ -0,0 +1,49 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.EnumCondition; + +public class ConditionHand extends EnumCondition<ConditionHand.Hand> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionHand> CONTAINER = new CITConditionContainer<>(ConditionHand.class, ConditionHand::new, + "hand"); + + public ConditionHand() { + super(ConditionHand.Hand::values); + } + + @Override + protected Hand getValue(CITContext context) { + if (context.entity.getMainHandStack() == context.stack) + return Hand.MAINHAND; + if (context.entity.getOffHandStack() == context.stack) + return Hand.OFFHAND; + + return null; + } + + @Override + public boolean test(CITContext context) { + Hand hand = getValue(context); + return this.value == hand || (this.value == Hand.ANY && hand != null); + } + + protected enum Hand implements EnumCondition.Aliased { + MAINHAND("main", "mainhand", "main_hand"), + OFFHAND("off", "offhand", "off_hand"), + ANY("any", "either", "*"); + + private final String[] aliases; + + Hand(String... aliases) { + this.aliases = aliases; + } + + @Override + public String[] getAliases() { + return this.aliases; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionItems.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionItems.java new file mode 100644 index 0000000..117aa47 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionItems.java @@ -0,0 +1,75 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IdentifierCondition; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.ListCondition; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class ConditionItems extends ListCondition<ConditionItems.ItemCondition> { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionItems> CONTAINER = new CITConditionContainer<>(ConditionItems.class, ConditionItems::new, + "items", "matchItems"); + + public Item[] items = new Item[0]; + + public ConditionItems() { + super(ItemCondition.class, ItemCondition::new); + } + + public ConditionItems(Item... items) { + this(); + this.items = items; + } + + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + super.load(value, properties); + + Set<Item> items = new LinkedHashSet<>(); + + for (ItemCondition itemCondition : this.conditions) + items.add(itemCondition.item); + + this.items = items.toArray(new Item[0]); + } + + @Override + public boolean test(CITContext context) { + for (Item item : this.items) + if (context.stack.getItem() == item) + return true; + + return false; + } + + protected static class ItemCondition extends IdentifierCondition { + public Item item = null; + + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + super.load(value, properties); + + if (Registry.ITEM.containsId(this.value)) + this.item = Registry.ITEM.get(this.value); + else { + this.item = null; + warn(this.value + " is not in the item registry", value, properties); + } + } + + @Override + protected Identifier getValue(CITContext context) { + return this.value; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionNBT.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionNBT.java new file mode 100644 index 0000000..09ae08e --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionNBT.java @@ -0,0 +1,244 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.nbt.*; +import net.minecraft.text.Text; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITCondition; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue; + +import java.util.Locale; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class ConditionNBT extends CITCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionNBT> CONTAINER = new CITConditionContainer<>(ConditionNBT.class, ConditionNBT::new, + "nbt"); + + protected String[] path; + + protected StringMatcher matchString = null; + protected NbtInt matchInteger = null; + protected NbtByte matchByte = null; + protected NbtFloat matchFloat = null; + protected NbtDouble matchDouble = null; + protected NbtLong matchLong = null; + protected NbtShort matchShort = null; + protected NbtCompound matchCompound = null; + + @Override + public void load(PropertyValue value, PropertyGroup properties) throws CITParsingException { + if (value.keyMetadata() == null || value.keyMetadata().isEmpty()) + throw new CITParsingException("Missing nbt path", properties, value.position()); + + path = value.keyMetadata().split("\\."); + for (String s : path) + if (s.isEmpty()) + throw new CITParsingException("Path segment cannot be empty", properties, value.position()); + + try { + if (value.value().startsWith("regex:")) + matchString = new StringMatcher.RegexMatcher(value.value().substring(6)); + else if (value.value().startsWith("iregex:")) + matchString = new StringMatcher.IRegexMatcher(value.value().substring(7)); + else if (value.value().startsWith("pattern:")) + matchString = new StringMatcher.PatternMatcher(value.value().substring(8)); + else if (value.value().startsWith("ipattern:")) + matchString = new StringMatcher.IPatternMatcher(value.value().substring(9)); + else + matchString = new StringMatcher.DirectMatcher(value.value()); + } catch (PatternSyntaxException e) { + throw new CITParsingException("Malformatted regex expression", properties, value.position(), e); + } catch (Exception ignored) { } + try { + if (value.value().startsWith("#")) + matchInteger = NbtInt.of(Integer.parseInt(value.value().substring(1).toLowerCase(Locale.ENGLISH), 16)); + else if (value.value().startsWith("0x")) + matchInteger = NbtInt.of(Integer.parseInt(value.value().substring(2).toLowerCase(Locale.ENGLISH), 16)); + else + matchInteger = NbtInt.of(Integer.parseInt(value.value())); + } catch (Exception ignored) { } + try { + matchByte = NbtByte.of(Byte.parseByte(value.value())); + } catch (Exception ignored) { } + try { + matchFloat = NbtFloat.of(Float.parseFloat(value.value())); + } catch (Exception ignored) { } + try { + matchDouble = NbtDouble.of(Double.parseDouble(value.value())); + } catch (Exception ignored) { } + try { + matchLong = NbtLong.of(Long.parseLong(value.value())); + } catch (Exception ignored) { } + try { + matchShort = NbtShort.of(Short.parseShort(value.value())); + } catch (Exception ignored) { } + try { + matchCompound = StringNbtReader.parse(value.value()); + } catch (Exception ignored) { } + } + + @Override + public boolean test(CITContext context) { + return testPath(context.stack.getNbt(), 0); + } + + protected boolean testPath(NbtElement element, int pathIndex) { + if (element == null) + return false; + + if (pathIndex >= path.length) + return testValue(element); + + final String path = this.path[pathIndex]; + if (path.equals("*")) { + if (element instanceof NbtCompound compound) { + for (NbtElement subElement : compound.entries.values()) + if (testPath(subElement, pathIndex + 1)) + return true; + } else if (element instanceof NbtList list) { + for (NbtElement subElement : list) + if (testPath(subElement, pathIndex + 1)) + return true; + } + } else { + if (element instanceof NbtCompound compound) + return testPath(compound.get(path), pathIndex + 1); + else if (element instanceof NbtList list) + try { + return testPath(list.get(Integer.parseInt(path)), pathIndex + 1); + } catch (NumberFormatException | IndexOutOfBoundsException ignored) { } + } + + return false; + } + + private boolean testValue(NbtElement element) { + try { + if (element instanceof NbtString nbtString) //noinspection ConstantConditions + return matchString.matches(nbtString.asString()) || matchString.matches(Text.Serializer.fromJson(nbtString.asString()).getString()); + else if (element instanceof NbtInt nbtInt) + return nbtInt.equals(matchInteger); + else if (element instanceof NbtByte nbtByte) + return nbtByte.equals(matchByte); + else if (element instanceof NbtFloat nbtFloat) + return nbtFloat.equals(matchFloat); + else if (element instanceof NbtDouble nbtDouble) + return nbtDouble.equals(matchDouble); + else if (element instanceof NbtLong nbtLong) + return nbtLong.equals(matchLong); + else if (element instanceof NbtShort nbtShort) + return nbtShort.equals(matchShort); + else if (element instanceof NbtCompound nbtCompound) + return NbtHelper.matches(matchCompound, nbtCompound, true); + } catch (Exception ignored) { } + return false; + } + + protected static abstract class StringMatcher { + public abstract boolean matches(String value); + + public static class DirectMatcher extends StringMatcher { + protected final String pattern; + + public DirectMatcher(String pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(String value) { + return pattern.equals(value); + } + } + + public static class RegexMatcher extends StringMatcher { + protected final Pattern pattern; + + public RegexMatcher(String pattern) { + this(Pattern.compile(pattern)); + } + + protected RegexMatcher(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(String value) { + return this.pattern.matcher(value).matches(); + } + } + + public static class PatternMatcher extends StringMatcher { + protected final String pattern; + + public PatternMatcher(String pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(String value) { + return matchesPattern(value, this.pattern, 0, value.length(), 0, pattern.length()); + } + + /** + * Author: Paul "prupe" Rupe<br> + * Taken and modified from MCPatcher under public domain licensing.<br> + * https://bitbucket.org/prupe/mcpatcher/src/1aa45839b2cd029143809edfa60ec59e5ef75f80/newcode/src/com/prupe/mcpatcher/mal/nbt/NBTRule.java#lines-269:301 + */ + protected boolean matchesPattern(String value, String pattern, int curV, int maxV, int curG, int maxG) { + for (; curG < maxG; curG++, curV++) { + char g = pattern.charAt(curG); + if (g == '*') { + while (true) { + if (matchesPattern(value, pattern, curV, maxV, curG + 1, maxG)) { + return true; + } + if (curV >= maxV) { + break; + } + curV++; + } + return false; + } else if (curV >= maxV) { + break; + } else if (g == '?') { + continue; + } + if (g == '\\' && curG + 1 < maxG) { + curG++; + g = pattern.charAt(curG); + } + + if (!charsEqual(g, value.charAt(curV))) + return false; + } + return curG == maxG && curV == maxV; + } + + protected boolean charsEqual(char p, char v) { + return p == v; + } + } + + public static class IRegexMatcher extends RegexMatcher { + public IRegexMatcher(String pattern) { + super(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE)); + } + } + + public static class IPatternMatcher extends PatternMatcher { + public IPatternMatcher(String pattern) { + super(pattern.toLowerCase(Locale.ROOT)); + } + + @Override + protected boolean charsEqual(char p, char v) { + return p == v || p == Character.toLowerCase(v); + } + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionStackSize.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionStackSize.java new file mode 100644 index 0000000..1d22a53 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionStackSize.java @@ -0,0 +1,21 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.conditions; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import shcm.shsupercm.fabric.citresewn.api.CITConditionContainer; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.cit.builtin.conditions.IntegerCondition; + +public class ConditionStackSize extends IntegerCondition { + @Entrypoint(CITConditionContainer.ENTRYPOINT) + public static final CITConditionContainer<ConditionStackSize> CONTAINER = new CITConditionContainer<>(ConditionStackSize.class, ConditionStackSize::new, + "stack_size", "stackSize", "amount"); + + public ConditionStackSize() { + super(true, false, false); + } + + @Override + protected int getValue(CITContext context) { + return context.stack.getCount(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeArmor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeArmor.java new file mode 100644 index 0000000..d97bf67 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeArmor.java @@ -0,0 +1,111 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.item.ArmorItem; +import net.minecraft.item.Item; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionItems; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue; + +import java.util.*; + +public class TypeArmor extends CITType { + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + + public final Map<String, Identifier> textures = new HashMap<>(); + + @Override + public Set<PropertyKey> typeProperties() { + return Set.of(PropertyKey.of("texture")); + } + + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + boolean itemsConditionPresent = false; + for (CITCondition condition : conditions) + if (condition instanceof ConditionItems conditionItems) + for (Item item : conditionItems.items) + if (item instanceof ArmorItem) + itemsConditionPresent = true; + else + throw new CITParsingException("This type only accepts armor items for the items condition", properties, -1); + + if (!itemsConditionPresent) + try { + Identifier propertiesName = new Identifier(properties.stripName()); + if (!Registry.ITEM.containsId(propertiesName)) + throw new Exception(); + Item item = Registry.ITEM.get(propertiesName); + if (!(item instanceof ArmorItem)) + throw new Exception(); + conditions.add(new ConditionItems(item)); + } catch (Exception ignored) { + throw new CITParsingException("Not targeting any item type", properties, -1); + } + + for (PropertyValue propertyValue : properties.get("citresewn", "texture")) { + Identifier identifier = resolveAsset(properties.identifier, propertyValue, "textures", ".png", resourceManager); + if (identifier == null) + throw new CITParsingException("Could not resolve texture", properties, propertyValue.position()); + + textures.put(propertyValue.keyMetadata(), identifier); + } + if (textures.size() == 0) + throw new CITParsingException("Texture not specified", properties, -1); + } + + public static class Container extends CITTypeContainer<TypeArmor> { + public Container() { + super(TypeArmor.class, TypeArmor::new, "armor"); + } + + public Set<CIT<TypeArmor>> loaded = new HashSet<>(); + public Map<ArmorItem, Set<CIT<TypeArmor>>> loadedTyped = new IdentityHashMap<>(); + + @Override + public void load(List<CIT<TypeArmor>> parsedCITs) { + loaded.addAll(parsedCITs); + for (CIT<TypeArmor> cit : parsedCITs) + for (CITCondition condition : cit.conditions) + if (condition instanceof ConditionItems items) + for (Item item : items.items) + if (item instanceof ArmorItem armorItem) + loadedTyped.computeIfAbsent(armorItem, i -> new LinkedHashSet<>()).add(cit); + } + + @Override + public void dispose() { + loaded.clear(); + loadedTyped.clear(); + } + + public CIT<TypeArmor> getCIT(CITContext context) { + return ((CITCacheArmor) (Object) context.stack).citresewn$getCacheTypeArmor().get(context).get(); + } + + public CIT<TypeArmor> getRealTimeCIT(CITContext context) { + if (!(context.stack.getItem() instanceof ArmorItem)) + return null; + + Set<CIT<TypeArmor>> loadedForItemType = loadedTyped.get(context.stack.getItem()); + if (loadedForItemType != null) + for (CIT<TypeArmor> cit : loadedForItemType) + if (cit.test(context)) + return cit; + + return null; + } + } + + public interface CITCacheArmor { + CITCache.Single<TypeArmor> citresewn$getCacheTypeArmor(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeElytra.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeElytra.java new file mode 100644 index 0000000..40e6fbe --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeElytra.java @@ -0,0 +1,76 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.item.ElytraItem; +import net.minecraft.item.Item; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionItems; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class TypeElytra extends CITType { + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + + public Identifier texture; + + @Override + public Set<PropertyKey> typeProperties() { + return Set.of(PropertyKey.of("texture")); + } + + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + for (CITCondition condition : conditions) + if (condition instanceof ConditionItems items) + for (Item item : items.items) + if (!(item instanceof ElytraItem)) + warn("Non elytra item type condition", null, properties); + + texture = resolveAsset(properties.identifier, properties.getLastWithoutMetadata("citresewn", "texture"), "textures", ".png", resourceManager); + if (texture == null) + throw new CITParsingException("Texture not specified", properties, -1); + } + + public static class Container extends CITTypeContainer<TypeElytra> { + public Container() { + super(TypeElytra.class, TypeElytra::new, "elytra"); + } + + public Set<CIT<TypeElytra>> loaded = new HashSet<>(); + + @Override + public void load(List<CIT<TypeElytra>> parsedCITs) { + loaded.addAll(parsedCITs); + } + + @Override + public void dispose() { + loaded.clear(); + } + + public CIT<TypeElytra> getCIT(CITContext context) { + return ((CITCacheElytra) (Object) context.stack).citresewn$getCacheTypeElytra().get(context).get(); + } + + public CIT<TypeElytra> getRealTimeCIT(CITContext context) { + for (CIT<TypeElytra> cit : loaded) + if (cit.test(context)) + return cit; + + return null; + } + } + + public interface CITCacheElytra { + CITCache.Single<TypeElytra> citresewn$getCacheTypeElytra(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeEnchantment.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeEnchantment.java new file mode 100644 index 0000000..0bd29e7 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeEnchantment.java @@ -0,0 +1,528 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; + +import com.mojang.blaze3d.systems.RenderSystem; +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.*; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; +import net.minecraft.util.math.Matrix4f; +import net.minecraft.util.math.Vec3f; +import org.lwjgl.opengl.GL11; +import shcm.shsupercm.fabric.citresewn.api.CITGlobalProperties; +import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionEnchantments; +import shcm.shsupercm.fabric.citresewn.defaults.config.CITResewnDefaultsConfig; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment.BufferBuilderStorageAccessor; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment.RenderPhaseAccessor; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue; +import shcm.shsupercm.util.logic.Loops; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.function.Consumer; + +import static com.mojang.blaze3d.systems.RenderSystem.*; +import static org.lwjgl.opengl.GL11.*; + +public class TypeEnchantment extends CITType { + @Entrypoint(CITGlobalProperties.ENTRYPOINT) + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + + @Override + public Set<PropertyKey> typeProperties() { + return Set.of( + PropertyKey.of("texture"), + PropertyKey.of("layer"), + PropertyKey.of("speed"), + PropertyKey.of("rotation"), + PropertyKey.of("duration"), + PropertyKey.of("r"), + PropertyKey.of("g"), + PropertyKey.of("b"), + PropertyKey.of("a"), + PropertyKey.of("useGlint"), + PropertyKey.of("blur"), + PropertyKey.of("blend")); + } + + public Identifier texture; + public int layer; + public float speed, rotation, duration, r, g, b, a; + public boolean useGlint, blur; + public Blend blend; + + public final Map<GlintRenderLayer, RenderLayer> renderLayers = new EnumMap<>(GlintRenderLayer.class); + private final MergeMethodIntensity methodIntensity = new MergeMethodIntensity(); + private Set<Identifier> enchantmentChecks = null; + + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + PropertyValue textureProp = properties.getLastWithoutMetadata("citresewn", "texture"); + this.texture = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (this.texture == null) + throw textureProp == null ? new CITParsingException("No texture specified", properties, -1) : new CITParsingException("Could not resolve texture", properties, textureProp.position()); + + PropertyValue layerProp = properties.getLastWithoutMetadataOrDefault("0", "citresewn", "layer"); + try { + this.layer = Integer.parseInt(layerProp.value()); + } catch (Exception e) { + throw new CITParsingException("Could not parse integer", properties, layerProp.position(), e); + } + + this.speed = parseFloatOrDefault(1f, "speed", properties); + this.rotation = parseFloatOrDefault(10f, "rotation", properties); + this.duration = Math.max(0f, parseFloatOrDefault(0f, "duration", properties)); + this.r = Math.max(0f, parseFloatOrDefault(1f, "r", properties)); + this.g = Math.max(0f, parseFloatOrDefault(1f, "g", properties)); + this.b = Math.max(0f, parseFloatOrDefault(1f, "b", properties)); + this.a = Math.max(0f, parseFloatOrDefault(1f, "a", properties)); + + this.useGlint = Boolean.parseBoolean(properties.getLastWithoutMetadataOrDefault("false", "citresewn", "useGlint").value()); + this.blur = Boolean.parseBoolean(properties.getLastWithoutMetadataOrDefault("true", "citresewn", "blur").value()); + + PropertyValue blendProp = properties.getLastWithoutMetadataOrDefault("add", "citresewn", "blend"); + try { + this.blend = Blend.getBlend(blendProp.value()); + } catch (Exception e) { + throw new CITParsingException("Could not parse blending method", properties, blendProp.position(), e); + } + + for (CITCondition condition : conditions) + if (condition instanceof ConditionEnchantments enchantments) { + if (enchantmentChecks == null && enchantments.getEnchantments().length > 0) + enchantmentChecks = new HashSet<>(); + + enchantmentChecks.addAll(Arrays.asList(enchantments.getEnchantments())); + } + } + + private float parseFloatOrDefault(float defaultValue, String propertyName, PropertyGroup properties) throws CITParsingException { + PropertyValue property = properties.getLastWithoutMetadata("citresewn", propertyName); + if (property == null) + return defaultValue; + try { + return Float.parseFloat(property.value()); + } catch (Exception e) { + throw new CITParsingException("Could not parse float", properties, property.position(), e); + } + } + + public static class Container extends CITTypeContainer<TypeEnchantment> implements CITGlobalProperties { + public Container() { + super(TypeEnchantment.class, TypeEnchantment::new, "enchantment"); + } + + public boolean globalUseGlint = true; + public int globalCap = Integer.MAX_VALUE; + public MergeMethodIntensity.MergeMethod globalMergeMethod = MergeMethodIntensity.MergeMethod.AVERAGE; + public float globalFade = 0.5f; + + public List<CIT<TypeEnchantment>> loaded = new ArrayList<>(); + public List<List<CIT<TypeEnchantment>>> loadedLayered = new ArrayList<>(); + + private List<CIT<TypeEnchantment>> appliedContext = null; + private boolean apply = false, defaultGlint = false; + + @Override + public void load(List<CIT<TypeEnchantment>> parsedCITs) { + loaded.addAll(parsedCITs); + + Map<Integer, List<CIT<TypeEnchantment>>> layers = new HashMap<>(); + for (CIT<TypeEnchantment> cit : loaded) + layers.computeIfAbsent(cit.type.layer, i -> new ArrayList<>()).add(cit); + loadedLayered.clear(); + layers.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEachOrdered(layer -> loadedLayered.add(layer.getValue())); + + for (CIT<TypeEnchantment> cit : loaded) + for (GlintRenderLayer glintLayer : GlintRenderLayer.values()) { + RenderLayer renderLayer = glintLayer.build(cit.type, cit.propertiesIdentifier); + + cit.type.renderLayers.put(glintLayer, renderLayer); + ((BufferBuilderStorageAccessor) MinecraftClient.getInstance().getBufferBuilders()).getEntityBuilders().put(renderLayer, new BufferBuilder(renderLayer.getExpectedBufferSize())); + } + } + + @Override + public void globalProperty(String key, @Nullable PropertyValue value) throws Exception { + switch (key) { + case "useGlint" -> { + globalUseGlint = value == null ? true : Boolean.parseBoolean(value.value()); + if (!globalUseGlint && !"false".equalsIgnoreCase(value.value())) + throw new Exception("Could not parse boolean"); + } + case "cap" -> { + globalCap = value == null ? Integer.MAX_VALUE : Integer.parseInt(value.value()); + } + case "method" -> { + globalMergeMethod = value == null ? MergeMethodIntensity.MergeMethod.AVERAGE : MergeMethodIntensity.MergeMethod.parse(value.value()); + } + case "fade" -> { + globalFade = value == null ? 0.5f : Float.parseFloat(value.value()); + } + } + } + + @Override + public void dispose() { + appliedContext = null; + apply = false; + + for (CIT<TypeEnchantment> cit : loaded) + for (RenderLayer renderLayer : cit.type.renderLayers.values()) + ((BufferBuilderStorageAccessor) MinecraftClient.getInstance().getBufferBuilders()).getEntityBuilders().remove(renderLayer); + + loaded.clear(); + loadedLayered.clear(); + } + + public void apply() { + if (appliedContext != null) + apply = true; + } + + public boolean shouldApply() { + return apply && active(); + } + + public boolean shouldNotApplyDefaultGlint() { + return !globalUseGlint || (apply && !defaultGlint); + } + + public Container setContext(CITContext context) { + apply = false; + defaultGlint = false; + appliedContext = null; + if (context == null) + return this; + + List<WeakReference<CIT<TypeEnchantment>>> cits = ((CITCacheEnchantment) (Object) context.stack).citresewn$getCacheTypeEnchantment().get(context); + + appliedContext = new ArrayList<>(); + if (cits != null) + for (WeakReference<CIT<TypeEnchantment>> citRef : cits) + if (citRef != null) { + CIT<TypeEnchantment> cit = citRef.get(); + if (cit != null) { + appliedContext.add(cit); + if (cit.type.useGlint) + defaultGlint = true; + } + } + + if (appliedContext.isEmpty()) + appliedContext = null; + else + globalMergeMethod.applyMethod(appliedContext, context); + + return this; + } + + public List<CIT<TypeEnchantment>> getRealTimeCIT(CITContext context) { + List<CIT<TypeEnchantment>> cits = new ArrayList<>(); + for (List<CIT<TypeEnchantment>> layer : loadedLayered) + for (CIT<TypeEnchantment> cit : layer) + if (cit.test(context)) { + cits.add(cit); + break; + } + + return cits; + } + } + + public enum GlintRenderLayer { + ARMOR_GLINT("armor_glint", 8f, layer -> layer + .shader(RenderPhaseAccessor.ARMOR_GLINT_SHADER()) + .writeMaskState(RenderPhaseAccessor.COLOR_MASK()) + .cull(RenderPhaseAccessor.DISABLE_CULLING()) + .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST()) + .layering(RenderPhaseAccessor.VIEW_OFFSET_Z_LAYERING())), + ARMOR_ENTITY_GLINT("armor_entity_glint", 0.16f, layer -> layer + .shader(RenderPhaseAccessor.ARMOR_ENTITY_GLINT_SHADER()) + .writeMaskState(RenderPhaseAccessor.COLOR_MASK()) + .cull(RenderPhaseAccessor.DISABLE_CULLING()) + .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST()) + .layering(RenderPhaseAccessor.VIEW_OFFSET_Z_LAYERING())), + GLINT_TRANSLUCENT("glint_translucent", 8f, layer -> layer + .shader(RenderPhaseAccessor.TRANSLUCENT_GLINT_SHADER()) + .writeMaskState(RenderPhaseAccessor.COLOR_MASK()) + .cull(RenderPhaseAccessor.DISABLE_CULLING()) + .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST()) + .target(RenderPhaseAccessor.ITEM_TARGET())), + GLINT("glint", 8f, layer -> layer + .shader(RenderPhaseAccessor.GLINT_SHADER()) + .writeMaskState(RenderPhaseAccessor.COLOR_MASK()) + .cull(RenderPhaseAccessor.DISABLE_CULLING()) + .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())), + DIRECT_GLINT("glint_direct", 8f, layer -> layer + .shader(RenderPhaseAccessor.DIRECT_GLINT_SHADER()) + .writeMaskState(RenderPhaseAccessor.COLOR_MASK()) + .cull(RenderPhaseAccessor.DISABLE_CULLING()) + .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())), + ENTITY_GLINT("entity_glint", 0.16f, layer -> layer + .shader(RenderPhaseAccessor.ENTITY_GLINT_SHADER()) + .writeMaskState(RenderPhaseAccessor.COLOR_MASK()) + .cull(RenderPhaseAccessor.DISABLE_CULLING()) + .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST()) + .target(RenderPhaseAccessor.ITEM_TARGET())), + DIRECT_ENTITY_GLINT("entity_glint_direct", 0.16f, layer -> layer + .shader(RenderPhaseAccessor.DIRECT_ENTITY_GLINT_SHADER()) + .writeMaskState(RenderPhaseAccessor.COLOR_MASK()) + .cull(RenderPhaseAccessor.DISABLE_CULLING()) + .depthTest(RenderPhaseAccessor.EQUAL_DEPTH_TEST())); + + public final String name; + private final Consumer<RenderLayer.MultiPhaseParameters.Builder> setup; + private final float scale; + + GlintRenderLayer(String name, float scale, Consumer<RenderLayer.MultiPhaseParameters.Builder> setup) { + this.name = name; + this.scale = scale; + this.setup = setup; + } + + public RenderLayer build(TypeEnchantment enchantment, Identifier propertiesIdentifier) { + class Texturing implements Runnable { + private final float speed, rotation, r, g, b, a; + private final MergeMethodIntensity methodIntensity; + + Texturing(float speed, float rotation, float r, float g, float b, float a, MergeMethodIntensity methodIntensity) { + this.speed = speed; + this.rotation = rotation; + this.r = r; + this.g = g; + this.b = b; + this.a = a; + this.methodIntensity = methodIntensity; + } + + @Override + public void run() { + float l = Util.getMeasuringTimeMs() * CITResewnDefaultsConfig.INSTANCE.type_enchantment_scroll_multiplier * speed; + float x = (l % 110000f) / 110000f; + float y = (l % 30000f) / 30000f; + Matrix4f matrix4f = Matrix4f.translate(-x, y, 0.0f); + matrix4f.multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(rotation)); + matrix4f.multiply(Matrix4f.scale(scale, scale, scale)); + setTextureMatrix(matrix4f); + + setShaderColor(r, g, b, a * methodIntensity.intensity); + } + } + + RenderLayer.MultiPhaseParameters.Builder layer = RenderLayer.MultiPhaseParameters.builder() + .texture(new RenderPhase.Texture(enchantment.texture, enchantment.blur, false)) + .texturing(new RenderPhase.Texturing("citresewn_glint_texturing", new Texturing(enchantment.speed, enchantment.rotation, enchantment.r, enchantment.g, enchantment.b, enchantment.a, enchantment.methodIntensity), () -> { + RenderSystem.resetTextureMatrix(); + + setShaderColor(1f, 1f, 1f, 1f); + })) + .transparency(enchantment.blend); + + this.setup.accept(layer); + + return RenderLayer.of("citresewn:enchantment_" + this.name + ":" + propertiesIdentifier.toString(), + VertexFormats.POSITION_TEXTURE, + VertexFormat.DrawMode.QUADS, + 256, + layer.build(false)); + } + + public VertexConsumer tryApply(VertexConsumer base, RenderLayer baseLayer, VertexConsumerProvider provider) { + if (!CONTAINER.apply || CONTAINER.appliedContext == null || CONTAINER.appliedContext.size() == 0) + return null; + + VertexConsumer[] layers = new VertexConsumer[Math.min(CONTAINER.appliedContext.size(), CONTAINER.globalCap)]; + + for (int i = 0; i < layers.length; i++) + layers[i] = provider.getBuffer(CONTAINER.appliedContext.get(i).type.renderLayers.get(GlintRenderLayer.this)); + + provider.getBuffer(baseLayer); // refresh base layer for armor consumer + + return base == null ? VertexConsumers.union(layers) : VertexConsumers.union(VertexConsumers.union(layers), base); + } + } + + public static class MergeMethodIntensity { + public float intensity = 1f; + + public enum MergeMethod { + NONE, + AVERAGE { + @Override + public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CIT<TypeEnchantment> cit) { + Identifier enchantment = null; + for (Identifier enchantmentMatch : cit.type.enchantmentChecks) + if (stackEnchantments.containsKey(enchantmentMatch)) { + enchantment = enchantmentMatch; + break; + } + + if (enchantment == null) { + cit.type.methodIntensity.intensity = 0f; + } else { + float sum = 0f; + for (Integer value : stackEnchantments.values()) + sum += value; + + cit.type.methodIntensity.intensity = (float) stackEnchantments.get(enchantment) / sum; + } + } + }, + LAYERED { + @Override + public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CIT<TypeEnchantment> cit) { + Identifier enchantment = null; + for (Identifier enchantmentMatch : cit.type.enchantmentChecks) + if (stackEnchantments.containsKey(enchantmentMatch)) { + enchantment = enchantmentMatch; + break; + } + if (enchantment == null) { + cit.type.methodIntensity.intensity = 0f; + return; + } + + float max = 0f; + for (Integer value : stackEnchantments.values()) + if (value > max) + max = value; + + cit.type.methodIntensity.intensity = (float) stackEnchantments.get(enchantment) / max; + } + }, + CYCLE { + @Override + public void applyMethod(List<CIT<TypeEnchantment>> citEnchantments, CITContext context) { + List<Map.Entry<CIT<TypeEnchantment>, Float>> durations = new ArrayList<>(); + for (CIT<TypeEnchantment> cit : citEnchantments) + durations.add(new HashMap.SimpleEntry<>(cit, cit.type.duration)); + + for (Map.Entry<CIT<TypeEnchantment>, Float> intensity : Loops.statelessFadingLoop(durations, CONTAINER.globalFade, ticks, 20).entrySet()) + intensity.getKey().type.methodIntensity.intensity = intensity.getValue(); + } + }; + + public static int ticks = 0; + + public void applyIntensity(Map<Identifier, Integer> stackEnchantments, CIT<TypeEnchantment> cit) { + cit.type.methodIntensity.intensity = 1f; + } + + public void applyMethod(List<CIT<TypeEnchantment>> citEnchantments, CITContext context) { + Map<Identifier, Integer> stackEnchantments = context.enchantments(); + + for (CIT<TypeEnchantment> cit : citEnchantments) + if (cit.type.enchantmentChecks != null) + applyIntensity(stackEnchantments, cit); + } + + public static MergeMethod parse(String value) { + return switch (value.toLowerCase(Locale.ROOT)) { + case "none" -> NONE; + case "average" -> AVERAGE; + case "layered" -> LAYERED; + case "cycle" -> CYCLE; + default -> throw new IllegalArgumentException("Unknown merge method"); + }; + } + } + } + + public static class Blend extends RenderPhase.Transparency { + private final int src, dst, srcAlpha, dstAlpha; + + private Blend(String name, int src, int dst, int srcAlpha, int dstAlpha) { + super(name + "_glint_transparency", null, null); + this.src = src; + this.dst = dst; + this.srcAlpha = srcAlpha; + this.dstAlpha = dstAlpha; + } + + private Blend(String name, int src, int dst) { + this(name, src, dst, GL_ZERO, GL_ONE); + } + + @Override + public void startDrawing() { + enableBlend(); + blendFuncSeparate(src, dst, srcAlpha, dstAlpha); + } + + @Override + public void endDrawing() { + defaultBlendFunc(); + disableBlend(); + } + + public static Blend getBlend(String blendString) throws Exception { + try { //check named blending function + return Named.valueOf(blendString.toUpperCase(Locale.ENGLISH)).blend; + } catch (IllegalArgumentException ignored) { // create custom blending function + String[] split = blendString.split("\\p{Zs}+"); + int src, dst, srcAlpha, dstAlpha; + if (split.length == 2) { + src = parseGLConstant(split[0]); + dst = parseGLConstant(split[1]); + srcAlpha = GL_ZERO; + dstAlpha = GL_ONE; + } else if (split.length == 4) { + src = parseGLConstant(split[0]); + dst = parseGLConstant(split[1]); + srcAlpha = parseGLConstant(split[2]); + dstAlpha = parseGLConstant(split[3]); + } else + throw new Exception(); + + return new Blend("custom_" + src + "_" + dst + "_" + srcAlpha + "_" + dstAlpha, src, dst, srcAlpha, dstAlpha); + } + } + + private enum Named { + REPLACE(new Blend("replace", 0, 0) { + @Override + public void startDrawing() { + disableBlend(); + } + }), + GLINT(new Blend("glint", GL_SRC_COLOR, GL_ONE)), + ALPHA(new Blend("alpha", GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)), + ADD(new Blend("add", GL_SRC_ALPHA, GL_ONE)), + SUBTRACT(new Blend("subtract", GL_ONE_MINUS_DST_COLOR, GL_ZERO)), + MULTIPLY(new Blend("multiply", GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA)), + DODGE(new Blend("dodge", GL_ONE, GL_ONE)), + BURN(new Blend("burn", GL_ZERO, GL_ONE_MINUS_SRC_COLOR)), + SCREEN(new Blend("screen", GL_ONE, GL_ONE_MINUS_SRC_COLOR)), + OVERLAY(new Blend("overlay", GL_DST_COLOR, GL_SRC_COLOR)); + + public final Blend blend; + + Named(Blend blend) { + this.blend = blend; + } + } + + private static int parseGLConstant(String s) throws Exception { + try { + return GL11.class.getDeclaredField(s).getInt(null); + } catch (NoSuchFieldException ignored) { } + + return s.startsWith("0x") ? Integer.parseInt(s.substring(2), 16) : Integer.parseInt(s); + } + } + + public interface CITCacheEnchantment { + CITCache.MultiList<TypeEnchantment> citresewn$getCacheTypeEnchantment(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeItem.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeItem.java new file mode 100644 index 0000000..fe0bafb --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/cit/types/TypeItem.java @@ -0,0 +1,520 @@ +package shcm.shsupercm.fabric.citresewn.defaults.cit.types; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Either; +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.render.model.json.ModelOverride; +import net.minecraft.client.render.model.json.ModelOverrideList; +import net.minecraft.client.render.model.json.ModelTransformation; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.io.IOUtils; +import shcm.shsupercm.fabric.citresewn.CITResewn; +import shcm.shsupercm.fabric.citresewn.api.CITTypeContainer; +import shcm.shsupercm.fabric.citresewn.cit.*; +import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionItems; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnItemModelIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnTextureIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item.JsonUnbakedModelAccessor; +import shcm.shsupercm.fabric.citresewn.ex.CITParsingException; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey; +import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +/** + * ///// PORTED FROM BETA \\\\\ + * This shit was ported from the + * beta and will be rewritten at + * some point! + * \\\\\ ///// + */ +public class TypeItem extends CITType { + @Entrypoint(CITTypeContainer.ENTRYPOINT) + public static final Container CONTAINER = new Container(); + + private static final String GENERATED_SUB_CITS_PREFIX = "sub_cititem_generated_"; + public static final Set<Identifier> GENERATED_SUB_CITS_SEEN = new HashSet<>(); + + private final List<Item> items = new ArrayList<>(); + + public Map<Identifier, Identifier> assetIdentifiers = new LinkedHashMap<>(); + public Map<List<ModelOverride.Condition>, JsonUnbakedModel> unbakedAssets = new LinkedHashMap<>(); + private Map<String, Either<SpriteIdentifier, String>> textureOverrideMap = new HashMap<>(); + private boolean isTexture = false; + + public BakedModel bakedModel = null; + public CITOverrideList bakedSubModels = new CITOverrideList(); + + @Override + public Set<PropertyKey> typeProperties() { + return Set.of(PropertyKey.of("model"), PropertyKey.of("texture"), PropertyKey.of("tile")); + } + + @Override + public void load(List<CITCondition> conditions, PropertyGroup properties, ResourceManager resourceManager) throws CITParsingException { + for (CITCondition condition : conditions) + if (condition instanceof ConditionItems conditionItems) + items.addAll(Arrays.asList(conditionItems.items)); + + if (this.items.size() == 0) + try { + Identifier propertiesName = new Identifier(properties.stripName()); + if (!Registry.ITEM.containsId(propertiesName)) + throw new Exception(); + Item item = Registry.ITEM.get(propertiesName); + conditions.add(new ConditionItems(item)); + this.items.add(item); + } catch (Exception ignored) { + throw new CITParsingException("Not targeting any item type", properties, -1); + } + + Identifier assetIdentifier; + PropertyValue modelProp = properties.getLastWithoutMetadata("citresewn", "model"); + boolean containsTexture = modelProp == null && !properties.get("citresewn", "texture", "tile").isEmpty(); + + if (!containsTexture) { + assetIdentifier = resolveAsset(properties.identifier, modelProp, "models", ".json", resourceManager); + if (assetIdentifier != null) + assetIdentifiers.put(null, assetIdentifier); + else if (modelProp != null) { + assetIdentifier = resolveAsset(properties.identifier, modelProp, "models", ".json", resourceManager); + if (assetIdentifier != null) + assetIdentifiers.put(null, assetIdentifier); + } + } + + for (PropertyValue property : properties.get("citresewn", "model")) { + Identifier subIdentifier = resolveAsset(properties.identifier, property, "models", ".json", resourceManager); + if (subIdentifier == null) + throw new CITParsingException("Cannot resolve path", properties, property.position()); + + String subItem = property.keyMetadata(); + Identifier subItemIdentifier = fixDeprecatedSubItem(subItem, properties, property.position()); + assetIdentifiers.put(subItemIdentifier == null ? new Identifier("minecraft", "item/" + subItem) : subItemIdentifier, subIdentifier); + } + + if (assetIdentifiers.size() == 0) { // attempt to load texture + isTexture = true; + PropertyValue textureProp = properties.getLastWithoutMetadata("citresewn", "texture", "tile"); + assetIdentifier = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (assetIdentifier != null) + assetIdentifiers.put(null, assetIdentifier); + + for (PropertyValue property : properties.get("citresewn", "texture", "tile")) { + Identifier subIdentifier = resolveAsset(properties.identifier, property, "textures", ".png", resourceManager); + if (subIdentifier == null) + throw new CITParsingException("Cannot resolve path", properties, property.position()); + + String subItem = property.keyMetadata(); + Identifier subItemIdentifier = fixDeprecatedSubItem(subItem, properties, property.position()); + assetIdentifiers.put(subItemIdentifier == null ? new Identifier("minecraft", "item/" + subItem) : subItemIdentifier, subIdentifier); + } + } else { // attempt to load textureOverrideMap from textures + PropertyValue textureProp = properties.getLastWithoutMetadata("citresewn", "texture", "tile"); + if (textureProp != null) { + assetIdentifier = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (assetIdentifier != null) + textureOverrideMap.put(null, Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(assetIdentifier)))); + else + throw new CITParsingException("Cannot resolve path", properties, textureProp.position()); + } + + for (PropertyValue property : properties.get("citresewn", "texture", "tile")) { + textureProp = property; + Identifier subIdentifier = resolveAsset(properties.identifier, textureProp, "textures", ".png", resourceManager); + if (subIdentifier == null) + throw new CITParsingException("Cannot resolve path", properties, property.position()); + + textureOverrideMap.put(property.keyMetadata(), Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(subIdentifier)))); + } + } + + if (assetIdentifiers.size() == 0) + throw new CITParsingException("Could not resolve a replacement model/texture", properties, -1); + } + + public void loadUnbakedAssets(ResourceManager resourceManager) throws Exception { + try { + if (isTexture) { + JsonUnbakedModel itemJson = getModelForFirstItemType(resourceManager); + if (((JsonUnbakedModelAccessor) itemJson).getTextureMap().size() > 1) { // use(some/all of) the asset identifiers to build texture override in layered models + textureOverrideMap = ((JsonUnbakedModelAccessor) itemJson).getTextureMap(); + Identifier defaultAsset = assetIdentifiers.get(null); + textureOverrideMap.replaceAll((layerName, originalTextureEither) -> { + Identifier textureIdentifier = assetIdentifiers.remove(originalTextureEither.map(SpriteIdentifier::getTextureId, Identifier::new)); + if (textureIdentifier != null) + return Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(textureIdentifier))); + if (defaultAsset != null) + return Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(defaultAsset))); + return null; + }); + + if (assetIdentifiers.size() == 0 || (assetIdentifiers.size() == 1 && assetIdentifiers.containsKey(null))) { + unbakedAssets.put(null, itemJson); + return; + } + } + + Identifier baseIdentifier = assetIdentifiers.remove(null); + + if (baseIdentifier != null) + unbakedAssets.put(null, loadUnbakedAsset(resourceManager, baseIdentifier)); + + if (!assetIdentifiers.isEmpty()) { // contains sub models + LinkedHashMap<Identifier, List<ModelOverride.Condition>> overrideConditions = new LinkedHashMap<>(); + for (Item item : this.items) { + Identifier itemIdentifier = Registry.ITEM.getId(item); + overrideConditions.put(new Identifier(itemIdentifier.getNamespace(), "item/" + itemIdentifier.getPath()), Collections.emptyList()); + + Identifier itemModelIdentifier = new Identifier(itemIdentifier.getNamespace(), "models/item/" + itemIdentifier.getPath() + ".json"); + try (Resource itemModelResource = resourceManager.getResource(itemModelIdentifier); Reader resourceReader = new InputStreamReader(itemModelResource.getInputStream())) { + JsonUnbakedModel itemModelJson = JsonUnbakedModel.deserialize(resourceReader); + + if (itemModelJson.getOverrides() != null && !itemModelJson.getOverrides().isEmpty()) + for (ModelOverride override : itemModelJson.getOverrides()) + overrideConditions.put(override.getModelId(), override.streamConditions().toList()); + } + } + + ArrayList<Identifier> overrideModels = new ArrayList<>(overrideConditions.keySet()); + Collections.reverse(overrideModels); + + for (Identifier overrideModel : overrideModels) { + Identifier replacement = assetIdentifiers.remove(overrideModel); + if (replacement == null) + continue; + + List<ModelOverride.Condition> conditions = overrideConditions.get(overrideModel); + unbakedAssets.put(conditions, loadUnbakedAsset(resourceManager, replacement)); + } + } + } else { // isModel + Identifier baseIdentifier = assetIdentifiers.remove(null); + + if (baseIdentifier != null) { + if (!GENERATED_SUB_CITS_SEEN.add(baseIdentifier)) // cit generated duplicate + baseIdentifier = new Identifier(baseIdentifier.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + baseIdentifier.getPath()); + GENERATED_SUB_CITS_SEEN.add(baseIdentifier); + + JsonUnbakedModel model = loadUnbakedAsset(resourceManager, baseIdentifier); + unbakedAssets.put(null, model); + + if (model.getOverrides().size() > 0 && textureOverrideMap.size() > 0) { + LinkedHashMap<Identifier, List<ModelOverride.Condition>> overrideConditions = new LinkedHashMap<>(); + + for (ModelOverride override : model.getOverrides()) + overrideConditions.put(override.getModelId(), override.streamConditions().toList()); + + ArrayList<Identifier> overrideModels = new ArrayList<>(overrideConditions.keySet()); + Collections.reverse(overrideModels); + + for (Identifier overrideModel : overrideModels) { + Identifier replacement = resolveAsset(baseIdentifier, overrideModel.toString(), "models", ".json", resourceManager); + if (replacement != null) { + String subTexturePath = replacement.toString().substring(0, replacement.toString().lastIndexOf('.')); + final String subTextureName = subTexturePath.substring(subTexturePath.lastIndexOf('/') + 1); + + replacement = baseIdentifier; + if (!GENERATED_SUB_CITS_SEEN.add(replacement)) // cit generated duplicate + replacement = new Identifier(replacement.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + replacement.getPath()); + GENERATED_SUB_CITS_SEEN.add(replacement); + + JsonUnbakedModel jsonModel = loadUnbakedAsset(resourceManager, replacement); + jsonModel.getOverrides().clear(); + + ((JsonUnbakedModelAccessor) jsonModel).getTextureMap().replaceAll((layerName, texture) -> { + if (layerName != null) + try { + for (String subTexture : textureOverrideMap.keySet()) + if (subTextureName.equals(subTexture)) + return textureOverrideMap.get(subTexture); + } catch (Exception ignored) { } + return texture; + }); + + unbakedAssets.put(overrideConditions.get(overrideModel), jsonModel); + } + } + } + } + + if (!assetIdentifiers.isEmpty()) { // contains sub models + LinkedHashMap<Identifier, List<ModelOverride.Condition>> overrideConditions = new LinkedHashMap<>(); + for (Item item : this.items) { + Identifier itemIdentifier = Registry.ITEM.getId(item); + overrideConditions.put(new Identifier(itemIdentifier.getNamespace(), "item/" + itemIdentifier.getPath()), Collections.emptyList()); + + Identifier itemModelIdentifier = new Identifier(itemIdentifier.getNamespace(), "models/item/" + itemIdentifier.getPath() + ".json"); + try (Resource itemModelResource = resourceManager.getResource(itemModelIdentifier); Reader resourceReader = new InputStreamReader(itemModelResource.getInputStream())) { + JsonUnbakedModel itemModelJson = JsonUnbakedModel.deserialize(resourceReader); + + if (itemModelJson.getOverrides() != null && !itemModelJson.getOverrides().isEmpty()) + for (ModelOverride override : itemModelJson.getOverrides()) + overrideConditions.put(override.getModelId(), override.streamConditions().toList()); + } + } + + ArrayList<Identifier> overrideModels = new ArrayList<>(overrideConditions.keySet()); + Collections.reverse(overrideModels); + + for (Identifier overrideModel : overrideModels) { + Identifier replacement = assetIdentifiers.remove(overrideModel); + if (replacement == null) + continue; + + if (!GENERATED_SUB_CITS_SEEN.add(replacement)) // cit generated duplicate + replacement = new Identifier(replacement.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + replacement.getPath()); + GENERATED_SUB_CITS_SEEN.add(replacement); + + List<ModelOverride.Condition> conditions = overrideConditions.get(overrideModel); + unbakedAssets.put(conditions, loadUnbakedAsset(resourceManager, replacement)); + } + } + } + } finally { + assetIdentifiers = null; + textureOverrideMap = null; + } + } + + private JsonUnbakedModel loadUnbakedAsset(ResourceManager resourceManager, Identifier assetIdentifier) throws Exception { + final Identifier identifier; + { + Identifier possibleIdentifier = assetIdentifier; + while (possibleIdentifier.getPath().startsWith(GENERATED_SUB_CITS_PREFIX)) + possibleIdentifier = new Identifier(possibleIdentifier.getNamespace(), possibleIdentifier.getPath().substring(possibleIdentifier.getPath().substring(GENERATED_SUB_CITS_PREFIX.length()).indexOf('_') + GENERATED_SUB_CITS_PREFIX.length() + 1)); + identifier = possibleIdentifier; + } + JsonUnbakedModel json; + if (identifier.getPath().endsWith(".json")) { + InputStream is = null; + Resource resource = null; + try { + json = JsonUnbakedModel.deserialize(IOUtils.toString(is = (resource = resourceManager.getResource(identifier)).getInputStream(), StandardCharsets.UTF_8)); + json.id = assetIdentifier.toString(); + json.id = json.id.substring(0, json.id.length() - 5); + + ((JsonUnbakedModelAccessor) json).getTextureMap().replaceAll((layer, original) -> { + Optional<SpriteIdentifier> left = original.left(); + if (left.isPresent()) { + Identifier resolvedIdentifier = resolveAsset(identifier, left.get().getTextureId().getPath(), "textures", ".png", resourceManager); + if (resolvedIdentifier != null) + return Either.left(new SpriteIdentifier(left.get().getAtlasId(), new ResewnTextureIdentifier(resolvedIdentifier))); + } + return original; + }); + + if (textureOverrideMap.size() > 0) { + Map<String, Either<SpriteIdentifier, String>> jsonTextureMap = ((JsonUnbakedModelAccessor) json).getTextureMap(); + if (jsonTextureMap.size() == 0) + jsonTextureMap.put("layer0", null); + + final Either<SpriteIdentifier, String> defaultTextureOverride = textureOverrideMap.get(null); + if (defaultTextureOverride != null) + jsonTextureMap.replaceAll((layerName, spriteIdentifierStringEither) -> defaultTextureOverride); + + //jsonTextureMap.putAll(textureOverrideMap); + jsonTextureMap.replaceAll((layerName, texture) -> { + if (layerName != null) + try { + String[] split = texture.map(id -> id.getTextureId().getPath(), s -> s).split("/"); + String textureName = split[split.length - 1]; + if (textureName.endsWith(".png")) + textureName = textureName.substring(0, textureName.length() - 4); + return Objects.requireNonNull(textureOverrideMap.get(textureName)); + } catch (Exception ignored) { } + return texture; + }); + jsonTextureMap.values().removeIf(Objects::isNull); + } + + Identifier parentId = ((JsonUnbakedModelAccessor) json).getParentId(); + if (parentId != null) { + String[] parentIdPathSplit = parentId.getPath().split("/"); + if (parentId.getPath().startsWith("./") || (parentIdPathSplit.length > 2 && parentIdPathSplit[1].equals("cit"))) { + parentId = resolveAsset(identifier, parentId.getPath(), "models", ".json", resourceManager); + if (parentId != null) + ((JsonUnbakedModelAccessor) json).setParentId(new ResewnItemModelIdentifier(parentId)); + } + } + + json.getOverrides().replaceAll(override -> { + String[] modelIdPathSplit = override.getModelId().getPath().split("/"); + if (override.getModelId().getPath().startsWith("./") || (modelIdPathSplit.length > 2 && modelIdPathSplit[1].equals("cit"))) { + Identifier resolvedOverridePath = resolveAsset(identifier, override.getModelId().getPath(), "models", ".json", resourceManager); + if (resolvedOverridePath != null) + return new ModelOverride(new ResewnItemModelIdentifier(resolvedOverridePath), override.streamConditions().collect(Collectors.toList())); + } + + return override; + }); + + return json; + } finally { + IOUtils.closeQuietly(is, resource); + } + } else if (identifier.getPath().endsWith(".png")) { + json = getModelForFirstItemType(resourceManager); + if (json == null) + json = new JsonUnbakedModel(new Identifier("minecraft", "item/generated"), new ArrayList<>(), ImmutableMap.of("layer0", Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(identifier)))), true, JsonUnbakedModel.GuiLight.ITEM, ModelTransformation.NONE, new ArrayList<>()); + json.getOverrides().clear(); + json.id = identifier.toString(); + json.id = json.id.substring(0, json.id.length() - 4); + + ((JsonUnbakedModelAccessor) json).getTextureMap().replaceAll((layerName, originalTextureEither) -> { + if (textureOverrideMap.size() > 0) { + Either<SpriteIdentifier, String> textureOverride = textureOverrideMap.get(layerName); + if (textureOverride == null) + textureOverride = textureOverrideMap.get(null); + return textureOverride == null ? originalTextureEither : textureOverride; + } else + return Either.left(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new ResewnTextureIdentifier(identifier))); + }); + return json; + } + + throw new Exception("Unknown asset type"); + } + + public Identifier fixDeprecatedSubItem(String subItem, PropertyGroup properties, int position) { + if (subItem == null) + return null; + String replacement = switch (subItem) { + case "bow_pulling_standby" -> "bow"; + case "crossbow_standby" -> "crossbow"; + case "potion_bottle_drinkable" -> "potion"; + case "potion_bottle_splash" -> "splash_potion"; + case "potion_bottle_lingering" -> "lingering_potion"; + + + default -> null; + }; + + if (replacement != null) { + CITResewn.logWarnLoading(properties.messageWithDescriptorOf("Warning: Using deprecated sub item id \"" + subItem + "\" instead of \"" + replacement + "\"", position)); + + return new Identifier("minecraft", "item/" + replacement); + } + + return null; + } + + private JsonUnbakedModel getModelForFirstItemType(ResourceManager resourceManager) { + Identifier firstItemIdentifier = Registry.ITEM.getId(this.items.iterator().next()), firstItemModelIdentifier = new Identifier(firstItemIdentifier.getNamespace(), "models/item/" + firstItemIdentifier.getPath() + ".json"); + Resource itemModelResource = null; + try { + JsonUnbakedModel json = JsonUnbakedModel.deserialize(IOUtils.toString((itemModelResource = resourceManager.getResource(firstItemModelIdentifier)).getInputStream(), StandardCharsets.UTF_8)); + + if (!GENERATED_SUB_CITS_SEEN.add(firstItemModelIdentifier)) // cit generated duplicate + firstItemModelIdentifier = new Identifier(firstItemModelIdentifier.getNamespace(), GENERATED_SUB_CITS_PREFIX + GENERATED_SUB_CITS_SEEN.size() + "_" + firstItemModelIdentifier.getPath()); + GENERATED_SUB_CITS_SEEN.add(firstItemModelIdentifier); + + json.id = firstItemModelIdentifier.toString(); + json.id = json.id.substring(0, json.id.length() - 5); + return json; + } catch (Exception e) { + return null; + } finally { + IOUtils.closeQuietly(itemModelResource); + } + } + + public BakedModel getItemModel(CITContext context, int seed) { + ClientWorld world = context.world instanceof ClientWorld clientWorld ? clientWorld : null; + // get sub items or bakedModel if no sub item matches @Nullable + BakedModel bakedModel = bakedSubModels.apply(this.bakedModel, context.stack, world, context.entity, seed); + + // apply model overrides + if (bakedModel != null && bakedModel.getOverrides() != null) + bakedModel = bakedModel.getOverrides().apply(bakedModel, context.stack, world, context.entity, seed); + + return bakedModel; + } + + public static class CITOverrideList extends ModelOverrideList { + public void override(List<ModelOverride.Condition> key, BakedModel bakedModel) { + Set<Identifier> conditionTypes = new LinkedHashSet<>(Arrays.asList(this.conditionTypes)); + for (ModelOverride.Condition condition : key) + conditionTypes.add(condition.getType()); + this.conditionTypes = conditionTypes.toArray(new Identifier[0]); + + this.overrides = Arrays.copyOf(this.overrides, this.overrides.length + 1); + + Object2IntMap<Identifier> object2IntMap = new Object2IntOpenHashMap<>(); + for(int i = 0; i < this.conditionTypes.length; ++i) + object2IntMap.put(this.conditionTypes[i], i); + + this.overrides[this.overrides.length - 1] = new BakedOverride( + key.stream() + .map((condition) -> new InlinedCondition(object2IntMap.getInt(condition.getType()), condition.getThreshold())) + .toArray(InlinedCondition[]::new) + , bakedModel); + } + } + + public static class Container extends CITTypeContainer<TypeItem> { + public Container() { + super(TypeItem.class, TypeItem::new, "item"); + } + + public Set<CIT<TypeItem>> loaded = new HashSet<>(); + public Map<Item, Set<CIT<TypeItem>>> loadedTyped = new IdentityHashMap<>(); + + @Override + public void load(List<CIT<TypeItem>> parsedCITs) { + loaded.addAll(parsedCITs); + for (CIT<TypeItem> cit : parsedCITs) + for (CITCondition condition : cit.conditions) + if (condition instanceof ConditionItems items) + for (Item item : items.items) + if (item != null) + loadedTyped.computeIfAbsent(item, i -> new LinkedHashSet<>()).add(cit); + } + + @Override + public void dispose() { + loaded.clear(); + loadedTyped.clear(); + } + + public CIT<TypeItem> getCIT(CITContext context, int seed) { + return ((CITCacheItem) (Object) context.stack).citresewn$getCacheTypeItem().get(context).get(); + } + + public CIT<TypeItem> getRealTimeCIT(CITContext context) { + Set<CIT<TypeItem>> loadedForItemType = loadedTyped.get(context.stack.getItem()); + if (loadedForItemType != null) + for (CIT<TypeItem> cit : loadedForItemType) + if (cit.test(context)) + return cit; + + return null; + } + } + + public interface CITCacheItem { + CITCache.Single<TypeItem> citresewn$getCacheTypeItem(); + } + + public interface BakedModelManagerMixinAccess { + void citresewn$forceMojankModel(BakedModel model); + } +}
\ No newline at end of file diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnItemModelIdentifier.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnItemModelIdentifier.java new file mode 100644 index 0000000..498f61f --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnItemModelIdentifier.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.common; + +import net.minecraft.util.Identifier; + +/** + * Marks models as cit models. + */ +public class ResewnItemModelIdentifier extends Identifier { + public ResewnItemModelIdentifier(String id) { + super(id); + } + + public ResewnItemModelIdentifier(Identifier identifier) { + super(identifier.getNamespace(), identifier.getPath()); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnTextureIdentifier.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnTextureIdentifier.java new file mode 100644 index 0000000..a6dd2ef --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/common/ResewnTextureIdentifier.java @@ -0,0 +1,12 @@ +package shcm.shsupercm.fabric.citresewn.defaults.common; + +import net.minecraft.util.Identifier; + +/** + * Marks path identifiers as forced literal texture paths. + */ +public class ResewnTextureIdentifier extends Identifier { + public ResewnTextureIdentifier(Identifier identifier) { + super(identifier.getNamespace(), identifier.getPath()); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfig.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfig.java new file mode 100644 index 0000000..41126d2 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfig.java @@ -0,0 +1,50 @@ +package shcm.shsupercm.fabric.citresewn.defaults.config; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonWriter; +import org.apache.commons.io.IOUtils; +import shcm.shsupercm.fabric.citresewn.CITResewn; + +import java.io.*; + +public class CITResewnDefaultsConfig { + public float type_enchantment_scroll_multiplier = 1f; + + private static final File FILE = new File("config/citresewn-defaults.json"); + + public static final CITResewnDefaultsConfig INSTANCE = read(); + + public static CITResewnDefaultsConfig read() { + if (!FILE.exists()) + return new CITResewnDefaultsConfig().write(); + + Reader reader = null; + try { + return new Gson().fromJson(reader = new FileReader(FILE), CITResewnDefaultsConfig.class); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(reader); + } + } + + public CITResewnDefaultsConfig write() { + Gson gson = new Gson(); + JsonWriter writer = null; + try { + FILE.getParentFile().mkdirs(); + writer = gson.newJsonWriter(new FileWriter(FILE)); + writer.setIndent(" "); + + gson.toJson(gson.toJsonTree(this, CITResewnDefaultsConfig.class), writer); + } catch (Exception e) { + CITResewn.LOG.error("Couldn't save defaults config"); + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(writer); + } + return this; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfigScreenFactory.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfigScreenFactory.java new file mode 100644 index 0000000..95b0579 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsConfigScreenFactory.java @@ -0,0 +1,33 @@ +package shcm.shsupercm.fabric.citresewn.defaults.config; + +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.LiteralText; +import net.minecraft.text.TranslatableText; +import shcm.shsupercm.fabric.citresewn.config.CITResewnConfigScreenFactory; + +public class CITResewnDefaultsConfigScreenFactory { + @Entrypoint(CITResewnConfigScreenFactory.DEFAULTS_CONFIG_ENTRYPOINT) + public static Screen create(Screen parent) { + CITResewnDefaultsConfig currentConfig = CITResewnDefaultsConfig.INSTANCE, defaultConfig = new CITResewnDefaultsConfig(); + + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(new TranslatableText("config.citresewn-defaults.title")) + .setSavingRunnable(currentConfig::write); + + ConfigCategory category = builder.getOrCreateCategory(new LiteralText("")); + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + + category.addEntry(entryBuilder.startFloatField(new TranslatableText("config.citresewn-defaults.type_enchantment_scroll_multiplier.title"), currentConfig.type_enchantment_scroll_multiplier) + .setTooltip(new TranslatableText("config.citresewn-defaults.type_enchantment_scroll_multiplier.tooltip")) + .setSaveConsumer(newConfig -> currentConfig.type_enchantment_scroll_multiplier = newConfig) + .setDefaultValue(defaultConfig.type_enchantment_scroll_multiplier) + .build()); + + return builder.build(); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsModMenu.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsModMenu.java new file mode 100644 index 0000000..48edf33 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/config/CITResewnDefaultsModMenu.java @@ -0,0 +1,27 @@ +package shcm.shsupercm.fabric.citresewn.defaults.config; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import io.shcm.shsupercm.fabric.fletchingtable.api.Entrypoint; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.NoticeScreen; +import net.minecraft.text.Text; + +@Entrypoint("modmenu") +public class CITResewnDefaultsModMenu implements ModMenuApi { + @Override + public ConfigScreenFactory<?> getModConfigScreenFactory() { + if (FabricLoader.getInstance().isModLoaded("cloth-config2")) + return new ClothConfigOpenImpl().getModConfigScreenFactory(); + + return parent -> new NoticeScreen(() -> MinecraftClient.getInstance().setScreen(parent), Text.of("CIT Resewn: Defaults"), Text.of("CIT Resewn requires Cloth Config to be able to show the config.")); + } + + private static class ClothConfigOpenImpl implements ModMenuApi { + @Override + public ConfigScreenFactory<?> getModConfigScreenFactory() { + return CITResewnDefaultsConfigScreenFactory::create; + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/ModelLoaderMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/ModelLoaderMixin.java new file mode 100644 index 0000000..8ed0e20 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/ModelLoaderMixin.java @@ -0,0 +1,87 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.common; + +import com.mojang.datafixers.util.Either; +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.render.model.json.ModelOverride; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import org.apache.commons.io.IOUtils; +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; +import shcm.shsupercm.fabric.citresewn.cit.CITType; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnItemModelIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnTextureIdentifier; +import shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item.JsonUnbakedModelAccessor; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Will be rewritten at some point. + */ +@Mixin(ModelLoader.class) +public class ModelLoaderMixin { + @Shadow @Final private ResourceManager resourceManager; + + @Inject(method = "loadModelFromJson", cancellable = true, at = @At("HEAD")) + public void citresewn$forceLiteralResewnModelIdentifier(Identifier id, CallbackInfoReturnable<JsonUnbakedModel> cir) { + if (id instanceof ResewnItemModelIdentifier) { + InputStream is = null; + Resource resource = null; + try { + JsonUnbakedModel json = JsonUnbakedModel.deserialize(IOUtils.toString(is = (resource = resourceManager.getResource(id)).getInputStream(), StandardCharsets.UTF_8)); + json.id = id.toString(); + json.id = json.id.substring(0, json.id.length() - 5); + + ((JsonUnbakedModelAccessor) json).getTextureMap().replaceAll((layer, original) -> { + Optional<SpriteIdentifier> left = original.left(); + if (left.isPresent()) { + String originalPath = left.get().getTextureId().getPath(); + String[] split = originalPath.split("/"); + if (originalPath.startsWith("./") || (split.length > 2 && split[1].equals("cit"))) { + Identifier resolvedIdentifier = CITType.resolveAsset(id, originalPath, "textures", ".png", resourceManager); + if (resolvedIdentifier != null) + return Either.left(new SpriteIdentifier(left.get().getAtlasId(), new ResewnTextureIdentifier(resolvedIdentifier))); + } + } + return original; + }); + + Identifier parentId = ((JsonUnbakedModelAccessor) json).getParentId(); + if (parentId != null) { + String[] parentIdPathSplit = parentId.getPath().split("/"); + if (parentId.getPath().startsWith("./") || (parentIdPathSplit.length > 2 && parentIdPathSplit[1].equals("cit"))) { + parentId = CITType.resolveAsset(id, parentId.getPath(), "models", ".json", resourceManager); + if (parentId != null) + ((JsonUnbakedModelAccessor) json).setParentId(new ResewnItemModelIdentifier(parentId)); + } + } + + json.getOverrides().replaceAll(override -> { + String[] modelIdPathSplit = override.getModelId().getPath().split("/"); + if (override.getModelId().getPath().startsWith("./") || (modelIdPathSplit.length > 2 && modelIdPathSplit[1].equals("cit"))) { + Identifier resolvedOverridePath = CITType.resolveAsset(id, override.getModelId().getPath(), "models", ".json", resourceManager); + if (resolvedOverridePath != null) + return new ModelOverride(new ResewnItemModelIdentifier(resolvedOverridePath), override.streamConditions().collect(Collectors.toList())); + } + + return override; + }); + + cir.setReturnValue(json); + } catch (Exception ignored) { + } finally { + IOUtils.closeQuietly(is, resource); + } + } + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/SpriteAtlasTextureMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/SpriteAtlasTextureMixin.java new file mode 100644 index 0000000..61eff5c --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/common/SpriteAtlasTextureMixin.java @@ -0,0 +1,18 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.common; + +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.util.Identifier; +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; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnTextureIdentifier; + +@Mixin(SpriteAtlasTexture.class) +public class SpriteAtlasTextureMixin { + @Inject(method = "getTexturePath", cancellable = true, at = @At("HEAD")) + public void forceLiteralResewnTextureIdentifier(Identifier id, CallbackInfoReturnable<Identifier> cir) { + if (id instanceof ResewnTextureIdentifier) + cir.setReturnValue(id); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ArmorFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ArmorFeatureRendererMixin.java new file mode 100644 index 0000000..d61af4c --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ArmorFeatureRendererMixin.java @@ -0,0 +1,51 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.armor; + +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ArmorItem; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import shcm.shsupercm.fabric.citresewn.cit.CIT; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeArmor; + +import java.util.Map; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeArmor.CONTAINER; + +@Mixin(ArmorFeatureRenderer.class) +public class ArmorFeatureRendererMixin<T extends LivingEntity, M extends BipedEntityModel<T>, A extends BipedEntityModel<T>> { + private Map<String, Identifier> citresewn$cachedTextures = null; + + @Inject(method = "renderArmor", at = @At("HEAD")) + public void citresewn$renderArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) { + citresewn$cachedTextures = null; + if (!CONTAINER.active()) + return; + + ItemStack equippedStack = entity.getEquippedStack(armorSlot); + + CIT<TypeArmor> cit = CONTAINER.getCIT(new CITContext(equippedStack, entity.getWorld(), entity)); + if (cit != null) + citresewn$cachedTextures = cit.type.textures; + } + + @Inject(method = "getArmorTexture", cancellable = true, at = @At("HEAD")) + private void citresewn$getArmorTexture(ArmorItem item, boolean legs, String overlay, CallbackInfoReturnable<Identifier> cir) { + if (citresewn$cachedTextures == null) + return; + + Identifier identifier = citresewn$cachedTextures.get(item.getMaterial().getName() + "_layer_" + (legs ? "2" : "1") + (overlay == null ? "" : "_" + overlay)); + if (identifier != null) + cir.setReturnValue(identifier); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ItemStackMixin.java new file mode 100644 index 0000000..85b0d22 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/armor/ItemStackMixin.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.armor; + +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import shcm.shsupercm.fabric.citresewn.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeArmor; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeArmor.CITCacheArmor { + private final CITCache.Single<TypeArmor> citresewn$cacheTypeArmor = new CITCache.Single<>(TypeArmor.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.Single<TypeArmor> citresewn$getCacheTypeArmor() { + return this.citresewn$cacheTypeArmor; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ElytraFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ElytraFeatureRendererMixin.java new file mode 100644 index 0000000..0772adf --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ElytraFeatureRendererMixin.java @@ -0,0 +1,42 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.elytra; + +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.feature.ElytraFeatureRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +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.CallbackInfo; +import shcm.shsupercm.fabric.citresewn.cit.CIT; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeElytra; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeElytra.CONTAINER; + +@Mixin(ElytraFeatureRenderer.class) +public class ElytraFeatureRendererMixin { + @Shadow @Mutable @Final private static Identifier SKIN; + + private final static Identifier citresewn$ORIGINAL_SKIN = SKIN; + + @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at = @At("HEAD")) + public void citresewn$render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { + if (!CONTAINER.active()) + return; + + ItemStack equippedStack = livingEntity.getEquippedStack(EquipmentSlot.CHEST); + if (!equippedStack.isOf(Items.ELYTRA)) + return; + + CIT<TypeElytra> cit = CONTAINER.getCIT(new CITContext(equippedStack, livingEntity.getWorld(), livingEntity)); + SKIN = cit == null ? citresewn$ORIGINAL_SKIN : cit.type.texture; + } +}
\ No newline at end of file diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ItemStackMixin.java new file mode 100644 index 0000000..a751fb9 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/elytra/ItemStackMixin.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.elytra; + +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import shcm.shsupercm.fabric.citresewn.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeElytra; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeElytra.CITCacheElytra { + private final CITCache.Single<TypeElytra> citresewn$cacheTypeElytra = new CITCache.Single<>(TypeElytra.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.Single<TypeElytra> citresewn$getCacheTypeElytra() { + return this.citresewn$cacheTypeElytra; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ArmorFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ArmorFeatureRendererMixin.java new file mode 100644 index 0000000..4a852ca --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ArmorFeatureRendererMixin.java @@ -0,0 +1,30 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +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.CallbackInfo; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment.CONTAINER; + +@Mixin(ArmorFeatureRenderer.class) +public class ArmorFeatureRendererMixin<T extends LivingEntity, M extends BipedEntityModel<T>, A extends BipedEntityModel<T>> { + @Inject(method = "renderArmor", at = @At("HEAD")) + private void citresewn$enchantment$setAppliedContextAndStartApplyingArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T livingEntity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(new CITContext(livingEntity.getEquippedStack(armorSlot), livingEntity.world, livingEntity)).apply(); + } + + @Inject(method = "renderArmor", at = @At("RETURN")) + private void citresewn$enchantment$stopApplyingArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T livingEntity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(null); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/BufferBuilderStorageAccessor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/BufferBuilderStorageAccessor.java new file mode 100644 index 0000000..e23e091 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/BufferBuilderStorageAccessor.java @@ -0,0 +1,15 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.BufferBuilderStorage; +import net.minecraft.client.render.RenderLayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.SortedMap; + +@Mixin(BufferBuilderStorage.class) +public interface BufferBuilderStorageAccessor { + @Accessor + SortedMap<RenderLayer, BufferBuilder> getEntityBuilders(); +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ElytraFeatureRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ElytraFeatureRendererMixin.java new file mode 100644 index 0000000..c64fb77 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ElytraFeatureRendererMixin.java @@ -0,0 +1,29 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.feature.ElytraFeatureRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +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.CallbackInfo; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment.CONTAINER; + +@Mixin(ElytraFeatureRenderer.class) +public class ElytraFeatureRendererMixin { + @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at = @At("HEAD")) + private void citresewn$enchantment$setAppliedContextAndStartApplyingElytra(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(new CITContext(livingEntity.getEquippedStack(EquipmentSlot.CHEST), livingEntity.world, livingEntity)).apply(); + } + + @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at = @At("RETURN")) + private void citresewn$enchantment$stopApplyingElytra(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(null); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemRendererMixin.java new file mode 100644 index 0000000..b4bd37d --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemRendererMixin.java @@ -0,0 +1,86 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.*; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.json.ModelTransformation; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment.CONTAINER; + +@Mixin(value = ItemRenderer.class, priority = 200) +public class ItemRendererMixin { + @Inject(method = "getModel", at = @At("HEAD")) + private void citresewn$enchantment$setAppliedContext(ItemStack stack, World world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) { + if (CONTAINER.active()) + CONTAINER.setContext(new CITContext(stack, world, entity)); + } + + @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", at = @At("HEAD")) + private void citresewn$enchantment$startApplyingItem(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.apply(); + } + + @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", at = @At("RETURN")) + private void citresewn$enchantment$stopApplyingItem(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { + if (CONTAINER.active()) + CONTAINER.setContext(null); + } + + @Inject(method = "getArmorGlintConsumer", cancellable = true, at = @At("RETURN")) + private static void citresewn$enchantment$getArmorGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = solid ? TypeEnchantment.GlintRenderLayer.ARMOR_GLINT.tryApply(cir.getReturnValue(), layer, provider) : TypeEnchantment.GlintRenderLayer.ARMOR_ENTITY_GLINT.tryApply(cir.getReturnValue(), layer, provider); + if (vertexConsumer != null) + cir.setReturnValue(vertexConsumer); + } + + @Inject(method = "getCompassGlintConsumer", cancellable = true, at = @At("RETURN")) + private static void citresewn$enchantment$getCompassGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, MatrixStack.Entry entry, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = TypeEnchantment.GlintRenderLayer.GLINT.tryApply(null, layer, provider); + if (vertexConsumer != null) + cir.setReturnValue(VertexConsumers.union(new OverlayVertexConsumer(vertexConsumer, entry.getPositionMatrix(), entry.getNormalMatrix()), cir.getReturnValue())); + } + + @Inject(method = "getDirectCompassGlintConsumer", cancellable = true, at = @At("RETURN")) + private static void citresewn$enchantment$getDirectCompassGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, MatrixStack.Entry entry, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = TypeEnchantment.GlintRenderLayer.DIRECT_GLINT.tryApply(null, layer, provider); + if (vertexConsumer != null) + cir.setReturnValue(VertexConsumers.union(new OverlayVertexConsumer(vertexConsumer, entry.getPositionMatrix(), entry.getNormalMatrix()), cir.getReturnValue())); + } + + @Inject(method = "getItemGlintConsumer", cancellable = true, at = @At("RETURN")) + private static void citresewn$enchantment$getItemGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = MinecraftClient.isFabulousGraphicsOrBetter() && layer == TexturedRenderLayers.getItemEntityTranslucentCull() ? TypeEnchantment.GlintRenderLayer.GLINT_TRANSLUCENT.tryApply(cir.getReturnValue(), layer, provider) : (solid ? TypeEnchantment.GlintRenderLayer.GLINT.tryApply(cir.getReturnValue(), layer, provider) : TypeEnchantment.GlintRenderLayer.ENTITY_GLINT.tryApply(cir.getReturnValue(), layer, provider)); + if (vertexConsumer != null) + cir.setReturnValue(vertexConsumer); + } + + @Inject(method = "getDirectItemGlintConsumer", cancellable = true, at = @At("RETURN")) + private static void citresewn$enchantment$getDirectItemGlintConsumer(VertexConsumerProvider provider, RenderLayer layer, boolean solid, boolean glint, CallbackInfoReturnable<VertexConsumer> cir) { + if (!CONTAINER.shouldApply()) + return; + VertexConsumer vertexConsumer = solid ? TypeEnchantment.GlintRenderLayer.DIRECT_GLINT.tryApply(cir.getReturnValue(), layer, provider) : TypeEnchantment.GlintRenderLayer.DIRECT_ENTITY_GLINT.tryApply(cir.getReturnValue(), layer, provider); + if (vertexConsumer != null) + cir.setReturnValue(vertexConsumer); + } +}
\ No newline at end of file diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemStackMixin.java new file mode 100644 index 0000000..d49e116 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/ItemStackMixin.java @@ -0,0 +1,25 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +import net.minecraft.item.ItemStack; +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; +import shcm.shsupercm.fabric.citresewn.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeEnchantment.CITCacheEnchantment { + private final CITCache.MultiList<TypeEnchantment> citresewn$cacheTypeEnchantment = new CITCache.MultiList<>(TypeEnchantment.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.MultiList<TypeEnchantment> citresewn$getCacheTypeEnchantment() { + return this.citresewn$cacheTypeEnchantment; + } + + @Inject(method = "hasGlint", cancellable = true, at = @At("HEAD")) + private void citresewn$enchantment$disableDefaultGlint(CallbackInfoReturnable<Boolean> cir) { + if (TypeEnchantment.CONTAINER.shouldNotApplyDefaultGlint()) + cir.setReturnValue(false); + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/MinecraftClientMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/MinecraftClientMixin.java new file mode 100644 index 0000000..e8b346b --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/MinecraftClientMixin.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +import net.minecraft.client.MinecraftClient; +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.CallbackInfo; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeEnchantment; + +@Mixin(MinecraftClient.class) +public class MinecraftClientMixin { + @Inject(method = "tick", at = @At("HEAD")) + public void onTick(CallbackInfo ci) { + TypeEnchantment.MergeMethodIntensity.MergeMethod.ticks++; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/RenderPhaseAccessor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/RenderPhaseAccessor.java new file mode 100644 index 0000000..b2a59b4 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/enchantment/RenderPhaseAccessor.java @@ -0,0 +1,21 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.enchantment; + +import net.minecraft.client.render.RenderPhase; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(RenderPhase.class) +public interface RenderPhaseAccessor { + @Accessor("ARMOR_GLINT_SHADER") static RenderPhase.Shader ARMOR_GLINT_SHADER() { throw new RuntimeException(); } + @Accessor("ARMOR_ENTITY_GLINT_SHADER") static RenderPhase.Shader ARMOR_ENTITY_GLINT_SHADER() { throw new RuntimeException(); } + @Accessor("TRANSLUCENT_GLINT_SHADER") static RenderPhase.Shader TRANSLUCENT_GLINT_SHADER() { throw new RuntimeException(); } + @Accessor("GLINT_SHADER") static RenderPhase.Shader GLINT_SHADER() { throw new RuntimeException(); } + @Accessor("DIRECT_GLINT_SHADER") static RenderPhase.Shader DIRECT_GLINT_SHADER() { throw new RuntimeException(); } + @Accessor("ENTITY_GLINT_SHADER") static RenderPhase.Shader ENTITY_GLINT_SHADER() { throw new RuntimeException(); } + @Accessor("DIRECT_ENTITY_GLINT_SHADER") static RenderPhase.Shader DIRECT_ENTITY_GLINT_SHADER() { throw new RuntimeException(); } + @Accessor("DISABLE_CULLING") static RenderPhase.Cull DISABLE_CULLING() { throw new RuntimeException(); } + @Accessor("EQUAL_DEPTH_TEST") static RenderPhase.DepthTest EQUAL_DEPTH_TEST() { throw new RuntimeException(); } + @Accessor("COLOR_MASK") static RenderPhase.WriteMaskState COLOR_MASK() { throw new RuntimeException(); } + @Accessor("VIEW_OFFSET_Z_LAYERING") static RenderPhase.Layering VIEW_OFFSET_Z_LAYERING() { throw new RuntimeException(); } + @Accessor("ITEM_TARGET") static RenderPhase.Target ITEM_TARGET() { throw new RuntimeException(); } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/BakedModelManagerMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/BakedModelManagerMixin.java new file mode 100644 index 0000000..f7f6b48 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/BakedModelManagerMixin.java @@ -0,0 +1,29 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.client.util.ModelIdentifier; +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; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem; + +@Mixin(BakedModelManager.class) +public class BakedModelManagerMixin implements TypeItem.BakedModelManagerMixinAccess { + private BakedModel citresewn$forcedMojankModel = null; + + @Inject(method = "getModel", cancellable = true, at = + @At("HEAD")) + private void citresewn$getCITMojankModel(ModelIdentifier id, CallbackInfoReturnable<BakedModel> cir) { + if (citresewn$forcedMojankModel != null) { + cir.setReturnValue(citresewn$forcedMojankModel); + citresewn$forcedMojankModel = null; + } + } + + @Override + public void citresewn$forceMojankModel(BakedModel model) { + this.citresewn$forcedMojankModel = model; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemRendererMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemRendererMixin.java new file mode 100644 index 0000000..5f59329 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemRendererMixin.java @@ -0,0 +1,68 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.item.ItemModels; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.json.ModelTransformation; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.world.World; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import shcm.shsupercm.fabric.citresewn.cit.CIT; +import shcm.shsupercm.fabric.citresewn.cit.CITContext; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem; + +import java.lang.ref.WeakReference; + +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem.CONTAINER; + +/** + * Do not go through this class, it looks awful because it was ported from a "proof of concept".<br> + * The whole type will be rewritten at some point. + */ +@Mixin(ItemRenderer.class) +public class ItemRendererMixin { + @Shadow @Final private ItemModels models; + + private WeakReference<BakedModel> citresewn$mojankCITModel = null; + + @Inject(method = "getModel", cancellable = true, at = @At("HEAD")) + private void citresewn$getItemModel(ItemStack stack, World world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) { + if (!CONTAINER.active()) + return; + + CITContext context = new CITContext(stack, world, entity); + CIT<TypeItem> cit = CONTAINER.getCIT(context, seed); + citresewn$mojankCITModel = null; + if (cit != null) { + BakedModel citModel = cit.type.getItemModel(context, seed); + + if (citModel != null) { + if (stack.isOf(Items.TRIDENT) || stack.isOf(Items.SPYGLASS)) { + citresewn$mojankCITModel = new WeakReference<>(citModel); + } else + cir.setReturnValue(citModel); + } + } + } + + @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", at = @At("HEAD")) + private void citresewn$fixMojankCITsContext(ItemStack stack, ModelTransformation.Mode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { + if (!CONTAINER.active() || citresewn$mojankCITModel == null) + return; + + if (renderMode == ModelTransformation.Mode.GUI || renderMode == ModelTransformation.Mode.GROUND || renderMode == ModelTransformation.Mode.FIXED) + ((TypeItem.BakedModelManagerMixinAccess) this.models.getModelManager()).citresewn$forceMojankModel(citresewn$mojankCITModel.get()); + + citresewn$mojankCITModel = null; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemStackMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemStackMixin.java new file mode 100644 index 0000000..450d8a3 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ItemStackMixin.java @@ -0,0 +1,16 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import shcm.shsupercm.fabric.citresewn.cit.CITCache; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem; + +@Mixin(ItemStack.class) +public class ItemStackMixin implements TypeItem.CITCacheItem { + private final CITCache.Single<TypeItem> citresewn$cacheTypeItem = new CITCache.Single<>(TypeItem.CONTAINER::getRealTimeCIT); + + @Override + public CITCache.Single<TypeItem> citresewn$getCacheTypeItem() { + return this.citresewn$cacheTypeItem; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/JsonUnbakedModelAccessor.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/JsonUnbakedModelAccessor.java new file mode 100644 index 0000000..350d01a --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/JsonUnbakedModelAccessor.java @@ -0,0 +1,22 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import com.mojang.datafixers.util.Either; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(JsonUnbakedModel.class) +public interface JsonUnbakedModelAccessor { + @Accessor + Map<String, Either<SpriteIdentifier, String>> getTextureMap(); + + @Accessor + Identifier getParentId(); + + @Accessor + void setParentId(Identifier parentId); +} diff --git a/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ModelLoaderMixin.java b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ModelLoaderMixin.java new file mode 100644 index 0000000..93bab6a --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/fabric/citresewn/defaults/mixin/types/item/ModelLoaderMixin.java @@ -0,0 +1,99 @@ +package shcm.shsupercm.fabric.citresewn.defaults.mixin.types.item; + +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.render.model.SpriteAtlasManager; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.render.model.json.ModelOverride; +import net.minecraft.client.texture.TextureManager; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; +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.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import shcm.shsupercm.fabric.citresewn.CITResewn; +import shcm.shsupercm.fabric.citresewn.cit.CIT; +import shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem; +import shcm.shsupercm.fabric.citresewn.defaults.common.ResewnItemModelIdentifier; + +import java.util.*; + +import static shcm.shsupercm.fabric.citresewn.CITResewn.info; +import static shcm.shsupercm.fabric.citresewn.defaults.cit.types.TypeItem.CONTAINER; + +@Mixin(ModelLoader.class) +public class ModelLoaderMixin { + @Shadow @Final private Set<Identifier> modelsToLoad; + @Shadow @Final private Map<Identifier, UnbakedModel> modelsToBake; + @Shadow @Final private Map<Identifier, UnbakedModel> unbakedModels; + @Shadow @Final private Map<Identifier, BakedModel> bakedModels; + + @Inject(method = "<init>", at = + @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V")) + public void citresewn$addTypeItemModels(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int i, CallbackInfo ci) { + profiler.swap("citresewn:type_item_models"); + if (!CONTAINER.active()) + return; + + info("Loading item CIT models..."); + for (CIT<TypeItem> cit : CONTAINER.loaded) + try { + cit.type.loadUnbakedAssets(resourceManager); + + for (JsonUnbakedModel unbakedModel : cit.type.unbakedAssets.values()) { + ResewnItemModelIdentifier id = new ResewnItemModelIdentifier(unbakedModel.id); + this.unbakedModels.put(id, unbakedModel); + this.modelsToLoad.addAll(unbakedModel.getModelDependencies()); + this.modelsToBake.put(id, unbakedModel); + } + } catch (Exception e) { + CITResewn.logErrorLoading("Errored loading model in " + cit.propertiesIdentifier + " from " + cit.packName); + e.printStackTrace(); + } + + TypeItem.GENERATED_SUB_CITS_SEEN.clear(); + } + + @Inject(method = "upload", at = @At("RETURN")) + public void citresewn$linkTypeItemModels(TextureManager textureManager, Profiler profiler, CallbackInfoReturnable<SpriteAtlasManager> cir) { + if (!CONTAINER.active()) + return; + + profiler.push("citresewn:type_item_linking"); + info("Linking baked models to item CITs..."); + + for (CIT<TypeItem> cit : CONTAINER.loaded) { + for (Map.Entry<List<ModelOverride.Condition>, JsonUnbakedModel> citModelEntry : cit.type.unbakedAssets.entrySet()) { + if (citModelEntry.getKey() == null) { + cit.type.bakedModel = this.bakedModels.get(new ResewnItemModelIdentifier(citModelEntry.getValue().id)); + } else { + BakedModel bakedModel = bakedModels.get(new ResewnItemModelIdentifier(citModelEntry.getValue().id)); + if (bakedModel == null) + CITResewn.logWarnLoading("Skipping sub cit: Failed loading model for \"" + citModelEntry.getValue().id + "\" in " + cit.propertiesIdentifier + " from " + cit.packName); + else + cit.type.bakedSubModels.override(citModelEntry.getKey(), bakedModel); + } + } + cit.type.unbakedAssets = null; + } + + profiler.pop(); + } + + @ModifyArg(method = "loadModelFromJson", at = + @At(value = "INVOKE", target = "Lnet/minecraft/resource/ResourceManager;getResource(Lnet/minecraft/util/Identifier;)Lnet/minecraft/resource/Resource;")) + public Identifier citresewn$fixDuplicatePrefixSuffix(Identifier original) { + if (CONTAINER.active() && original.getPath().startsWith("models/models/") && original.getPath().endsWith(".json.json") && original.getPath().contains("cit")) + return new Identifier(original.getNamespace(), original.getPath().substring(7, original.getPath().length() - 5)); + + return original; + } +} diff --git a/defaults/src/main/java/shcm/shsupercm/util/logic/Loops.java b/defaults/src/main/java/shcm/shsupercm/util/logic/Loops.java new file mode 100644 index 0000000..6c18244 --- /dev/null +++ b/defaults/src/main/java/shcm/shsupercm/util/logic/Loops.java @@ -0,0 +1,60 @@ +package shcm.shsupercm.util.logic; + +import java.util.*; + +/** + * This class(or class portion) is a part of SHCM's utilities. Feel free to use without credit. + */ +public class Loops { + /** + * Creates a loop of T with linked intensities allowing for fading between the elements. + * @param items list of items and pause durations(in time units) ordered as they are in the loop + * @param fade time in units to fade between each item + * @param ticks positive raising counter + * @param tpu the amount of ticks per time unit + * @param <T> element type + * @return map of elements and their respective intensities(between 0.0f and 1.0f) + */ + public static <T> Map<T, Float> statelessFadingLoop(List<Map.Entry<T, Float>> items, float fade, int ticks, int tpu) { + Map<T, Float> itemValues = new HashMap<>(); + + if (items == null || items.size() == 0) + return itemValues; + + if (items.size() == 1) { + itemValues.put(items.get(0).getKey(), 1f); + return itemValues; + } + + float totalUnitsInLoop = 0f; + for (Map.Entry<T, Float> item : items) { + itemValues.put(item.getKey(), 0f); + totalUnitsInLoop += item.getValue() + fade; + } + + float unitInLoop = (ticks % (tpu * totalUnitsInLoop)) / tpu; + + for (int i = 0; i < items.size(); i++) { + Map.Entry<T, Float> item = items.get(i); + if (unitInLoop < item.getValue()) { + itemValues.put(item.getKey(), 1f); + break; + } else + unitInLoop -= item.getValue(); + + if (unitInLoop < fade) { + Map.Entry<T, Float> nextItem = items.get(i + 1 >= items.size() ? 0 : i + 1); + + unitInLoop /= fade; + + itemValues.put(item.getKey(), 1f - unitInLoop); + itemValues.put(nextItem.getKey(), unitInLoop); + + break; + } else + unitInLoop -= fade; + } + + return itemValues; + } +}
\ No newline at end of file diff --git a/defaults/src/main/resources/assets/citresewn-defaults/lang/en_us.json b/defaults/src/main/resources/assets/citresewn-defaults/lang/en_us.json new file mode 100644 index 0000000..5f68e45 --- /dev/null +++ b/defaults/src/main/resources/assets/citresewn-defaults/lang/en_us.json @@ -0,0 +1,7 @@ +{ + "config.citresewn-defaults.title": "CIT Resewn: Defaults", + "config.citresewn-defaults.tooltip": "Go to the defaults' config menu", + + "config.citresewn-defaults.type_enchantment_scroll_multiplier.title": "Type Enchantment: Scroll multiplier", + "config.citresewn-defaults.type_enchantment_scroll_multiplier.tooltip": "A general multiplier for how fast cit enchantment glints scroll" +}
\ No newline at end of file diff --git a/defaults/src/main/resources/assets/citresewn-defaults/logo.png b/defaults/src/main/resources/assets/citresewn-defaults/logo.png Binary files differnew file mode 100644 index 0000000..35859ac --- /dev/null +++ b/defaults/src/main/resources/assets/citresewn-defaults/logo.png diff --git a/defaults/src/main/resources/citresewn-defaults.accesswidener b/defaults/src/main/resources/citresewn-defaults.accesswidener new file mode 100644 index 0000000..739fc5e --- /dev/null +++ b/defaults/src/main/resources/citresewn-defaults.accesswidener @@ -0,0 +1,30 @@ +accessWidener v1 named + +# ConditionNBT +accessible field net/minecraft/nbt/NbtCompound entries Ljava/util/Map; + +# TypeItem +accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride +accessible class net/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition +accessible method net/minecraft/client/render/model/json/ModelOverrideList <init> ()V +accessible method net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride <init> ([Lnet/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition;Lnet/minecraft/client/render/model/BakedModel;)V +accessible method net/minecraft/client/render/model/json/ModelOverrideList$InlinedCondition <init> (IF)V +accessible field net/minecraft/client/render/model/json/ModelOverrideList overrides [Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride; +mutable field net/minecraft/client/render/model/json/ModelOverrideList overrides [Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride; +accessible field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier; +mutable field net/minecraft/client/render/model/json/ModelOverrideList conditionTypes [Lnet/minecraft/util/Identifier; + +# TypeEnchantment +accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters +accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;ILnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; +accessible class net/minecraft/client/render/RenderPhase$Shader +accessible class net/minecraft/client/render/RenderPhase$Texturing +accessible class net/minecraft/client/render/RenderPhase$Transparency +accessible class net/minecraft/client/render/RenderPhase$Texture +accessible class net/minecraft/client/render/RenderPhase$Lightmap +accessible class net/minecraft/client/render/RenderPhase$Overlay +accessible class net/minecraft/client/render/RenderPhase$Cull +accessible class net/minecraft/client/render/RenderPhase$DepthTest +accessible class net/minecraft/client/render/RenderPhase$WriteMaskState +accessible class net/minecraft/client/render/RenderPhase$Layering +accessible class net/minecraft/client/render/RenderPhase$Target
\ No newline at end of file diff --git a/defaults/src/main/resources/citresewn-defaults.mixins.json b/defaults/src/main/resources/citresewn-defaults.mixins.json new file mode 100644 index 0000000..536380b --- /dev/null +++ b/defaults/src/main/resources/citresewn-defaults.mixins.json @@ -0,0 +1,9 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "shcm.shsupercm.fabric.citresewn.defaults.mixin", + "compatibilityLevel": "JAVA_17", + "injectors": { + "defaultRequire": 1 + } +} diff --git a/defaults/src/main/resources/fabric.mod.json b/defaults/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..f35e7bb --- /dev/null +++ b/defaults/src/main/resources/fabric.mod.json @@ -0,0 +1,36 @@ +{ + "schemaVersion": 1, + "id": "citresewn-defaults", + "version": "${version}", + "name": "CIT Resewn: Defaults", + "description": "Default types and conditions for CIT Resewn", + "authors": [ + "SHsuperCM" + ], + "license": "MIT", + "icon": "assets/citresewn-defaults/logo.png", + "contact": { + "homepage": "https://citresewn.shcm.io/", + "sources": "https://github.com/SHsuperCM/CITResewn", + "issues": "https://github.com/SHsuperCM/CITResewn/issues" + }, + + "environment": "client", + "accessWidener" : "citresewn-defaults.accesswidener", + "mixins": [ + "citresewn-defaults.mixins.json" + ], + "custom": { + "modmenu": { + "parent": { + "id": "citresewn" + } + } + }, + "depends": { + "citresewn": "${version}" + }, + "conflicts": { + "eatinganimationid": "*" + } +} |