Compare commits

...

32 commits
v1.1 ... main

Author SHA1 Message Date
1d847e6880 place buttons in single column 2025-01-22 20:08:04 +01:00
38d94c663b improve buttons 2025-01-22 20:08:04 +01:00
5c161a0f7c uniques done 2025-01-22 20:08:04 +01:00
6b50b6cb3b unique support 90% done 2025-01-22 20:08:04 +01:00
3325839d7d trying to add uniques 2025-01-22 20:08:04 +01:00
5e02499406 hide legendary button on crafted and transmog screens 2025-01-22 20:08:04 +01:00
10f586e5db crafted modifiers and transmogs 2025-01-22 20:08:04 +01:00
a355c7982a improve lvl input - only allow numeric inputs, right click to clear, middle to set player lvl, 2025-01-22 20:08:04 +01:00
97d87a1d36 1.5 version bump 2025-01-22 20:08:04 +01:00
f011e8509b 1.4 rang support 2024-12-13 02:56:53 +01:00
2e10d4afce attribute -> modifiers 2024-12-06 00:01:51 +01:00
3e70c30b89 fucking implicits and base weight fix 2024-12-05 23:47:36 +01:00
93fa1a89f2 enum -> string 2024-12-05 21:42:09 +01:00
1e89a8f3e7 remove todo 2024-12-05 21:42:09 +01:00
ab5fbdfec2 shift + click/ctrl to go to prev category 2024-12-05 21:42:09 +01:00
15ebedce84 show greater/legendaries only if they can actually drop (they don't have noLegendary tag) 2024-12-05 21:42:09 +01:00
19380da36f help container works correctly on most sizes now (left text might overflow if playing on blind gui scales non-standard aspect ratios) 2024-12-05 21:42:09 +01:00
5bfc6c83ba blacklist chance display for crafted modifiers 2024-12-05 21:42:09 +01:00
e758d2b787 help 2024-12-05 21:42:09 +01:00
ef0bfec587 remove dupe protection 2024-12-05 21:42:09 +01:00
64b42515df show simple components instead of ranged ones if cloud/ability min and max lvl matches 2024-12-05 21:42:09 +01:00
606a54e3af version bump to 1.3 2024-12-05 21:42:09 +01:00
bf09fb18d0 clouds 2024-12-05 21:42:09 +01:00
b90d503174 hide groups with no rollable modifiers 2024-12-05 21:42:09 +01:00
78c1ab279d fix effect avoidance 2024-12-05 21:42:09 +01:00
d2969663bc auto qol hunters button conflict resolution 2024-12-05 21:42:09 +01:00
1ef461ab37 support greater modifier category 2024-12-05 21:42:09 +01:00
602d181e58 fix few oversights 2024-12-05 21:42:09 +01:00
addd286756 cleanup 2024-12-05 21:42:09 +01:00
7a04012234 pipeline 2024-12-05 21:42:09 +01:00
87f94725dd U16 2024-11-21 21:21:20 +01:00
9f7c3df47e
Create README.md 2024-09-25 16:08:13 +02:00
30 changed files with 2195 additions and 697 deletions

70
.github/workflows/gradle.yml vendored Normal file
View file

@ -0,0 +1,70 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Java CI with Gradle
on:
push
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
- name: Build with Gradle Wrapper
run: ./gradlew build
# NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).
# If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.
#
# - name: Setup Gradle
# uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
# with:
# gradle-version: '8.9'
#
# - name: Build with Gradle 8.9
# run: gradle build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: artifacts
path: build/libs/*.jar
dependency-submission:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.
# See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

23
README.md Normal file
View file

@ -0,0 +1,23 @@
# VHatCanIRoll
Shows you all possible modifiers you can roll/craft on vault gear at any level.
In-game version of the [gear page](https://vault-hunters.netlify.app/gear), you don't have to leave the game now to see what you can modifiers you can get.
Triangles with the same color indicate modifier exclusivity (you can't roll 2 modifiers with the same color).
You can open the screen by pressing the "Gear Modifiers" button in the Vault Screen (right below the quest button) or by pressing dedicated keybind (unbound by default).
You can disable or move the button in the config.
Supports [Wold's vaults](https://www.curseforge.com/minecraft/modpacks/wolds-vaults)
Client side only
Keyboard navigation:
| KEY | ACTION |
| --- | --- |
| left arrow (h) | decrease lvl |
| right arrow (l) | increase lvl |
| up arrow (k) | scroll up |
| down arrow (j) | scroll down |
| tab | move to next gear piece |
| shift + tab | move to previous gear piece |
| alt | toggle legendary modifiers |
| ctrl + , | toggle compact "+lvl to abilities" modifier view |

View file

@ -173,8 +173,8 @@ dependencies {
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
//VH
implementation fg.deobf("curse.maven:vault-hunters-official-mod-458203:5631250")
// implementation fg.deobf("curse.maven:wolds-vaults-official-mod-958799:5622985")
implementation fg.deobf("curse.maven:vault-hunters-official-mod-458203:5925633")
//deps
implementation fg.deobf("curse.maven:curios-309927:4418032")
implementation fg.deobf("curse.maven:sophisticated-backpacks-vault-hunters-edition-696425:4287544")
@ -188,6 +188,8 @@ dependencies {
implementation fg.deobf("curse.maven:alexs-mobs-426558:3853078") // wold plushie effects
implementation fg.deobf("curse.maven:citadel-331936:3783096") // alex mobs dep
implementation fg.deobf("curse.maven:create-328085:4550986") // config
//faster loading
implementation fg.deobf("curse.maven:lazydfu-433518:3209972")
implementation fg.deobf("curse.maven:modernfix-790626:4565795")

View file

@ -38,7 +38,7 @@ mod_name=VHat Can I Roll?
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=GNU LGPL 3.0
# The mod version. See https://semver.org/
mod_version=1.1
mod_version=1.5
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html

0
gradlew vendored Normal file → Executable file
View file

View file

@ -1,44 +1,73 @@
package com.radimous.vhatcaniroll;
import iskallia.vault.config.gear.VaultGearTierConfig;
import net.minecraftforge.common.ForgeConfigSpec;
import java.util.List;
public class Config {
public static final ForgeConfigSpec SPEC;
public static final ForgeConfigSpec.BooleanValue VAULT_SCREEN_BUTTON;
public static final ForgeConfigSpec.BooleanValue ALLOW_DUPE;
public static final ForgeConfigSpec.IntValue BUTTON_X;
public static final ForgeConfigSpec.IntValue BUTTON_Y;
public static final ForgeConfigSpec.BooleanValue COMBINE_LVL_TO_ABILITIES;
public static final ForgeConfigSpec.IntValue MAX_LEVEL_OVERRIDE;
public static final ForgeConfigSpec.BooleanValue SHOW_ABILITY_ENHANCEMENTS;
public static final ForgeConfigSpec.BooleanValue SHOW_WEIGHT;
public static final ForgeConfigSpec.BooleanValue SHOW_CHANCE;
public static final ForgeConfigSpec.BooleanValue QOL_HUNTERS_CONFLICT_RESOLUTION;
public static final ForgeConfigSpec.BooleanValue SHOW_UNOBTAINABLE_CRAFTED;
public static final ForgeConfigSpec.BooleanValue DEBUG_UNIQUE_GEAR;
// string instead of enum, because forge would remove enum values that are not present in the enum
// (this could cause problems if mods are extending the enum - like wold's)
public static final ForgeConfigSpec.ConfigValue<List<String>> AFFIX_TAG_GROUP_CHANCE_BLACKLIST;
static {
ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
COMBINE_LVL_TO_ABILITIES = builder
.comment("combine +lvl to abilities into one row")
.define("combineAddedLvlToAbilities", false);
builder.push("BUTTON");
builder.push("BUTTON");
VAULT_SCREEN_BUTTON = builder
.comment("open VHat can I roll? from vault screen")
.define("vaultScreenButton", true);
BUTTON_X = builder
.comment("x position of the button")
.defineInRange("buttonPositionX", 5, Integer.MIN_VALUE, Integer.MAX_VALUE);
BUTTON_Y = builder
.comment("y position of the button")
.comment("y position of the button (109 default, 130 for QOL Hunters)")
.defineInRange("buttonPositionY", 109, Integer.MIN_VALUE, Integer.MAX_VALUE);
builder.pop();
builder.push("DEBUG");
ALLOW_DUPE = builder
.comment("allow duplicate modifiers")
.define("allowDupe", false);
QOL_HUNTERS_CONFLICT_RESOLUTION = builder
.comment("QOL Hunters conflict resolution (shouldn't be disabled unless it causes issues)")
.define("QOLHuntersConflictResolution", true);
AFFIX_TAG_GROUP_CHANCE_BLACKLIST = builder
.comment("vhcir won't show chance/weight for affixes in these groups")
.define("affixTagGroupBlacklist", List.of(VaultGearTierConfig.ModifierAffixTagGroup.CRAFTED_PREFIX.name(), VaultGearTierConfig.ModifierAffixTagGroup.CRAFTED_SUFFIX.name()));
MAX_LEVEL_OVERRIDE = builder
.comment("override max level")
.defineInRange("maxLevelOverride", -1, -1, Integer.MAX_VALUE);
DEBUG_UNIQUE_GEAR = builder
.comment("debug unique gear")
.define("debugUniqueGear", false);
builder.pop();
SHOW_ABILITY_ENHANCEMENTS = builder
.comment("show ability enhancements")
.define("showAbilityEnhancements", false);
SHOW_WEIGHT = builder
.comment("show weight")
.define("showWeight", false);
SHOW_CHANCE = builder
.comment("show chance")
.define("showChance", true);
SHOW_UNOBTAINABLE_CRAFTED = builder
.comment("show unobtainable crafted modifiers (above current lvl)")
.define("showUnobtainableCrafted", false);
SPEC = builder.build();
}
}

View file

@ -1,303 +0,0 @@
package com.radimous.vhatcaniroll;
import com.mojang.blaze3d.platform.InputConstants;
import iskallia.vault.client.gui.framework.ScreenRenderers;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.FakeItemSlotElement;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.NineSliceButtonElement;
import iskallia.vault.client.gui.framework.element.NineSliceElement;
import iskallia.vault.client.gui.framework.element.TabElement;
import iskallia.vault.client.gui.framework.element.TextInputElement;
import iskallia.vault.client.gui.framework.element.TextureAtlasElement;
import iskallia.vault.client.gui.framework.element.spi.ILayoutStrategy;
import iskallia.vault.client.gui.framework.render.ScreenTooltipRenderer;
import iskallia.vault.client.gui.framework.screen.AbstractElementScreen;
import iskallia.vault.client.gui.framework.screen.layout.ScreenLayout;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.IPosition;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import iskallia.vault.client.gui.overlay.VaultBarOverlay;
import iskallia.vault.init.ModConfigs;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.item.ItemStack;
import java.util.ArrayList;
import java.util.List;
public class GearModifierScreen extends AbstractElementScreen {
//TODO: code cleanup - variable naming, magic numbers, some logic reordering etc
private ModifierListContainer modifierList;
private final TextInputElement<?> lvlInput;
private boolean legendary;
private LabelElement<?> legendaryLabel;
private int currIndex = 0;
private final List<TabElement<?>> tabs = new ArrayList<>();
public GearModifierScreen() {
super(new TranslatableComponent("vhatcaniroll.screen.title"), ScreenRenderers.getBuffered(), ScreenTooltipRenderer::create);
// make screen size 95% of the window height and width that looks good
this.setGuiSize(Spatials.size(340, 300).height((int) (
(Minecraft.getInstance().getWindow().getHeight() / Minecraft.getInstance().getWindow().getGuiScale()) *
0.95)));
// outer background
NineSliceElement<?> background = new NineSliceElement<>(
Spatials.positionXY(0, 30).size(this.getGuiSpatial().width(), this.getGuiSpatial().height() - 30),
ScreenTextures.DEFAULT_WINDOW_BACKGROUND
).layout(this.translateWorldSpatial());
// window title
LabelElement<?> windowName = new LabelElement<>(
Spatials.positionXY(7, 38).size(this.getGuiSpatial().width() / 2 - 7, 20),
this.title.copy().withStyle(ChatFormatting.BLACK),
LabelTextStyle.defaultStyle()
).layout(this.translateWorldSpatial());
this.addElement(background);
this.addElement(windowName);
createTabs();
this.lvlInput = this.addElement(createLvlInput());
createLvlButtons();
createLegendaryButton();
// inner black window
this.modifierList = new ModifierListContainer(
Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57),
this)
.layout(this.translateWorldSpatial());
this.addElement(this.modifierList);
}
public ItemStack getCurrGear() {
return VHatCanIRoll.getVaultGearItems().get(currIndex);
}
public int getCurrLvl() {
try {
return Integer.parseInt(this.lvlInput.getInput());
} catch (NumberFormatException e) {
return 0;
}
}
private void increaseLvl() {
this.lvlInput.setInput(String.valueOf(this.getCurrLvl() + 1));
}
private void decreaseLvl() {
this.lvlInput.setInput(String.valueOf(this.getCurrLvl() - 1));
}
private void updateModifierList(boolean keepScroll) {
var oldScroll = this.modifierList.getScroll();
this.removeElement(this.modifierList);
this.modifierList = new ModifierListContainer(
Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57),
this)
.layout(this.translateWorldSpatial());
if (keepScroll) {
this.modifierList.setScroll(oldScroll);
}
this.addElement(this.modifierList);
ScreenLayout.requestLayout();
}
private ILayoutStrategy translateWorldSpatial() {
return (screen, gui, parent, world) -> world.translateXY(this.getGuiSpatial());
}
private IPosition getPos(int tabIndex, boolean selected) {
if (tabIndex < 11){
return Spatials.positionXY(5 + tabIndex * 30, 2 + (selected ? 0 : 4));
}
return Spatials.positionXY(337 + (selected? 0:3), 35 + (tabIndex-11) * 30);
}
private ISpatial getItemPos(int tabIndex) {
if (tabIndex < 11){
return Spatials.positionXY(10 + tabIndex * 30, 11);
}
return Spatials.positionXY(342, 40 + (tabIndex-11) * 30);
}
// actual tab
private TabElement<?> getTabElement(int tabIndex, boolean selected) {
if (tabIndex < 11){
return new TabElement<>(getPos(tabIndex, selected),
new TextureAtlasElement<>(
selected ? ScreenTextures.TAB_BACKGROUND_TOP_SELECTED : ScreenTextures.TAB_BACKGROUND_TOP),
new TextureAtlasElement<>(ScreenTextures.EMPTY), () -> switchTab(tabIndex))
.layout(this.translateWorldSpatial());
}
return new TabElement<>(getPos(tabIndex, selected),
new TextureAtlasElement<>(
selected ? ScreenTextures.TAB_BACKGROUND_RIGHT_SELECTED : ScreenTextures.TAB_BACKGROUND_RIGHT),
new TextureAtlasElement<>(ScreenTextures.EMPTY), () -> switchTab(tabIndex))
.layout(this.translateWorldSpatial());
}
private void switchTab(int tabIndex) {
for (int i = 0; i < VHatCanIRoll.getVaultGearItems().size(); i++) {
this.removeElement(tabs.get(i));
TabElement<?> tab = getTabElement(i, i == tabIndex);
tabs.set(i, tab);
this.addElement(tab);
}
this.currIndex = tabIndex;
updateModifierList(false);
}
private TextInputElement<?> createLvlInput() {
ScrollableTextInputElement inputElement = this.addElement(
new ScrollableTextInputElement(Spatials.positionXY(this.getGuiSpatial().width() - 54 - 13, 36).size(26, 12),
Minecraft.getInstance().font)
.layout((screen, gui, parent, world) -> world.translateXY(gui))
);
inputElement.adjustEditBox(editBox -> {
editBox.setFilter(this::isValidLevel);
editBox.setMaxLength(3);
editBox.setValue(String.valueOf(VaultBarOverlay.vaultLevel));
});
inputElement.onTextChanged(s -> updateModifierList(true));
return inputElement;
}
private boolean isValidLevel(String input){
// reminds me of original vault filters
if (input.isEmpty()) {
return true;
}
int lvl;
try {
lvl = Integer.parseInt(input);
} catch (NumberFormatException numberformatexception) {
return false;
}
if (lvl <= ModConfigs.LEVELS_META.getMaxLevel() && lvl >= 0) {
ScreenLayout.requestLayout();
return true;
}
if (lvl <= Config.MAX_LEVEL_OVERRIDE.get() && lvl >= 0) {
ScreenLayout.requestLayout();
return true;
}
return false;
}
private void createLvlButtons() {
LabelElement<?> minusLabel =
new LabelElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 68 - 13, 38), new TextComponent("-"),
LabelTextStyle.border4(ChatFormatting.BLACK))
.layout(this.translateWorldSpatial());
LabelElement<?> plusLabel =
new LabelElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 21 - 13, 38), new TextComponent("+"),
LabelTextStyle.border4(ChatFormatting.BLACK))
.layout(this.translateWorldSpatial());
NineSliceButtonElement<?> btnMinus =
new NineSliceButtonElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 72 - 13, 35).size(15, 14),
ScreenTextures.BUTTON_EMPTY_TEXTURES, this::decreaseLvl).layout(this.translateWorldSpatial());
NineSliceButtonElement<?> btnPlus =
new NineSliceButtonElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 25 - 13, 35).size(15, 14),
ScreenTextures.BUTTON_EMPTY_TEXTURES, this::increaseLvl).layout(this.translateWorldSpatial());
this.addElement(btnMinus);
this.addElement(minusLabel);
this.addElement(plusLabel);
this.addElement(btnPlus);
}
private void toggleLegend() {
this.legendary = !this.legendary;
updateLegendaryLabel();
updateModifierList(true);
}
private void updateLegendaryLabel() {
if (this.legendaryLabel != null) {
this.removeElement(this.legendaryLabel);
}
var formatting = this.legendary ? ChatFormatting.GOLD : ChatFormatting.WHITE;
this.legendaryLabel = new LabelElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 5 - 13, 38),
new TextComponent("").withStyle(formatting), LabelTextStyle.defaultStyle())
.layout(this.translateWorldSpatial());
this.addElement(legendaryLabel);
}
private void createLegendaryButton() {
updateLegendaryLabel();
NineSliceButtonElement<?> btnLegend =
new NineSliceButtonElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 8 - 13, 35).size(14, 14),
ScreenTextures.BUTTON_EMPTY_TEXTURES, this::toggleLegend).layout(this.translateWorldSpatial());
this.addElement(btnLegend);
}
private void createTabs() {
for (int i = 0; i < VHatCanIRoll.getVaultGearItems().size(); i++) {
addTab(i);
addFakeItemSlot(i);
}
}
private void addTab(int index) {
TabElement<?> tab = getTabElement(index, index == currIndex);
tabs.add(this.addElement(tab));
}
private void addFakeItemSlot(int index) {
ItemStack gearItem = VHatCanIRoll.getVaultGearItems().get(index);
this.addElement(
new FakeItemSlotElement<>(getItemPos(index), () -> gearItem, () -> false,
ScreenTextures.EMPTY, ScreenTextures.EMPTY).layout(
this.translateWorldSpatial()));
}
public boolean isLegendary() {
return legendary;
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
// left/right to increase/decrease lvl
if (keyCode == InputConstants.KEY_L || keyCode == InputConstants.KEY_RIGHT) {
increaseLvl();
}
if (keyCode == InputConstants.KEY_H || keyCode == InputConstants.KEY_LEFT) {
decreaseLvl();
}
// up/down to scroll up/down
if (keyCode == InputConstants.KEY_K || keyCode == InputConstants.KEY_UP) {
this.modifierList.onMouseScrolled(0, 0, 1);
}
if (keyCode == InputConstants.KEY_J || keyCode == InputConstants.KEY_DOWN) {
this.modifierList.onMouseScrolled(0, 0, -1);
}
// tab to next gear item
if (keyCode == InputConstants.KEY_TAB && !hasShiftDown()) {
switchTab((currIndex + 1) % VHatCanIRoll.getVaultGearItems().size());
}
// shift+tab to previous gear item
if (keyCode == InputConstants.KEY_TAB && hasShiftDown()) {
switchTab((currIndex - 1 + VHatCanIRoll.getVaultGearItems().size()) % VHatCanIRoll.getVaultGearItems().size());
}
// alt to toggle legendary
if (keyCode == InputConstants.KEY_LALT || keyCode == InputConstants.KEY_RALT) {
toggleLegend();
}
// ctrl + , to toggle compact +lvl to abilities
if (keyCode == InputConstants.KEY_COMMA && hasControlDown()) {
Config.COMBINE_LVL_TO_ABILITIES.set(!Config.COMBINE_LVL_TO_ABILITIES.get());
updateModifierList(true);
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
}

View file

@ -1,233 +0,0 @@
package com.radimous.vhatcaniroll;
import com.radimous.vhatcaniroll.mixin.EffectConfigAccessor;
import com.radimous.vhatcaniroll.mixin.VaultGearTierConfigAccessor;
import iskallia.vault.config.gear.VaultGearTierConfig;
import iskallia.vault.gear.attribute.VaultGearAttribute;
import iskallia.vault.gear.attribute.VaultGearAttributeRegistry;
import iskallia.vault.gear.attribute.ability.AbilityLevelAttribute;
import iskallia.vault.gear.attribute.ability.special.base.SpecialAbilityModification;
import iskallia.vault.gear.attribute.config.BooleanFlagGenerator;
import iskallia.vault.gear.attribute.config.ConfigurableAttributeGenerator;
import iskallia.vault.gear.attribute.custom.EffectGearAttribute;
import iskallia.vault.init.ModConfigs;
import iskallia.vault.util.TextComponentUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fml.LogicalSide;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class Helper {
private static final ChatFormatting[] COLORS =
new ChatFormatting[]{ChatFormatting.RED, ChatFormatting.GREEN, ChatFormatting.BLUE, ChatFormatting.YELLOW,
ChatFormatting.LIGHT_PURPLE, ChatFormatting.AQUA, ChatFormatting.WHITE};
public static List<Component> getModifierList(int lvl, VaultGearTierConfig cfg, boolean legendary) {
Map<VaultGearTierConfig.ModifierAffixTagGroup, VaultGearTierConfig.AttributeGroup> modifierGroup =
((VaultGearTierConfigAccessor) cfg).getModifierGroup();
ArrayList<Component> modList = new ArrayList<>();
for (VaultGearTierConfig.ModifierAffixTagGroup affixTagGroup : modifierGroup.keySet()) {
processAffixTagGroup(lvl, affixTagGroup, modifierGroup, modList, legendary);
}
return modList;
}
private static void processAffixTagGroup(int lvl, VaultGearTierConfig.ModifierAffixTagGroup affixTagGroup,
Map<VaultGearTierConfig.ModifierAffixTagGroup, VaultGearTierConfig.AttributeGroup> modifierGroup,
ArrayList<Component> modList, boolean legendary) {
if (affixTagGroup.equals(VaultGearTierConfig.ModifierAffixTagGroup.ABILITY_ENHANCEMENT)) {
return;
}
if (modifierGroup.get(affixTagGroup).isEmpty()) {
return;
}
modList.add(new TextComponent(affixTagGroup.toString().replace("_", " ")).withStyle(ChatFormatting.BOLD));
Map<String, Integer> groupCounts = countGroups(lvl, affixTagGroup, modifierGroup, legendary);
List<String> grList = new ArrayList<>();
for (VaultGearTierConfig.ModifierTierGroup modifierTierGroup : modifierGroup.get(affixTagGroup)) {
ArrayList<VaultGearTierConfig.ModifierTier<?>> mTierList;
if (legendary) {
mTierList = getLegendaryModifierTiers(lvl, modifierTierGroup);
} else {
mTierList = getModifierTiers(lvl, modifierTierGroup);
}
if (mTierList.isEmpty()) {
continue;
}
String modGr = modifierTierGroup.getModifierGroup();
Component newMod = getVal(
Objects.requireNonNull(VaultGearAttributeRegistry.getAttribute(modifierTierGroup.getAttribute())),
mTierList);
if (groupCounts.get(modGr) > 1 && !grList.contains(modGr)) {
grList.add(modGr);
}
int index = grList.indexOf(modGr); // index used to determine color
MutableComponent full;
if (index == -1) {
full = new TextComponent(" ");
} else {
full = new TextComponent("").withStyle(COLORS[index % COLORS.length]);
}
full.append(newMod);
if (Config.ALLOW_DUPE.get() || !(modList.get(modList.size() - 1).getString()).equals(full.getString())) { //dumb way to fix ability lvl+ duplication
modList.add(full);
}
}
modList.add(TextComponent.EMPTY);
}
private static Map<String, Integer> countGroups(int lvl, VaultGearTierConfig.ModifierAffixTagGroup affixTagGroup,
Map<VaultGearTierConfig.ModifierAffixTagGroup, VaultGearTierConfig.AttributeGroup> modifierGroup,
boolean legendary) {
Map<String, Integer> groupCounts = new HashMap<>();
for (VaultGearTierConfig.ModifierTierGroup modifierTierGroup : modifierGroup.get(affixTagGroup)) {
ArrayList<VaultGearTierConfig.ModifierTier<?>> mTierList;
if (legendary) {
mTierList = getLegendaryModifierTiers(lvl, modifierTierGroup);
} else {
mTierList = getModifierTiers(lvl, modifierTierGroup);
}
if (mTierList.isEmpty()) {
continue;
}
groupCounts.put(modifierTierGroup.getModifierGroup(),
groupCounts.getOrDefault(modifierTierGroup.getModifierGroup(), 0) + 1);
}
return groupCounts;
}
private static ArrayList<VaultGearTierConfig.ModifierTier<?>> getLegendaryModifierTiers(int lvl,
VaultGearTierConfig.ModifierTierGroup modifierTierGroup) {
var res = new ArrayList<VaultGearTierConfig.ModifierTier<?>>();
var highest = modifierTierGroup.getHighestForLevel(lvl);
if (highest == null) {
return res; // empty
}
int index = Math.min(highest.getModifierTier() + 2, modifierTierGroup.size() - 1);
var legendTier = modifierTierGroup.get(index);
if (legendTier == null || legendTier.getWeight() == 0){
return res; // empty
}
if (legendTier.getModifierConfiguration() instanceof BooleanFlagGenerator.BooleanFlag bf &&
!bf.get()) {
return res; // empty
}
res.add(legendTier);
return res; // only one
}
@NotNull private static ArrayList<VaultGearTierConfig.ModifierTier<?>> getModifierTiers(int lvl,
VaultGearTierConfig.ModifierTierGroup modifierTierGroup) {
return modifierTierGroup.getModifiersForLevel(lvl).stream()
.filter(x -> x.getWeight() != 0
&& !(x.getModifierConfiguration() instanceof BooleanFlagGenerator.BooleanFlag bf &&
!bf.get())) // bool with false :( looking at you, soulbound
.collect(Collectors.toCollection(ArrayList::new));
}
// TODO: wtf is this, fix variable names and make it readable
// I don't think proper generics are possible, VaultGearTierConfig#getModifiersForLevel returns List<ModifierTier<?>>
private static <T, C> Component getVal(VaultGearAttribute<T> atr,
ArrayList<VaultGearTierConfig.ModifierTier<?>> val) {
if (val.isEmpty()) {
return new TextComponent("ERR - EMPTY VAL");
}
ConfigurableAttributeGenerator<T, C> atrGenerator = (ConfigurableAttributeGenerator<T, C>) atr.getGenerator();
if (atrGenerator == null) {
return new TextComponent("ERR - NULL GENERATOR");
}
C minConfig = (C) val.get(0).getModifierConfiguration();
C maxConfig = (C) val.get(val.size() - 1).getModifierConfiguration();
MutableComponent res;
ResourceLocation atrRegName = atr.getRegistryName();
if (atrRegName == null) {
return new TextComponent("ERR - NULL REGISTRY NAME");
}
String atrName = atrRegName.toString();
var minConfigDisplay = atrGenerator.getConfigDisplay(atr.getReader(), minConfig);
var maxConfigDisplay = atrGenerator.getConfigDisplay(atr.getReader(), maxConfig);
if (val.size() > 1) {
// range
res = atrGenerator.getConfigRangeDisplay(atr.getReader(), minConfig, maxConfig);
if (res != null && minConfig instanceof AbilityLevelAttribute.Config minConfigAbility) {
return abilityLvlComponent(res, atr, minConfigAbility);
}
if (res != null && atrName.equals("the_vault:wendarr_affinity")) {
return res.append(" God Affinity").withStyle(atr.getReader().getColoredTextStyle());
}
if (atrName.equals("the_vault:effect_avoidance") && minConfigDisplay != null) {
// res -> "30% - 50%"
// single -> "30% Poison Avoidance"
// minRange -> "30%"
var single = minConfigDisplay.withStyle(atr.getReader().getColoredTextStyle());
var minRange = atrGenerator.getConfigRangeDisplay(atr.getReader(), minConfig, minConfig);
if (minRange != null && res != null) {
res.append(single.getString().replace(minRange.getString(), ""));
// res -> "30% - 50% Poison Avoidance"
}
}
if (minConfig instanceof EffectGearAttribute.Config minEffectConfig
&& maxConfig instanceof EffectGearAttribute.Config
&& maxConfigDisplay != null) {
var effectStr = ((EffectConfigAccessor)minEffectConfig).getAmplifier() + "-" +
maxConfigDisplay.getString();
return new TextComponent(effectStr).withStyle(atr.getReader().getColoredTextStyle());
}
if (res != null) {
return atr.getReader().formatConfigDisplay(LogicalSide.CLIENT, res);
}
}
if (minConfigDisplay != null) {
res = minConfigDisplay.withStyle(atr.getReader().getColoredTextStyle());
if (minConfig instanceof AbilityLevelAttribute.Config minConfigAbility) {
return abilityLvlComponent(res, atr, minConfigAbility);
}
if (atrName.equals("the_vault:wendarr_affinity")) {
return TextComponentUtils.replace(TextComponentUtils.createSourceStack(LogicalSide.CLIENT), res,
"Wendarr Affinity", new TextComponent("God Affinity"))
.withStyle(atr.getReader().getColoredTextStyle());
}
if (minConfig instanceof EffectGearAttribute.Config ) {
return minConfigDisplay;
}
return res;
}
return new TextComponent("ERR - NULL DISPLAY " + atrName);
}
private static Component abilityLvlComponent(MutableComponent res, VaultGearAttribute<?> atr,
AbilityLevelAttribute.Config minConfig) {
if (Config.COMBINE_LVL_TO_ABILITIES.get()) {
return res.append(" added ability levels").withStyle(atr.getReader().getColoredTextStyle());
} else {
var abComp = new TextComponent("+").withStyle(atr.getReader().getColoredTextStyle());
var optSkill = ModConfigs.ABILITIES.getAbilityById(minConfig.getAbilityKey());
if (optSkill.isEmpty()) {
return res.append(" added ability levels").withStyle(atr.getReader().getColoredTextStyle());
}
var abName = optSkill.get().getName();
abComp.append(res);
abComp.append(" to level of ");
abComp.append(new TextComponent(abName).withStyle(SpecialAbilityModification.getAbilityStyle()));
return abComp;
}
}
}

View file

@ -1,6 +1,8 @@
package com.radimous.vhatcaniroll;
import com.mojang.blaze3d.platform.InputConstants;
import com.radimous.vhatcaniroll.ui.GearModifierScreen;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;

View file

@ -1,54 +0,0 @@
package com.radimous.vhatcaniroll;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.VerticalScrollClipContainer;
import iskallia.vault.client.gui.framework.spatial.Padding;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import iskallia.vault.config.gear.VaultGearTierConfig;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.TextComponent;
import java.util.Optional;
public class ModifierListContainer extends VerticalScrollClipContainer<ModifierListContainer> {
public ModifierListContainer(ISpatial spatial, GearModifierScreen parent) {
super(spatial, Padding.ZERO, ScreenTextures.INSET_BLACK_BACKGROUND);
int i = 20;
int k = 9;
boolean legendary = parent.isLegendary();
Optional<VaultGearTierConfig> optCfg = VaultGearTierConfig.getConfig(parent.getCurrGear());
LabelElement<?> itemName = new LabelElement<>(
Spatials.positionXY(k, 5).width(this.innerWidth() - k).height(15), new TextComponent(
parent.getCurrGear().getItem().toString().toUpperCase() + " - LVL " + parent.getCurrLvl())
.withStyle(ChatFormatting.UNDERLINE).withStyle(legendary ? ChatFormatting.GOLD : ChatFormatting.WHITE), LabelTextStyle.defaultStyle()
);
this.addElement(itemName);
if (optCfg.isPresent()) {
VaultGearTierConfig cfg = optCfg.get();
for (var modifier : Helper.getModifierList(parent.getCurrLvl(), cfg, legendary)) {
LabelElement<?> labelelement = new LabelElement<>(
Spatials.positionXY(k, i).width(this.innerWidth() - k).height(15), modifier, LabelTextStyle.defaultStyle()
);
this.addElement(labelelement);
i += 10;
}
} else {
LabelElement<?> labelelement = new LabelElement<>(
Spatials.positionXY(k, i).width(this.innerWidth() - k).height(15), new TextComponent(
parent.getCurrGear().getItem() + " not found"), LabelTextStyle.defaultStyle()
);
this.addElement(labelelement);
}
}
public float getScroll() {
return this.verticalScrollBarElement.getValue();
}
public void setScroll(float scroll) {
this.verticalScrollBarElement.setValue(scroll);
}
}

View file

@ -0,0 +1,41 @@
package com.radimous.vhatcaniroll;
import net.minecraftforge.common.ForgeConfigSpec;
public class QOLHuntersCompat {
private static boolean qolHuntersLoaded = true;
private static ForgeConfigSpec.BooleanValue qolConfigButton = null;
public static void resolveQOLHuntersButtonConflict(){
if(!Config.QOL_HUNTERS_CONFLICT_RESOLUTION.get()){
// just for debugging or if it blows up
return;
}
if (!qolHuntersLoaded) {
if (Config.BUTTON_Y.get() == 130) {
Config.BUTTON_Y.set(109);
}
return;
}
if (qolConfigButton == null) {
// use reflection to avoid a million dependencies
try {
Class<?> cl = Class.forName("io.iridium.qolhunters.config.QOLHuntersClientConfigs");
Object qolButton = cl.getField("SHOW_CONFIG_BUTTON").get(null);
qolConfigButton = (ForgeConfigSpec.BooleanValue) qolButton;
} catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) {
qolHuntersLoaded = false;
}
}
// if qol button, move our button down
if (qolConfigButton != null && qolConfigButton.get() && Config.BUTTON_Y.get() == 109) {
Config.BUTTON_Y.set(130);
}
// if no qol button, move our button up
if ((qolConfigButton == null || !qolConfigButton.get()) && Config.BUTTON_Y.get() == 130) {
Config.BUTTON_Y.set(109);
}
}
}

View file

@ -1,30 +0,0 @@
package com.radimous.vhatcaniroll;
import iskallia.vault.client.gui.framework.element.TextInputElement;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import net.minecraft.client.gui.Font;
public class ScrollableTextInputElement extends TextInputElement<ScrollableTextInputElement> {
public ScrollableTextInputElement(ISpatial spatial, Font font) {
super(spatial, font);
}
@Override
public boolean onMouseScrolled(double mouseX, double mouseY, double delta) {
if (this.isMouseOver(mouseX, mouseY)) {
int val = parseInt(this.getInput());
val += delta > 0 ? 1 : -1;
this.setInput(String.valueOf(val));
return true;
}
return super.onMouseScrolled(mouseX, mouseY, delta);
}
private int parseInt(String val) {
try {
return Integer.parseInt(val);
} catch (NumberFormatException e) {
return 0;
}
}
}

View file

@ -1,73 +1,16 @@
package com.radimous.vhatcaniroll;
import iskallia.vault.init.ModItems;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Mod("vhatcaniroll")
public class VHatCanIRoll {
public static final String MODID = "vhatcaniroll";
private static final List<ItemStack> GEAR_ITEMS = new ArrayList<>();
public VHatCanIRoll() {
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.SPEC);
MinecraftForge.EVENT_BUS.register(this);
}
public static List<ItemStack> getVaultGearItems() {
if (GEAR_ITEMS.isEmpty()){
GEAR_ITEMS.addAll(List.of(
new ItemStack(ModItems.SWORD),
new ItemStack(ModItems.AXE),
new ItemStack(ModItems.HELMET),
new ItemStack(ModItems.CHESTPLATE),
new ItemStack(ModItems.LEGGINGS),
new ItemStack(ModItems.BOOTS),
new ItemStack(ModItems.FOCUS),
new ItemStack(ModItems.SHIELD),
new ItemStack(ModItems.WAND),
new ItemStack(ModItems.MAGNET),
new ItemStack(ModItems.JEWEL)
)
);
GEAR_ITEMS.addAll(getWoldGearItems());
}
return Collections.unmodifiableList(GEAR_ITEMS);
}
public static List<ItemStack> getWoldGearItems() {
List<ItemStack> woldItems = new ArrayList<>();
List<String> woldItemFields = Arrays.asList(
"BATTLESTAFF",
"TRIDENT",
"PLUSHIE",
"LOOT_SACK"
);
try{
Class<?> wold = Class.forName("xyz.iwolfking.woldsvaults.init.ModItems");
for (String name : woldItemFields) {
try {
Item item = (Item) wold.getField(name).get(null);
woldItems.add(new ItemStack(item));
} catch (IllegalArgumentException | SecurityException | NoSuchFieldException |
IllegalAccessException ignored) {
// no-op
}
}
} catch (ClassNotFoundException ignored) {
// no-op
}
return woldItems;
}
}

View file

@ -0,0 +1,87 @@
package com.radimous.vhatcaniroll.logic;
import iskallia.vault.gear.VaultGearState;
import iskallia.vault.gear.data.VaultGearData;
import iskallia.vault.init.ModGearAttributes;
import iskallia.vault.init.ModItems;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Items {
private static final List<ItemStack> GEAR_ITEMS = new ArrayList<>();
public static List<ItemStack> getVaultGearItems() {
if (GEAR_ITEMS.isEmpty()){
GEAR_ITEMS.addAll(getVHGearItems());
GEAR_ITEMS.addAll(getWoldGearItems());
}
return Collections.unmodifiableList(GEAR_ITEMS);
}
public static List<ItemStack> getVHGearItems() {
return List.of(
withTransmog(new ItemStack(ModItems.SWORD), new ResourceLocation("the_vault:gear/sword/sword_0")),
withTransmog(new ItemStack(ModItems.AXE), new ResourceLocation("the_vault:gear/axe/axe_0")),
withTransmog(new ItemStack(ModItems.HELMET), new ResourceLocation("the_vault:gear/armor/gladiator_dark/helmet")),
withTransmog(new ItemStack(ModItems.CHESTPLATE), new ResourceLocation("the_vault:gear/armor/magmatic/chestplate")),
withTransmog(new ItemStack(ModItems.LEGGINGS), new ResourceLocation("the_vault:gear/armor/reinforced_platemail_dark/leggings")),
withTransmog(new ItemStack(ModItems.BOOTS), new ResourceLocation("the_vault:gear/armor/gladiator_dark/boots")),
withTransmog(new ItemStack(ModItems.FOCUS), new ResourceLocation("the_vault:gear/focus/tatteredtome")),
withTransmog(new ItemStack(ModItems.SHIELD), new ResourceLocation("the_vault:gear/shield/gold_plated")),
withTransmog(new ItemStack(ModItems.WAND), new ResourceLocation("the_vault:gear/wand/lunar")),
withTransmog(new ItemStack(ModItems.MAGNET), new ResourceLocation("the_vault:magnets/magnet_1")),
withTransmog(new ItemStack(ModItems.JEWEL), new ResourceLocation("the_vault:gear/jewel/sword_0"))
);
}
public static List<ItemStack> getWoldGearItems() {
List<ItemStack> woldItems = new ArrayList<>();
List<Pair<String, String>> woldItemFields = Arrays.asList(
Pair.of("BATTLESTAFF", "the_vault:gear/battlestaff/battlestaff_redstone"),
Pair.of("TRIDENT", "the_vault:gear/trident/orange"),
Pair.of("PLUSHIE", "the_vault:gear/plushie/hrry"),
Pair.of("LOOT_SACK", "the_vault:gear/loot_sack/bundle"),
Pair.of("RANG", "the_vault:gear/rang/wooden")
);
try{
Class<?> woldItemClass = Class.forName("xyz.iwolfking.woldsvaults.init.ModItems");
for (Pair<String, String> woldFieldTransmogs : woldItemFields) {
try {
String woldFieldName = woldFieldTransmogs.getLeft();
Item item = (Item) woldItemClass.getField(woldFieldName).get(null);
woldItems.add(withTransmog(new ItemStack(item), new ResourceLocation(woldFieldTransmogs.getRight())));
} catch (IllegalArgumentException | SecurityException | NoSuchFieldException |
IllegalAccessException ignored) {
// no-op
}
}
} catch (ClassNotFoundException ignored) {
// no-op
}
return woldItems;
}
/**
* Creates copy of the given stack with the transmog applied
* @param stack the stack to apply the transmog to
* @param transmog the transmog to apply
* @return the stack with the transmog applied
*/
public static ItemStack withTransmog(ItemStack stack, ResourceLocation transmog){
ItemStack displayStack = stack.copy();
VaultGearData gearData = VaultGearData.read(displayStack);
gearData.setState(VaultGearState.IDENTIFIED);
gearData.createOrReplaceAttributeValue(ModGearAttributes.GEAR_MODEL, transmog);
gearData.write(displayStack);
return displayStack;
}
}

View file

@ -0,0 +1,46 @@
package com.radimous.vhatcaniroll.logic;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextColor;
public enum ModifierCategory {
NORMAL,
GREATER,
LEGENDARY;
public String getSymbol() {
return "";
// greater is not full width :(
// return switch (this) {
// case NORMAL -> "";
// case GREATER -> "";
// case LEGENDARY -> "";
// };
}
public Style getStyle() {
return switch (this) {
case NORMAL -> Style.EMPTY.withColor(ChatFormatting.WHITE);
case GREATER -> Style.EMPTY.withColor(TextColor.fromRgb(5886486));
case LEGENDARY -> Style.EMPTY.withColor(ChatFormatting.GOLD);
};
}
public int getTierIncrease() {
return switch (this) {
case NORMAL -> 0;
case GREATER -> 1;
case LEGENDARY -> 2;
};
}
public ModifierCategory next() {
return values()[(this.ordinal() + 1) % values().length];
}
public ModifierCategory previous() {
return values()[(this.ordinal() + values().length - 1) % values().length];
}
}

View file

@ -0,0 +1,554 @@
package com.radimous.vhatcaniroll.logic;
import com.radimous.vhatcaniroll.Config;
import com.radimous.vhatcaniroll.mixin.AbilityFloatValueAttributeReaderInvoker;
import com.radimous.vhatcaniroll.mixin.EffectConfigAccessor;
import com.radimous.vhatcaniroll.mixin.VaultGearTierConfigAccessor;
import iskallia.vault.config.UniqueGearConfig;
import iskallia.vault.config.gear.VaultGearTierConfig;
import iskallia.vault.gear.attribute.VaultGearAttribute;
import iskallia.vault.gear.attribute.VaultGearAttributeRegistry;
import iskallia.vault.gear.attribute.VaultGearModifier;
import iskallia.vault.gear.attribute.ability.AbilityAreaOfEffectPercentAttribute;
import iskallia.vault.gear.attribute.ability.AbilityLevelAttribute;
import iskallia.vault.gear.attribute.ability.special.EntropyPoisonModification;
import iskallia.vault.gear.attribute.ability.special.FrostNovaVulnerabilityModification;
import iskallia.vault.gear.attribute.ability.special.base.SpecialAbilityConfig;
import iskallia.vault.gear.attribute.ability.special.base.SpecialAbilityGearAttribute;
import iskallia.vault.gear.attribute.ability.special.base.SpecialAbilityModification;
import iskallia.vault.gear.attribute.ability.special.base.template.FloatRangeModification;
import iskallia.vault.gear.attribute.ability.special.base.template.IntRangeModification;
import iskallia.vault.gear.attribute.ability.special.base.template.config.FloatRangeConfig;
import iskallia.vault.gear.attribute.ability.special.base.template.config.IntRangeConfig;
import iskallia.vault.gear.attribute.ability.special.base.template.value.FloatValue;
import iskallia.vault.gear.attribute.ability.special.base.template.value.IntValue;
import iskallia.vault.gear.attribute.config.BooleanFlagGenerator;
import iskallia.vault.gear.attribute.config.ConfigurableAttributeGenerator;
import iskallia.vault.gear.attribute.custom.effect.EffectGearAttribute;
import iskallia.vault.gear.attribute.custom.loot.LootTriggerAttribute;
import iskallia.vault.gear.attribute.custom.loot.ManaPerLootAttribute;
import iskallia.vault.gear.reader.VaultGearModifierReader;
import iskallia.vault.init.ModConfigs;
import iskallia.vault.skill.ability.component.AbilityLabelFormatters;
import iskallia.vault.skill.base.Skill;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextColor;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fml.LogicalSide;
import org.jetbrains.annotations.NotNull;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* This is responsible for all the logic of transforming vh config -> list of components needed for the UI
*/
public class Modifiers {
private static final ChatFormatting[] COLORS =
new ChatFormatting[]{ChatFormatting.RED, ChatFormatting.GREEN, ChatFormatting.BLUE, ChatFormatting.YELLOW,
ChatFormatting.LIGHT_PURPLE, ChatFormatting.AQUA, ChatFormatting.WHITE};
private static final Pattern CLOUD_PATTERN = Pattern.compile("^(?<effect>.*?) ?(?<lvl>I|II|III|IV|V|VI|VII|VIII|IX|X)? (?<suffix>Cloud.*)$");
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.##");
public static List<Component> getModifierList(int lvl, VaultGearTierConfig cfg, ModifierCategory modifierCategory) {
Map<VaultGearTierConfig.ModifierAffixTagGroup, VaultGearTierConfig.AttributeGroup> modifierGroup = ((VaultGearTierConfigAccessor) cfg).getModifierGroup();
ArrayList<Component> modList = new ArrayList<>();
for (VaultGearTierConfig.ModifierAffixTagGroup affixTagGroup : modifierGroup.keySet()) {
modList.addAll(getAffixGroupComponents(lvl, affixTagGroup, modifierGroup.get(affixTagGroup), modifierCategory));
}
return modList;
}
public static List<Component> getUniqueModifierList(int lvl, ModifierCategory modifierCategory, Map<UniqueGearConfig.AffixTargetType, List<ResourceLocation>> modifierIdentifiers) {
ArrayList<Component> modList = new ArrayList<>();
for (Map.Entry<UniqueGearConfig.AffixTargetType, List<ResourceLocation>> modifierIdentifier : modifierIdentifiers.entrySet()) {
modList.addAll(getUniqueAffixComponents(lvl, modifierIdentifier, modifierCategory));
}
return modList;
}
public static List<Component> getAffixGroupComponents(int lvl, VaultGearTierConfig.ModifierAffixTagGroup affixTagGroup,
VaultGearTierConfig.AttributeGroup modifierGroups,
ModifierCategory modifierCategory) {
ArrayList<Component> componentList = new ArrayList<>();
if (!Config.SHOW_ABILITY_ENHANCEMENTS.get() && affixTagGroup.equals(VaultGearTierConfig.ModifierAffixTagGroup.ABILITY_ENHANCEMENT)) {
return componentList;
}
Map<String, Integer> groupCounts = countGroups(lvl, modifierGroups, modifierCategory);
AtomicBoolean noWeightAttr = new AtomicBoolean(false);
int totalWeight = modifierGroups.stream()
.mapToInt(modTierGroup -> getModifierTiers(lvl, modTierGroup, modifierCategory).stream().mapToInt(
tier -> {
if ((affixTagGroup == VaultGearTierConfig.ModifierAffixTagGroup.IMPLICIT
|| affixTagGroup == VaultGearTierConfig.ModifierAffixTagGroup.BASE_ATTRIBUTES) && groupCounts.get(modTierGroup.getModifierGroup()) == 1) {
noWeightAttr.set(true);
return 0;
}
return tier.getWeight();
}
)
.sum())
.sum();
if (totalWeight == 0 && !noWeightAttr.get()) {
return componentList;
}
componentList.add(new TextComponent(affixTagGroup.toString().replace("_", " ")).withStyle(ChatFormatting.BOLD));
if (Config.SHOW_WEIGHT.get() && shouldShowWeight(modifierCategory, affixTagGroup) && totalWeight > 0) {
componentList.add(new TextComponent("Total Weight: " + totalWeight).withStyle(ChatFormatting.BOLD));
}
Map<String, List<Component>> groupedModifiers = new HashMap<>();
for (VaultGearTierConfig.ModifierTierGroup modifierTierGroup :modifierGroups) {
ArrayList<VaultGearTierConfig.ModifierTier<?>> mTierList;
mTierList = getModifierTiers(lvl, modifierTierGroup, modifierCategory);
if (mTierList.isEmpty()) {
continue;
}
String modGr = modifierTierGroup.getModifierGroup();
MutableComponent modComp = getModifierComponent(VaultGearAttributeRegistry.getAttribute(modifierTierGroup.getAttribute()),mTierList);
if (!(
(affixTagGroup == VaultGearTierConfig.ModifierAffixTagGroup.BASE_ATTRIBUTES
|| affixTagGroup == VaultGearTierConfig.ModifierAffixTagGroup.IMPLICIT
)
&& groupCounts.get(modGr) == 1)){
int weight = modTierListWeight(mTierList);
if (Config.SHOW_WEIGHT.get() && shouldShowWeight(modifierCategory, affixTagGroup)) {
modComp.append(new TextComponent(" w"+weight).withStyle(ChatFormatting.GRAY));
}
if (Config.SHOW_CHANCE.get() && shouldShowWeight(modifierCategory, affixTagGroup) && totalWeight > 0) {
modComp.append(new TextComponent(String.format(" %.2f%%", ((double) weight * 100 / totalWeight))).withStyle(ChatFormatting.GRAY));
}
}
if (groupCounts.get(modGr) > 1) {
groupedModifiers.computeIfAbsent(modGr, k -> new ArrayList<>()).add(modComp);
continue;
}
MutableComponent full = new TextComponent(" ");
full.append(modComp);
componentList.add(full);
}
// more than 7 groups is a bit crazy, but just in case
boolean useNums = groupedModifiers.size() > COLORS.length;
int i = 0;
for (List<Component> modGr: groupedModifiers.values()) {
for (Component mod: modGr) {
MutableComponent full = new TextComponent(useNums ? i + " " : "").withStyle(COLORS[i % COLORS.length]);
full.append(mod);
componentList.add(full);
}
i++;
}
componentList.add(TextComponent.EMPTY);
return componentList;
}
/**
* Same as getAffixGroupComponents, but for unique gear without chances and groups
*/
public static List<Component> getUniqueAffixComponents(int lvl,Map.Entry<UniqueGearConfig.AffixTargetType, List<ResourceLocation>> modifierIdentifier, ModifierCategory modifierCategory) {
ArrayList<Component> componentList = new ArrayList<>();
if (modifierIdentifier.getValue().isEmpty()) {
return componentList; // no affix for this type
}
UniqueGearConfig.AffixTargetType affixTagGroup = modifierIdentifier.getKey();
componentList.add(new TextComponent(affixTagGroup.toString().replace("_", " ")).withStyle(ChatFormatting.BOLD));
for (ResourceLocation modifier : modifierIdentifier.getValue()) {
VaultGearTierConfig.ModifierTierGroup modifierTierGroup = ModConfigs.VAULT_GEAR_CONFIG.get(VaultGearTierConfig.UNIQUE_ITEM).getTierGroup(modifier);
if (modifierTierGroup == null) {
continue;
}
ArrayList<VaultGearTierConfig.ModifierTier<?>> mTierList;
mTierList = getModifierTiers(lvl, modifierTierGroup, modifierCategory);
if (mTierList.isEmpty()) {
continue;
}
MutableComponent modComp = getModifierComponent(VaultGearAttributeRegistry.getAttribute(modifierTierGroup.getAttribute()),mTierList);
MutableComponent full = new TextComponent(" ");
full.append(modComp);
componentList.add(full);
}
if (componentList.size() == 1) { // only header
return new ArrayList<>(); // no affixes for this type (all tiers are unobtainable)
}
componentList.add(TextComponent.EMPTY);
return componentList;
}
private static Map<String, Integer> countGroups(int lvl, VaultGearTierConfig.AttributeGroup modifierTierGroups, ModifierCategory modifierCategory) {
Map<String, Integer> groupCounts = new HashMap<>();
for (VaultGearTierConfig.ModifierTierGroup modifierTierGroup : modifierTierGroups) {
ArrayList<VaultGearTierConfig.ModifierTier<?>> mTierList;
mTierList = getModifierTiers(lvl, modifierTierGroup, modifierCategory);
if (mTierList.isEmpty()) {
continue;
}
groupCounts.put(modifierTierGroup.getModifierGroup(),
groupCounts.getOrDefault(modifierTierGroup.getModifierGroup(), 0) + 1);
}
return groupCounts;
}
public static ArrayList<VaultGearTierConfig.ModifierTier<?>> getModifierTiers(int lvl, VaultGearTierConfig.ModifierTierGroup modifierTierGroup, ModifierCategory modifierCategory) {
if (modifierCategory == ModifierCategory.NORMAL) {
return getNormalModifierTiers(lvl, modifierTierGroup);
}
ArrayList<VaultGearTierConfig.ModifierTier<?>> res = new ArrayList<>();
VaultGearTierConfig.ModifierTier<?> highest = modifierTierGroup.getHighestForLevel(lvl);
if (highest == null) {
return res; // empty
}
if (modifierTierGroup.getTags().contains("noLegendary")){
return res; // empty
}
int index = Math.min(highest.getModifierTier() + modifierCategory.getTierIncrease(), modifierTierGroup.size() - 1);
VaultGearTierConfig.ModifierTier<?> legendTier = modifierTierGroup.get(index);
if (legendTier == null || legendTier.getWeight() == 0){
return res; // empty
}
if (legendTier.getModifierConfiguration() instanceof BooleanFlagGenerator.BooleanFlag bf &&
!bf.get()) {
return res; // empty
}
res.add(legendTier);
return res; // only one
}
@NotNull private static ArrayList<VaultGearTierConfig.ModifierTier<?>> getNormalModifierTiers(int lvl,
VaultGearTierConfig.ModifierTierGroup modifierTierGroup) {
return modifierTierGroup.getModifiersForLevel(lvl).stream()
.filter(x -> x.getWeight() != 0
&& !(x.getModifierConfiguration() instanceof BooleanFlagGenerator.BooleanFlag bf &&
!bf.get())) // bool with false :( looking at you, soulbound
.collect(Collectors.toCollection(ArrayList::new));
}
@SuppressWarnings("unchecked") // I don't think proper generics are possible, VaultGearTierConfig#getModifiersForLevel returns List<ModifierTier<?>>
public static <T, C> MutableComponent getModifierComponent(VaultGearAttribute<T> atr,
ArrayList<VaultGearTierConfig.ModifierTier<?>> modifierTiers) {
if (modifierTiers.isEmpty()) {
return new TextComponent("ERR - EMPTY MODIFIER TIERS");
}
if (atr == null) {
return new TextComponent("ERR - NULL ATTRIBUTE");
}
ConfigurableAttributeGenerator<T, C> atrGenerator = (ConfigurableAttributeGenerator<T, C>) atr.getGenerator();
if (atrGenerator == null) {
return new TextComponent("ERR - NULL ATTRIBUTE GENERATOR");
}
C minConfig = (C) modifierTiers.get(0).getModifierConfiguration();
C maxConfig = (C) modifierTiers.get(modifierTiers.size() - 1).getModifierConfiguration();
ResourceLocation atrRegName = atr.getRegistryName();
if (atrRegName == null) {
return new TextComponent("ERR - NULL REGISTRY NAME");
}
String atrName = atrRegName.toString();
MutableComponent minConfigDisplay = atrGenerator.getConfigDisplay(atr.getReader(), minConfig);
if (minConfig instanceof SpecialAbilityGearAttribute.SpecialAbilityTierConfig<?,?,?> minConfigSpecial) {
return getSpecialAbilityAttributeComponent(modifierTiers, minConfigSpecial);
}
MutableComponent res = null;
if (modifierTiers.size() > 1) {
res = rangeComponent(atrName, atr, atrGenerator, minConfig, maxConfig);
if (res != null) {
return res;
}
}
if (minConfigDisplay != null) {
res = minConfigDisplay.withStyle(atr.getReader().getColoredTextStyle());
if (minConfig instanceof AbilityLevelAttribute.Config minConfigAbility) {
return abilityLvlComponent(res, atr, minConfigAbility);
}
if (minConfig instanceof EffectGearAttribute.Config ) {
return minConfigDisplay;
}
if (minConfig instanceof AbilityAreaOfEffectPercentAttribute.Config minConfigA) {
return getAbilityAoePercentageComponent(atr, minConfigA, minConfigA);
}
if (minConfig instanceof ManaPerLootAttribute.Config maxManaPerLootConfig) {
return getManaPerLootComponent(maxManaPerLootConfig, maxManaPerLootConfig);
}
return res;
}
return new TextComponent("ERR - NULL DISPLAY " + atrName);
}
@SuppressWarnings("unchecked") // this thing is insane
private static @NotNull MutableComponent getSpecialAbilityAttributeComponent(
ArrayList<VaultGearTierConfig.ModifierTier<?>> modifierTiers,
SpecialAbilityGearAttribute.SpecialAbilityTierConfig<?, ?, ?> minConfigSpecial) {
SpecialAbilityModification<? extends SpecialAbilityConfig<?>, ?> modification = minConfigSpecial.getModification();
if (modification instanceof IntRangeModification intRangeModification){
var minValue = intRangeModification.getMinimumValue(getIntTiers(modifierTiers));
var maxValue = intRangeModification.getMaximumValue(getIntTiers(modifierTiers));
String minValueDisplay = minValue.map(x -> String.valueOf(x.getValue().getValue())).orElse("NULL");
String maxValueDisplay = maxValue.map(x -> String.valueOf(x.getValue().getValue())).orElse("NULL");
MutableComponent minToMaxComponent = new TextComponent(minValueDisplay + "-" + maxValueDisplay).withStyle(ChatFormatting.UNDERLINE).withStyle(Style.EMPTY.withColor(TextColor.fromRgb(6082075)));
if (intRangeModification instanceof FrostNovaVulnerabilityModification) {
return (new TextComponent("Frost Nova also applies Level ").append(minToMaxComponent).append(" Vulnerability")).withStyle(Style.EMPTY.withColor(TextColor.fromRgb(14076214)));
}
if (intRangeModification instanceof EntropyPoisonModification) {
return new TextComponent("Entropic Bind also applies Poison ").withStyle(Style.EMPTY.withColor(TextColor.fromRgb(14076214))).append(minToMaxComponent);
}
if (intRangeModification.getKey().toString().equals("the_vault:glacial_blast_hypothermia")){
return (new TextComponent("Glacial Blast is ").append(minToMaxComponent).append("X more likely to shatter")).withStyle(Style.EMPTY.withColor(TextColor.fromRgb(14076214)));
}
}
if (modification instanceof FloatRangeModification floatRangeModification) {
var minValue = floatRangeModification.getMinimumValue(getFloatTiers(modifierTiers));
var maxValue = floatRangeModification.getMaximumValue(getFloatTiers(modifierTiers));
float minValueDisplay = minValue.map(x -> x.getValue().getValue()).orElse(0f);
float maxValueDisplay = maxValue.map(x -> x.getValue().getValue()).orElse(0f);
MutableComponent minToMaxComponent = new TextComponent(DECIMAL_FORMAT.format(minValueDisplay*100) + "%-" + DECIMAL_FORMAT.format(maxValueDisplay*100)+"%").withStyle(ChatFormatting.UNDERLINE).withStyle(Style.EMPTY.withColor(TextColor.fromRgb(6082075)));
if (floatRangeModification.getKey().toString().equals("the_vault:fireball_special_modification")){
return (new TextComponent("Fireball has ").append(minToMaxComponent).append(" chance to fire twice")).withStyle(Style.EMPTY.withColor(TextColor.fromRgb(14076214)));
}
}
String abilityKey = minConfigSpecial.getAbilityKey();
return ModConfigs.ABILITIES.getAbilityById(abilityKey).filter(skill -> skill.getName() != null).map(skill -> {
String name = skill.getName();
return new TextComponent("Special " + name + " modification");
}).orElseGet(() -> (TextComponent) new TextComponent(abilityKey).withStyle(Style.EMPTY.withColor(14076214)));
}
@SuppressWarnings("unchecked")
private static List<SpecialAbilityGearAttribute.SpecialAbilityTierConfig<SpecialAbilityModification<IntRangeConfig, IntValue>, IntRangeConfig, IntValue>> getIntTiers(List<VaultGearTierConfig.ModifierTier<?>> modifierTiers) {
return modifierTiers.stream().map(x -> (SpecialAbilityGearAttribute.SpecialAbilityTierConfig<SpecialAbilityModification<IntRangeConfig, IntValue>, IntRangeConfig, IntValue>) x.getModifierConfiguration()).toList();
}
@SuppressWarnings("unchecked")
private static List<SpecialAbilityGearAttribute.SpecialAbilityTierConfig<SpecialAbilityModification<FloatRangeConfig, FloatValue>, FloatRangeConfig, FloatValue>> getFloatTiers(List<VaultGearTierConfig.ModifierTier<?>> modifierTiers) {
return modifierTiers.stream().map(x -> (SpecialAbilityGearAttribute.SpecialAbilityTierConfig<SpecialAbilityModification<FloatRangeConfig, FloatValue>, FloatRangeConfig, FloatValue>) x.getModifierConfiguration()).toList();
}
/**
* This method handles combining multiple configs into a single component
* VH doesn't have method for this, so we need to do it manually
* it is using the same logic as VH does when shifting on gear piece to get the range
* and combining it with normal display for single component (that has name and color)
*/
private static <T, C> MutableComponent rangeComponent(String atrName, VaultGearAttribute<T> atr,
ConfigurableAttributeGenerator<T, C> atrGenerator, C minConfig, C maxConfig) {
MutableComponent res = atrGenerator.getConfigRangeDisplay(atr.getReader(), minConfig, maxConfig);
MutableComponent minConfigDisplay = atrGenerator.getConfigDisplay(atr.getReader(), minConfig);
MutableComponent maxConfigDisplay = atrGenerator.getConfigDisplay(atr.getReader(), maxConfig);
if (res != null && minConfig instanceof AbilityLevelAttribute.Config minConfigAbility) {
return abilityLvlComponent(res, atr, minConfigAbility);
}
if ((atrName.equals("the_vault:effect_avoidance") || atrName.equals("the_vault:effect_list_avoidance")) && minConfigDisplay != null) {
// res -> "30% - 50%"
// single -> "30% Poison Avoidance"
// minRange -> "30%"
MutableComponent single = minConfigDisplay.withStyle(atr.getReader().getColoredTextStyle());
MutableComponent minRange = atrGenerator.getConfigRangeDisplay(atr.getReader(), minConfig, minConfig);
if (minRange != null && res != null) {
res.append(single.getString().replace(minRange.getString(), ""));
// res -> "30% - 50% Poison Avoidance"
}
}
if (minConfigDisplay != null && maxConfigDisplay != null && (atrName.equals("the_vault:effect_cloud") || atrName.equals("the_vault:effect_cloud_when_hit"))) {
return getCloudRangeComponent(minConfigDisplay, maxConfigDisplay, atr);
}
if ((minConfig instanceof AbilityAreaOfEffectPercentAttribute.Config minConfigA) && (maxConfig instanceof AbilityAreaOfEffectPercentAttribute.Config maxConfigA)) {
return getAbilityAoePercentageComponent(atr, minConfigA, maxConfigA);
}
if (minConfig instanceof EffectGearAttribute.Config minEffectConfig
&& maxConfig instanceof EffectGearAttribute.Config
&& maxConfigDisplay != null) {
String effectStr = ((EffectConfigAccessor)minEffectConfig).getAmplifier() + "-" +
maxConfigDisplay.getString();
return new TextComponent(effectStr).withStyle(atr.getReader().getColoredTextStyle());
}
if (minConfig instanceof ManaPerLootAttribute.Config minManaPerLootConfig && maxConfig instanceof ManaPerLootAttribute.Config maxManaPerLootConfig) {
return getManaPerLootComponent(minManaPerLootConfig, maxManaPerLootConfig);
}
if (atrName.equals("the_vault:effect_cloud")){
return new TextComponent("Special ability modification");
}
if (res != null) {
return atr.getReader().formatConfigDisplay(LogicalSide.CLIENT, res);
}
return res;
}
private static @NotNull MutableComponent getManaPerLootComponent(ManaPerLootAttribute.Config minManaPerLootConfig,
ManaPerLootAttribute.Config maxManaPerLootConfig) {
int minGenerated = minManaPerLootConfig.getManaGenerated().getMin();
int maxGenerated = maxManaPerLootConfig.getManaGenerated().getMax();
float minGenChance = minManaPerLootConfig.getManaGenerationChance().getMin();
float maxGenChance = maxManaPerLootConfig.getManaGenerationChance().getMax();
var minToMax = new TextComponent(DECIMAL_FORMAT.format(minGenChance*100) + "%-" + DECIMAL_FORMAT.format(maxGenChance*100) + "%").withStyle(Style.EMPTY.withColor(20479));
var generated = new TextComponent(minGenerated + "-" + maxGenerated).withStyle(Style.EMPTY.withColor(20479));
var loot = new TextComponent(maxManaPerLootConfig.getDisplayName()).withStyle(Style.EMPTY.withColor(20479));
return new TextComponent("").withStyle(Style.EMPTY.withColor(65535)).append(minToMax).append(new TextComponent(" chance to generate ")).append(generated).append(" Mana per ").append(loot).append(" looted");
}
private static <T> @NotNull MutableComponent getAbilityAoePercentageComponent(VaultGearAttribute<T> atr,
AbilityAreaOfEffectPercentAttribute.Config minConfigA,
AbilityAreaOfEffectPercentAttribute.Config maxConfigA) {
float min = minConfigA.getMin();
float max = maxConfigA.generateMaximumValue();
VaultGearModifierReader<T> reader = atr.getReader();
MutableComponent minValueDisplay = new TextComponent(new DecimalFormat("0.#").format(Math.abs(min * 100.0F)) + "%");
MutableComponent maxValueDisplay = new TextComponent(new DecimalFormat("0.#").format(Math.abs(max * 100.0F)) + "%");
boolean positive = min > 0;
MutableComponent areaCmp = new TextComponent("Area Of Effect").withStyle(Style.EMPTY.withColor(ModConfigs.COLORS.getColor("areaOfEffect")));
String cdInfo;
if (positive) {
cdInfo = " more ";
} else {
cdInfo = " less ";
}
return new TextComponent("")
.append(VaultGearModifier.AffixType.IMPLICIT.getAffixPrefixComponent(true)
.withStyle(Style.EMPTY.withColor(6082075)))
.append(minValueDisplay.withStyle(Style.EMPTY.withColor(6082075)))
.append(new TextComponent("-").withStyle(Style.EMPTY.withColor(6082075)))
.append(maxValueDisplay.withStyle(Style.EMPTY.withColor(6082075)))
.append(cdInfo)
.append(areaCmp)
.append(" of ")
.append(
((AbilityFloatValueAttributeReaderInvoker) reader).invokeFormatAbilityName(minConfigA.getAbilityKey()))
.setStyle(reader.getColoredTextStyle());
}
private static MutableComponent getCloudRangeComponent(MutableComponent minConfigDisplay, MutableComponent maxConfigDisplay, VaultGearAttribute<?> atr) {
// <Effect> [<LVL>] Cloud [when Hit]
// Poison Cloud
// Poison III Cloud
String minString = minConfigDisplay.getString();
String maxString = maxConfigDisplay.getString();
String minLvl = getCloudLvl(minString);
String maxLvl = getCloudLvl(maxString);
if (minLvl.equals(maxLvl)) {
return minConfigDisplay.withStyle(atr.getReader().getColoredTextStyle());
}
String cloudRange = makeCloudLvlRange(minString, minLvl, maxLvl);
return new TextComponent(cloudRange).withStyle(atr.getReader().getColoredTextStyle());
}
private static String getCloudLvl(String displayString){
Matcher matcher = CLOUD_PATTERN.matcher(displayString);
if (matcher.find()) {
if (matcher.group("lvl") != null) {
return matcher.group("lvl");
}
return "I";
}
return "I";
}
private static String makeCloudLvlRange(String displayString, String minLvl, String maxLvl){
Matcher matcher = CLOUD_PATTERN.matcher(displayString);
if (matcher.find()) {
return matcher.group("effect") + " " + minLvl + "-" + maxLvl + " " + matcher.group("suffix");
}
return displayString;
}
private static MutableComponent abilityLvlComponent(MutableComponent prev, VaultGearAttribute<?> atr,
AbilityLevelAttribute.Config minConfig) {
MutableComponent abComp = new TextComponent("+").withStyle(atr.getReader().getColoredTextStyle());
Optional<Skill> optSkill = ModConfigs.ABILITIES.getAbilityById(minConfig.getAbilityKey());
if (optSkill.isEmpty()) {
return prev.append(" added ability levels").withStyle(atr.getReader().getColoredTextStyle());
}
String abName = optSkill.get().getName();
String[] parts = prev.getString().split("-");
MutableComponent res = new TextComponent("").withStyle(prev.getStyle());
if (parts.length == 2) {
if (parts[0].equals(parts[1])) {
res.append(parts[0]);
} else {
res.append(parts[0]);
res.append("-");
res.append(parts[1]);
}
} else {
res.append(prev);
}
abComp.append(res);
abComp.append(" to level of ");
abComp.append(new TextComponent(abName).withStyle(Style.EMPTY.withColor(14076214)));
return abComp;
}
private static int modTierListWeight(List<VaultGearTierConfig.ModifierTier<?>> val) {
if (val == null || val.isEmpty()) {
return 0;
}
return val.stream().mapToInt(VaultGearTierConfig.ModifierTier::getWeight).sum();
}
private static boolean shouldShowWeight(ModifierCategory modifierCategory, VaultGearTierConfig.ModifierAffixTagGroup affixTagGroup) {
return modifierCategory == ModifierCategory.NORMAL && !Config.AFFIX_TAG_GROUP_CHANCE_BLACKLIST.get().contains(affixTagGroup.name());
}
}

View file

@ -0,0 +1,12 @@
package com.radimous.vhatcaniroll.mixin;
import iskallia.vault.gear.attribute.ability.AbilityFloatValueAttribute;
import net.minecraft.network.chat.MutableComponent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(value = AbilityFloatValueAttribute.Reader.class, remap = false)
public interface AbilityFloatValueAttributeReaderInvoker {
@Invoker
MutableComponent invokeFormatAbilityName(String abilityKey);
}

View file

@ -1,6 +1,6 @@
package com.radimous.vhatcaniroll.mixin;
import iskallia.vault.gear.attribute.custom.EffectGearAttribute;
import iskallia.vault.gear.attribute.custom.effect.EffectGearAttribute;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

View file

@ -1,7 +1,8 @@
package com.radimous.vhatcaniroll.mixin;
import com.radimous.vhatcaniroll.Config;
import com.radimous.vhatcaniroll.GearModifierScreen;
import com.radimous.vhatcaniroll.QOLHuntersCompat;
import com.radimous.vhatcaniroll.ui.GearModifierScreen;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.ButtonElement;
import iskallia.vault.client.gui.framework.element.FakeItemSlotElement;
@ -42,6 +43,8 @@ public class StatisticsElementContainerScreenMixin extends AbstractSkillTabEleme
// TODO: figure out how to add button like the quest button, not this ugly shit
QOLHuntersCompat.resolveQOLHuntersButtonConflict();
// add blank button to vault screen
this.addElement(
new ButtonElement<>(Spatials.positionXY(-3, 3), ScreenTextures.BUTTON_EMPTY_16_TEXTURES, () -> {
@ -56,8 +59,9 @@ public class StatisticsElementContainerScreenMixin extends AbstractSkillTabEleme
})
);
// add chestplate icon to it
ItemStack chestplateStack = new ItemStack(ModItems.CHESTPLATE);
this.addElement(
new FakeItemSlotElement<>(Spatials.positionXY(-3, 3), () -> new ItemStack(ModItems.CHESTPLATE), () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY
new FakeItemSlotElement<>(Spatials.positionXY(-3, 3), () -> chestplateStack, () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY
).layout(
(screen, gui, parent, world) -> world.width(21).height(21).translateX(gui.right() + Config.BUTTON_X.get()).translateY(this.getTabContentSpatial().bottom() + Config.BUTTON_Y.get())
)

View file

@ -0,0 +1,18 @@
package com.radimous.vhatcaniroll.mixin;
import iskallia.vault.config.UniqueGearConfig;
import iskallia.vault.core.util.WeightedList;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(value = UniqueGearConfig.class, remap = false)
public interface UniqueGearConfigAccessor {
@Accessor
Map<ResourceLocation, UniqueGearConfig.Entry> getRegistry();
@Accessor
Map<ResourceLocation, WeightedList<ResourceLocation>> getPools();
}

View file

@ -0,0 +1,250 @@
package com.radimous.vhatcaniroll.ui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.radimous.vhatcaniroll.Config;
import com.radimous.vhatcaniroll.logic.ModifierCategory;
import iskallia.vault.client.atlas.TextureAtlasRegion;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.ButtonElement;
import iskallia.vault.client.gui.framework.element.DynamicLabelElement;
import iskallia.vault.client.gui.framework.element.FakeItemSlotElement;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.NineSliceButtonElement;
import iskallia.vault.client.gui.framework.element.VerticalScrollClipContainer;
import iskallia.vault.client.gui.framework.render.spi.IElementRenderer;
import iskallia.vault.client.gui.framework.screen.layout.ScreenLayout;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.IPosition;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import iskallia.vault.config.gear.VaultGearWorkbenchConfig;
import iskallia.vault.gear.VaultGearState;
import iskallia.vault.gear.attribute.VaultGearModifier;
import iskallia.vault.gear.data.VaultGearData;
import iskallia.vault.init.ModGearAttributes;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextColor;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import static iskallia.vault.client.gui.framework.ScreenTextures.BUTTON_EMPTY;
import static iskallia.vault.client.gui.framework.ScreenTextures.BUTTON_EMPTY_16_48_TEXTURES;
import static iskallia.vault.client.gui.framework.ScreenTextures.BUTTON_EMPTY_DISABLED;
public class CraftedModifiersListContainer extends VerticalScrollClipContainer<CraftedModifiersListContainer>
implements InnerGearScreen {
public CraftedModifiersListContainer(ISpatial spatial, int lvl, ModifierCategory modifierCategory,
ItemStack gearPiece) {
super(spatial);
int labelX = 9;
int labelY = 0;
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) {
return;
}
ItemStack stackCopy = gearPiece.copy();
VaultGearData gearData = VaultGearData.read(stackCopy);
gearData.setItemLevel(lvl);
gearData.setState(VaultGearState.IDENTIFIED);
gearData.createOrReplaceAttributeValue(ModGearAttributes.PREFIXES, 1);
gearData.createOrReplaceAttributeValue(ModGearAttributes.SUFFIXES, 1);
gearData.write(stackCopy);
List<VaultGearWorkbenchConfig.CraftableModifierConfig> crMods = VaultGearWorkbenchConfig.getConfig(gearPiece.getItem())
.map(VaultGearWorkbenchConfig::getAllCraftableModifiers).orElse(null);
if (crMods == null) {
cookieDialog();
return;
}
for (VaultGearWorkbenchConfig.CraftableModifierConfig mod : crMods) {
boolean disabled = false;
// is unlocked?
MutableComponent fullCmp = new TextComponent("");
VaultGearModifier<?> mm = mod.createModifier().orElse(null);
if (mm == null) {
continue;
}
Optional<MutableComponent> oCfgDisplay = mm.getConfigDisplay(gearPiece);
oCfgDisplay.ifPresent(fullCmp::append);
MutableComponent restriction = new TextComponent("");
int minLevel = mod.getMinLevel();
if (mod.getUnlockCategory() == VaultGearWorkbenchConfig.UnlockCategory.LEVEL && lvl < minLevel) {
if (!Config.SHOW_UNOBTAINABLE_CRAFTED.get()){
continue;
}
restriction.append(new TextComponent(" LVL " + minLevel + "+").withStyle(ChatFormatting.DARK_RED));
disabled = true;
}
if (mod.getUnlockCategory() == VaultGearWorkbenchConfig.UnlockCategory.VAULT_DISCOVERY && !mod.hasPrerequisites(player)) {
if (minLevel > lvl) {
if (!Config.SHOW_UNOBTAINABLE_CRAFTED.get()){
continue;
}
restriction.append(new TextComponent(" ARCHIVE").withStyle(ChatFormatting.DARK_RED));
} else {
restriction.append(new TextComponent(" ARCHIVE").withStyle(Style.EMPTY.withColor(TextColor.parseColor("#ff6f00"))));
}
disabled = true;
}
LabelElement<?> labelelement = new LabelElement<>(
Spatials.positionXY(8, labelY + 5).width(this.innerHeight() - labelX), fullCmp,
LabelTextStyle.defaultStyle().shadow() // WHY DOESN'T SHADOW WORK?
);
NineSliceButtonElement<?> btn = new NineSliceButtonElement<>(Spatials.positionXY(0, labelY ).width(innerWidth()).height(18),
new NineSliceButtonElement.NineSliceButtonTextures(BUTTON_EMPTY, BUTTON_EMPTY, BUTTON_EMPTY, BUTTON_EMPTY_DISABLED), () -> {});
//align right
LabelElement<?> restrictionLabel = new LabelElement<>(
Spatials.positionXY(btn.right() - 70, labelY + 5).width(this.innerHeight() - labelX).height(14), restriction,
LabelTextStyle.defaultStyle().border4()
);
btn.setDisabled(disabled);
this.addElement(btn);
this.addElement(labelelement);
this.addElement(restrictionLabel);
labelY += 18;
}
}
public float getScroll() {
return 0;
}
public void setScroll(float scroll) {
}
@Override
public InnerGearScreen create(ISpatial spatial, int lvl, ModifierCategory modifierCategory, ItemStack gearPiece) {
return new CraftedModifiersListContainer(spatial, lvl, modifierCategory, gearPiece);
}
// :)
private void cookieDialog(){
var noModifiers = new LabelElement<>(Spatials.positionXY(innerWidth()/2 - 60, 20).width(18).height(18), new TextComponent("No crafted modifiers found."), LabelTextStyle.defaultStyle().border4().center());
var wantCookie = new LabelElement<>(Spatials.positionXY(innerWidth()/2 - 50, 80).width(18).height(18), new TextComponent("Would you like a cookie?"), LabelTextStyle.defaultStyle().border4().center());
var yesBtn = new ButtonElement<>(Spatials.positionXY(innerWidth()/2 - 20, 100).width(100).height(20), BUTTON_EMPTY_16_48_TEXTURES, () -> {});
var noBtn = new ButtonElement<>(Spatials.positionXY(innerWidth()/2 - 20, 120).width(100).height(20), BUTTON_EMPTY_16_48_TEXTURES, () -> { /* sad cookie monster noises */});
var yesLabel = new LabelElement<>(Spatials.positionXY(innerWidth()/2 - 6, 105).width(18).height(18), new TextComponent("Yes"), LabelTextStyle.defaultStyle().border4().center());
var noLabel = new LabelElement<>(Spatials.positionXY(innerWidth()/2 - 3, 125).width(18).height(18), new TextComponent("No"), LabelTextStyle.defaultStyle().border4().center());
var sadCookie = new LabelElement<>(Spatials.positionXY(innerWidth()/2 - 65, 145).width(18).height(18), new TextComponent("*Sad cookie monster noises*").withStyle(ChatFormatting.GRAY), LabelTextStyle.defaultStyle().center());
yesBtn.setOnClick(() -> {
this.removeElement(noModifiers);
this.removeElement(wantCookie);
this.removeElement(yesBtn);
this.removeElement(noBtn);
this.removeElement(yesLabel);
this.removeElement(noLabel);
this.removeElement(sadCookie);
ScreenLayout.requestLayout();
cookie();
});
noBtn.setOnClick(() -> {
this.addElement(sadCookie);
noBtn.setDisabled(true);
ScreenLayout.requestLayout();
});
this.addElement(noModifiers);
this.addElement(wantCookie);
this.addElement(yesLabel);
this.addElement(noLabel);
this.addElement(noBtn);
this.addElement(yesBtn);
}
private void cookie(){
AtomicInteger counter = new AtomicInteger(0);
var slot = new ScaledFakeItemSlotElement<>(Spatials.positionXY(innerWidth()/2, innerHeight()/2), () -> new ItemStack(Items.COOKIE), () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY,8*16,8*16, 8f);
slot.whenClicked(counter::getAndIncrement);
this.addElement(slot);
this.addElement(new CookieLabelElement(Spatials.positionXY(innerWidth()/2, innerHeight()/2 - 70).width(18).height(18), counter::get, LabelTextStyle.defaultStyle().border4().center()));
}
private class CookieLabelElement extends DynamicLabelElement<Integer, CookieLabelElement>{
public CookieLabelElement(IPosition position,
Supplier<Integer> valueSupplier, LabelTextStyle.Builder labelTextStyle) {
super(position, valueSupplier, labelTextStyle);
}
@Override protected void onValueChanged(Integer count) {
this.set(new TextComponent(count.toString()));
}
@Override public void render(IElementRenderer renderer, @NotNull PoseStack poseStack, int mouseX, int mouseY,
float partialTick) {
if (this.valueSupplier.get() == 0) {
return;
}
PoseStack viewStack = RenderSystem.getModelViewStack();
viewStack.pushPose();
float scale = 2f;
viewStack.scale(scale, scale, scale);
viewStack.translate(-getWorldSpatial().x() * (1 - 1/scale), -getWorldSpatial().y() * (1 - 1/scale), 0);
RenderSystem.applyModelViewMatrix();
super.render(renderer, poseStack, mouseX, mouseY, partialTick);
viewStack.popPose();
}
}
private static class ScaledFakeItemSlotElement <E extends ScaledFakeItemSlotElement<E>> extends FakeItemSlotElement<E> {
private float scale;
private long lastClicked = 0;
public ScaledFakeItemSlotElement(ISpatial spatial, Supplier<ItemStack> itemStack, Supplier<Boolean> disabled, TextureAtlasRegion slotTexture, TextureAtlasRegion disabledSlotTexture, int width, int height, float scale) {
super(Spatials.positionX((int) (spatial.x() - (scale * 9))).positionY((int) (spatial.y() - (scale * 9))), itemStack, disabled, slotTexture, disabledSlotTexture, width, height);
this.scale = scale;
}
@Override public void render(IElementRenderer renderer, @NotNull PoseStack poseStack, int mouseX, int mouseY, float partialTick) {
float originalScale = scale;
long msDiff = System.currentTimeMillis() - lastClicked;
if (msDiff < 150) {
scale *= 1 + msDiff * 0.001f;
} else if (msDiff < 300) {
scale *= 1 + (300 - msDiff) * 0.001f;
}
PoseStack viewStack = RenderSystem.getModelViewStack();
viewStack.pushPose();
viewStack.translate(- (scale - originalScale) * originalScale, - (scale - originalScale) * originalScale, 0);
viewStack.scale(scale, scale, 1);
viewStack.translate(-getWorldSpatial().x() * (1 - 1/scale), -getWorldSpatial().y() * (1 - 1/scale), 0);
RenderSystem.applyModelViewMatrix();
super.render(renderer, poseStack, mouseX, mouseY, partialTick);
viewStack.popPose();
scale = originalScale;
}
@Override public boolean mouseClicked(double mouseX, double mouseY, int buttonIndex) {
if (this.isEnabled() && this.containsMouse(mouseX, mouseY) && buttonIndex == 0)
lastClicked = System.currentTimeMillis();
return super.mouseClicked(mouseX, mouseY, buttonIndex);
}
}
}

View file

@ -0,0 +1,508 @@
package com.radimous.vhatcaniroll.ui;
import com.mojang.blaze3d.platform.InputConstants;
import com.radimous.vhatcaniroll.Config;
import com.radimous.vhatcaniroll.VHatCanIRoll;
import com.radimous.vhatcaniroll.logic.Items;
import com.radimous.vhatcaniroll.logic.ModifierCategory;
import com.simibubi.create.foundation.config.ui.ConfigScreen;
import com.simibubi.create.foundation.config.ui.SubMenuConfigScreen;
import iskallia.vault.client.gui.framework.ScreenRenderers;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.ButtonElement;
import iskallia.vault.client.gui.framework.element.FakeItemSlotElement;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.NineSliceButtonElement;
import iskallia.vault.client.gui.framework.element.NineSliceElement;
import iskallia.vault.client.gui.framework.element.TabElement;
import iskallia.vault.client.gui.framework.element.TextureAtlasElement;
import iskallia.vault.client.gui.framework.element.VerticalScrollClipContainer;
import iskallia.vault.client.gui.framework.element.spi.ILayoutElement;
import iskallia.vault.client.gui.framework.element.spi.ILayoutStrategy;
import iskallia.vault.client.gui.framework.render.ScreenTooltipRenderer;
import iskallia.vault.client.gui.framework.render.TooltipDirection;
import iskallia.vault.client.gui.framework.render.Tooltips;
import iskallia.vault.client.gui.framework.screen.AbstractElementScreen;
import iskallia.vault.client.gui.framework.screen.layout.ScreenLayout;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.IPosition;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import iskallia.vault.gear.VaultGearState;
import iskallia.vault.gear.data.VaultGearData;
import iskallia.vault.init.ModBlocks;
import iskallia.vault.init.ModGearAttributes;
import iskallia.vault.init.ModItems;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fml.config.ModConfig;
import java.util.ArrayList;
import java.util.List;
public class GearModifierScreen extends AbstractElementScreen {
//TODO: remove magic numbers
private InnerGearScreen innerScreen;
private NineSliceButtonElement<?> minusButton;
private NineSliceButtonElement<?> plusButton;
private LabelElement<?> minusLabel;
private LabelElement<?> plusLabel;
private final ScrollableLvlInputElement lvlInput;
private ModifierCategory modifierCategory = ModifierCategory.NORMAL;
private LabelElement<?> modifierCategoryLabel;
private NineSliceButtonElement<?> modifierCategoryButton;
private final HelpContainer helpContainer;
private final LabelElement<?> windowNameLabel;
private int currIndex = 0;
private final List<TabElement<?>> tabs = new ArrayList<>();
public GearModifierScreen() {
super(new TextComponent("VHat can I roll?"), ScreenRenderers.getBuffered(), ScreenTooltipRenderer::create);
// make screen size 95% of the window height and width that looks good
this.setGuiSize(Spatials.size(340, 300).height((int) (
(Minecraft.getInstance().getWindow().getHeight() / Minecraft.getInstance().getWindow().getGuiScale()) *
0.95)));
// outer background
NineSliceElement<?> background = new NineSliceElement<>(
Spatials.positionXY(0, 30).size(this.getGuiSpatial().width(), this.getGuiSpatial().height() - 30),
ScreenTextures.DEFAULT_WINDOW_BACKGROUND
).layout(this.translateWorldSpatial());
// window title
LabelElement<?> windowName = new LabelElement<>(
Spatials.positionXY(7, 38).size(this.getGuiSpatial().width() / 2 - 7, 20),
new TranslatableComponent("vhatcaniroll.screen.title.random").withStyle(ChatFormatting.BLACK),
LabelTextStyle.defaultStyle()
).layout(this.translateWorldSpatial());
this.windowNameLabel = windowName;
this.addElement(background);
this.addElement(windowName);
createTabs();
this.lvlInput = this.addElement(createLvlInput());
createLvlButtons(lvlInput);
createModifierCategoryButton();
createConfigButton();
// inner black window
ISpatial modListSpatial = Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57);
this.innerScreen = new ModifierListContainer(modListSpatial, lvlInput.getValue(), modifierCategory, getCurrGear()).layout(this.translateWorldSpatial());
this.addElement(this.innerScreen);
// help container will overlay the modifier list
this.helpContainer = new HelpContainer(Spatials.positionXY(0, 0).size(0, 0));
createHelpButton(helpContainer);
this.addElement(helpContainer);
createModifierButton();
createTransmogButton();
createCraftedModsButton();
createUniqueGearButton();
}
// helper methods
public ItemStack getCurrGear() {
return Items.getVaultGearItems().get(currIndex);
}
/**
* Update the modifier list with the current gear item and lvl and legendary flag
* @param keepScroll whether to keep the current scroll position
*/
private void updateModifierList(boolean keepScroll) {
float oldScroll = this.innerScreen.getScroll();
this.removeElement(this.innerScreen);
ISpatial modListSpatial = Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57);
this.innerScreen = this.innerScreen.create(modListSpatial, lvlInput.getValue(), modifierCategory, getCurrGear());
if (this.innerScreen instanceof ILayoutElement<?> layoutElement) {
layoutElement.layout(this.translateWorldSpatial());
}
if (keepScroll) {
this.innerScreen.setScroll(oldScroll);
}
this.addElement(this.innerScreen);
ScreenLayout.requestLayout();
}
private void switchToTransmog(){
this.removeElement(this.innerScreen);
this.modifierCategory = ModifierCategory.NORMAL;
updateModifierCategoryButtonLabel();
ISpatial modListSpatial = Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57);
this.innerScreen = new TransmogListContainer(modListSpatial, getCurrGear()).layout(this.translateWorldSpatial());
this.disableCategoryButtons();
this.disableLvlButtons();
this.windowNameLabel.set(new TranslatableComponent("vhatcaniroll.screen.title.transmogs").withStyle(ChatFormatting.BLACK));
this.addElement(this.innerScreen);
ScreenLayout.requestLayout();
}
private void switchToModifiers(){
this.removeElement(this.innerScreen);
ISpatial modListSpatial = Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57);
this.innerScreen = new ModifierListContainer(modListSpatial, lvlInput.getValue(), modifierCategory, getCurrGear()).layout(this.translateWorldSpatial());
this.enableCategoryButtons();
this.enableLvlButtons();
this.windowNameLabel.set(new TranslatableComponent("vhatcaniroll.screen.title.random").withStyle(ChatFormatting.BLACK));
this.addElement(this.innerScreen);
ScreenLayout.requestLayout();
}
private void switchToCrafted(){
this.removeElement(this.innerScreen);
this.modifierCategory = ModifierCategory.NORMAL;
updateModifierCategoryButtonLabel();
ISpatial modListSpatial = Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57);
this.innerScreen = new CraftedModifiersListContainer(modListSpatial, lvlInput.getValue(), modifierCategory, getCurrGear()).layout(this.translateWorldSpatial());
this.enableLvlButtons();
this.disableCategoryButtons();
this.windowNameLabel.set(new TranslatableComponent("vhatcaniroll.screen.title.crafted").withStyle(ChatFormatting.BLACK));
this.addElement(this.innerScreen);
ScreenLayout.requestLayout();
}
private void switchToUnique(){
this.removeElement(this.innerScreen);
this.modifierCategory = ModifierCategory.NORMAL;
updateModifierCategoryButtonLabel();
ISpatial modListSpatial = Spatials.positionXY(7, 50).size(this.getGuiSpatial().width() - 14, this.getGuiSpatial().height() - 57);
this.innerScreen = new UniqueGearListContainer(modListSpatial, lvlInput.getValue(), modifierCategory, getCurrGear()).layout(this.translateWorldSpatial());
this.enableLvlButtons();
this.disableCategoryButtons();
this.windowNameLabel.set(new TranslatableComponent("vhatcaniroll.screen.title.unique").withStyle(ChatFormatting.BLACK));
this.addElement(this.innerScreen);
ScreenLayout.requestLayout();
}
private void enableCategoryButtons(){
this.modifierCategoryLabel.setVisible(true);
this.modifierCategoryButton.setVisible(true);
this.modifierCategoryButton.setDisabled(false);
}
private void disableCategoryButtons(){
this.modifierCategoryLabel.setVisible(false);
this.modifierCategoryButton.setVisible(false);
this.modifierCategoryButton.setDisabled(true);
}
private void enableLvlButtons(){
this.lvlInput.setVisible(true);
this.minusButton.setVisible(true);
this.minusLabel.setVisible(true);
this.plusButton.setVisible(true);
this.plusLabel.setVisible(true);
}
private void disableLvlButtons(){
this.lvlInput.setVisible(false);
this.minusButton.setVisible(false);
this.minusLabel.setVisible(false);
this.plusButton.setVisible(false);
this.plusLabel.setVisible(false);
}
// pulled from QuestOverviewElementScreen
private ILayoutStrategy translateWorldSpatial() {
return (screen, gui, parent, world) -> world.translateXY(this.getGuiSpatial());
}
// tabs
/**
* Get the position where tab should be drawn
* @param tabIndex
* @param selected
* @return
*/
private IPosition getTabPos(int tabIndex, boolean selected) {
if (tabIndex < 11){ // top tabs
return Spatials.positionXY(5 + tabIndex * 30, 2 + (selected ? 0 : 4));
}
// right tabs (only needed for wold's rn)
return Spatials.positionXY(337 + (selected ? 0:3), 35 + (tabIndex-11) * 30);
}
/**
* Get the position where item "icon" should be drawn
* @param tabIndex
* @return
*/
private ISpatial getItemPos(int tabIndex) {
if (tabIndex < 11){
return Spatials.positionXY(10 + tabIndex * 30, 11);
}
return Spatials.positionXY(342, 40 + (tabIndex-11) * 30);
}
private TabElement<?> getTabElement(int tabIndex, boolean selected) {
if (tabIndex < 11){ // top tabs
return new TabElement<>(getTabPos(tabIndex, selected),
new TextureAtlasElement<>(
selected ? ScreenTextures.TAB_BACKGROUND_TOP_SELECTED : ScreenTextures.TAB_BACKGROUND_TOP),
new TextureAtlasElement<>(ScreenTextures.EMPTY), () -> switchTab(tabIndex))
.layout(this.translateWorldSpatial());
}
return new TabElement<>(getTabPos(tabIndex, selected), // right tabs (only needed for wold's rn)
new TextureAtlasElement<>(
selected ? ScreenTextures.TAB_BACKGROUND_RIGHT_SELECTED : ScreenTextures.TAB_BACKGROUND_RIGHT),
new TextureAtlasElement<>(ScreenTextures.EMPTY), () -> switchTab(tabIndex))
.layout(this.translateWorldSpatial());
}
private void switchTab(int tabIndex) {
for (int i = 0; i < Items.getVaultGearItems().size(); i++) {
this.removeElement(tabs.get(i));
TabElement<?> tab = getTabElement(i, i == tabIndex);
tabs.set(i, tab);
this.addElement(tab);
}
this.currIndex = tabIndex;
updateModifierList(false);
}
private void createTabs() {
for (int i = 0; i < Items.getVaultGearItems().size(); i++) {
addTab(i);
addFakeItemSlot(i);
}
}
private void addTab(int index) {
TabElement<?> tab = getTabElement(index, index == currIndex);
tabs.add(this.addElement(tab));
}
private void addFakeItemSlot(int index) {
ItemStack gearItem = Items.getVaultGearItems().get(index);
this.addElement(
new FakeItemSlotElement<>(getItemPos(index), () -> gearItem, () -> false,
ScreenTextures.EMPTY, ScreenTextures.EMPTY).layout(
this.translateWorldSpatial()));
}
// header
private ScrollableLvlInputElement createLvlInput() {
ScrollableLvlInputElement inputElement = this.addElement(
new ScrollableLvlInputElement(Spatials.positionXY(this.getGuiSpatial().width() - 54 - 13, 36).size(26, 12),
Minecraft.getInstance().font)
.layout(this.translateWorldSpatial())
);
inputElement.onTextChanged(s -> updateModifierList(true));
return inputElement;
}
private void createLvlButtons(ScrollableLvlInputElement lvlInput) {
LabelElement<?> minusLabel =
new LabelElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 68 - 13, 38), new TextComponent("-"),
LabelTextStyle.border4(ChatFormatting.BLACK))
.layout(this.translateWorldSpatial());
LabelElement<?> plusLabel =
new LabelElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 21 - 13, 38), new TextComponent("+"),
LabelTextStyle.border4(ChatFormatting.BLACK))
.layout(this.translateWorldSpatial());
NineSliceButtonElement<?> btnMinus =
new NineSliceButtonElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 72 - 13, 35).size(15, 14),
ScreenTextures.BUTTON_EMPTY_TEXTURES, lvlInput::decrement).layout(this.translateWorldSpatial());
NineSliceButtonElement<?> btnPlus =
new NineSliceButtonElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 25 - 13, 35).size(15, 14),
ScreenTextures.BUTTON_EMPTY_TEXTURES, lvlInput::increment).layout(this.translateWorldSpatial());
this.minusButton = this.addElement(btnMinus);
this.minusLabel = this.addElement(minusLabel);
this.plusLabel = this.addElement(plusLabel);
this.plusButton = this.addElement(btnPlus);
}
private void nextModifierCategory() {
if (!(this.innerScreen instanceof ModifierListContainer)) return;
this.modifierCategory = modifierCategory.next();
updateModifierCategoryButtonLabel();
updateModifierList(true);
}
private void previousModifierCategory() {
if (!(this.innerScreen instanceof ModifierListContainer)) return;
this.modifierCategory = modifierCategory.previous();
updateModifierCategoryButtonLabel();
updateModifierList(true);
}
private void updateModifierCategoryButtonLabel() {
if (this.modifierCategoryLabel != null) {
this.removeElement(this.modifierCategoryLabel);
}
this.modifierCategoryLabel = new LabelElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 5 - 13, 38),
new TextComponent(modifierCategory.getSymbol()).withStyle(modifierCategory.getStyle()), LabelTextStyle.defaultStyle())
.layout(this.translateWorldSpatial());
this.addElement(modifierCategoryLabel);
}
private void createModifierCategoryButton() {
updateModifierCategoryButtonLabel();
NineSliceButtonElement<?> btnLegend =
new NineSliceButtonElement<>(Spatials.positionXY(this.getGuiSpatial().width() - 8 - 13, 35).size(14, 14),
ScreenTextures.BUTTON_EMPTY_TEXTURES, () -> {
if (hasShiftDown()) {
previousModifierCategory();
} else {
nextModifierCategory();
}
}).layout(this.translateWorldSpatial());
this.addElement(btnLegend);
this.modifierCategoryButton = btnLegend;
}
private void createConfigButton(){
this.addElement(new ButtonElement<>(Spatials.positionXY(-3, 3), ScreenTextures.BUTTON_HISTORY_TEXTURES, () -> {
SubMenuConfigScreen screen = new SubMenuConfigScreen(this, "VHat Can I Roll? Configuration", ModConfig.Type.CLIENT, Config.SPEC, Config.SPEC.getValues());
ConfigScreen.modID = VHatCanIRoll.MODID;
Minecraft.getInstance().setScreen(screen);
})).layout((screen, gui, parent, world) -> {
world.width(21).height(21).translateX(gui.left() - 18).translateY(this.getGuiSpatial().bottom() - 26);
}).tooltip(
Tooltips.single(TooltipDirection.LEFT,() -> new TextComponent("Configuration"))
);
}
private void createHelpButton(HelpContainer hc) {
this.addElement(new ButtonElement<>(Spatials.positionXY(-3, 3), ScreenTextures.BUTTON_QUEST_TEXTURES, () -> {
hc.setVisible(!hc.isVisible());
})).layout((screen, gui, parent, world) -> {
world.width(21).height(21).translateX(gui.left() - 18).translateY(this.getGuiSpatial().bottom() - 48);
}).tooltip(
Tooltips.single(TooltipDirection.LEFT, () -> new TextComponent("Help"))
);
}
private void createModifierButton() {
this.addElement(new ButtonElement<>(Spatials.positionXY(-3, 3), ScreenTextures.BUTTON_EMPTY_16_TEXTURES, () -> {
if (!(this.innerScreen instanceof ModifierListContainer))
switchToModifiers();
})).layout((screen, gui, parent, world) -> {
world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 50);
}).tooltip(
Tooltips.single(TooltipDirection.LEFT, () -> new TranslatableComponent("vhatcaniroll.screen.title.random"))
);
ItemStack chestplateStack = new ItemStack(ModItems.CHESTPLATE);
this.addElement(
new FakeItemSlotElement<>(Spatials.positionXY(-3, 3), () -> chestplateStack, () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY)
.layout((screen, gui, parent, world) -> world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 50))
);
}
private void createUniqueGearButton() {
this.addElement(new ButtonElement<>(Spatials.positionXY(-3, 3), ScreenTextures.BUTTON_EMPTY_16_TEXTURES, () -> {
if (!(this.innerScreen instanceof UniqueGearListContainer))
switchToUnique();
})).layout((screen, gui, parent, world) -> {
world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 70);
}).tooltip(
Tooltips.single(TooltipDirection.LEFT, () -> new TranslatableComponent("vhatcaniroll.screen.title.unique"))
);
ItemStack chestplateStack = new ItemStack(ModItems.CHESTPLATE);
VaultGearData gearData = VaultGearData.read(chestplateStack);
gearData.createOrReplaceAttributeValue(ModGearAttributes.GEAR_ROLL_TYPE, "Unique");
gearData.write(chestplateStack);
this.addElement(
new FakeItemSlotElement<>(Spatials.positionXY(-3, 3), () -> chestplateStack, () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY)
.layout((screen, gui, parent, world) -> world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 70))
);
}
private void createTransmogButton() {
this.addElement(new ButtonElement<>(Spatials.positionXY(-3, 3), ScreenTextures.BUTTON_EMPTY_16_TEXTURES, () -> {
if (!(this.innerScreen instanceof TransmogListContainer))
switchToTransmog();
})).layout((screen, gui, parent, world) -> {
world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 90);
}).tooltip(
Tooltips.single(TooltipDirection.LEFT, () -> new TranslatableComponent("vhatcaniroll.screen.title.transmogs"))
);
ItemStack chestplateStack = new ItemStack(ModItems.CHESTPLATE);
VaultGearData gearData = VaultGearData.read(chestplateStack);
gearData.setState(VaultGearState.IDENTIFIED);
gearData.createOrReplaceAttributeValue(ModGearAttributes.GEAR_MODEL, new ResourceLocation("the_vault:gear/armor/flamingo/chestplate"));
gearData.write(chestplateStack);
this.addElement(
new FakeItemSlotElement<>(Spatials.positionXY(-3, 3), () -> chestplateStack, () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY)
.layout((screen, gui, parent, world) -> world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 90))
);
}
private void createCraftedModsButton() {
this.addElement(new ButtonElement<>(Spatials.positionXY(-3, 3), ScreenTextures.BUTTON_EMPTY_16_TEXTURES, () -> {
if (!(this.innerScreen instanceof CraftedModifiersListContainer))
switchToCrafted();
})).layout((screen, gui, parent, world) -> {
world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 110);
}).tooltip(
Tooltips.single(TooltipDirection.LEFT, () -> new TranslatableComponent("vhatcaniroll.screen.title.crafted"))
);
ItemStack workbenchStack = new ItemStack(ModBlocks.MODIFIER_WORKBENCH);
this.addElement(
new FakeItemSlotElement<>(Spatials.positionXY(-3, 3), () -> workbenchStack, () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY)
.layout((screen, gui, parent, world) -> world.width(21).height(21).translateX(gui.left() - 16).translateY(this.getGuiSpatial().top() + 110))
);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
// left/right to increase/decrease lvl
if (keyCode == InputConstants.KEY_L || keyCode == InputConstants.KEY_RIGHT) {
lvlInput.increment();
}
if (keyCode == InputConstants.KEY_H || keyCode == InputConstants.KEY_LEFT) {
lvlInput.decrement();
}
// up/down to scroll up/down
if (keyCode == InputConstants.KEY_K || keyCode == InputConstants.KEY_UP) {
if (this.innerScreen instanceof VerticalScrollClipContainer<?> vsc){
vsc.onMouseScrolled(0,0,1);
}
}
if (keyCode == InputConstants.KEY_J || keyCode == InputConstants.KEY_DOWN) {
if (this.innerScreen instanceof VerticalScrollClipContainer<?> vsc){
vsc.onMouseScrolled(0,0,-1);
}
}
// tab to next gear item
if (keyCode == InputConstants.KEY_TAB && !hasShiftDown()) {
switchTab((currIndex + 1) % Items.getVaultGearItems().size());
}
// shift+tab to previous gear item
if (keyCode == InputConstants.KEY_TAB && hasShiftDown()) {
switchTab((currIndex - 1 + Items.getVaultGearItems().size()) % Items.getVaultGearItems().size());
}
// ctrl to change modifier category (normal, greater, legendary)
if (keyCode == InputConstants.KEY_LCONTROL || keyCode == InputConstants.KEY_RCONTROL) {
if (hasShiftDown()) {
previousModifierCategory();
} else {
nextModifierCategory();
}
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
}

View file

@ -0,0 +1,119 @@
package com.radimous.vhatcaniroll.ui;
import com.radimous.vhatcaniroll.logic.ModifierCategory;
import iskallia.vault.client.gui.framework.element.ContainerElement;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.spi.ILayoutStrategy;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.TextComponent;
public class HelpContainer extends ContainerElement<HelpContainer> {
public HelpContainer(ISpatial spatial) {
super(spatial);
this.setVisible(false); // Hide by default
LabelElement<?> tabLabel = new LabelElement<>(
Spatials.positionXY(336, 16).width(16).height(16),
new TextComponent("TAB").withStyle(ChatFormatting.GOLD),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(tabLabel);
LabelElement<?> shiftTabLabel = new LabelElement<>(
Spatials.positionXY(-60, 16).width(16).height(16),
new TextComponent("SHIFT + TAB").withStyle(ChatFormatting.GOLD),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(shiftTabLabel);
LabelElement<?> arrows = new LabelElement<>(
Spatials.positionXY(260, 54).width(16).height(16),
new TextComponent("").withStyle(ChatFormatting.GOLD)
.append(new TextComponent("scroll").withStyle(ChatFormatting.BLUE))
.append(new TextComponent("").withStyle(ChatFormatting.GOLD)),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(arrows);
LabelElement<?> vimArrows = new LabelElement<>(
Spatials.positionXY(262, 64).width(16).height(16),
new TextComponent("h ").withStyle(ChatFormatting.GOLD)
.append(new TextComponent("wheel").withStyle(ChatFormatting.BLUE))
.append(new TextComponent(" l").withStyle(ChatFormatting.GOLD)),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(vimArrows);
LabelElement<?> ctrlLabel = new LabelElement<>(
Spatials.positionXY(365, 38).width(16).height(16),
new TextComponent("CTRL").withStyle(ChatFormatting.GOLD),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(ctrlLabel);
LabelElement<?> categoryLabelNormal = new LabelElement<>(
Spatials.positionXY(375, 52).width(16).height(16),
new TextComponent(ModifierCategory.NORMAL.name()).withStyle(ModifierCategory.NORMAL.getStyle()),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(categoryLabelNormal);
LabelElement<?> categoryLabelGreater = new LabelElement<>(
Spatials.positionXY(375, 62).width(16).height(16),
new TextComponent(ModifierCategory.GREATER.name()).withStyle(ModifierCategory.GREATER.getStyle()),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(categoryLabelGreater);
LabelElement<?> categoryLabelLegendary = new LabelElement<>(
Spatials.positionXY(375, 72).width(16).height(16),
new TextComponent(ModifierCategory.LEGENDARY.name()).withStyle(ModifierCategory.LEGENDARY.getStyle()),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(categoryLabelLegendary);
LabelElement<?> upLabel = new LabelElement<>(
Spatials.positionXY(340, 190).width(16).height(16),
new TextComponent("↑ k").withStyle(ChatFormatting.GOLD),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(upLabel);
LabelElement<?> downLabel = new LabelElement<>(
Spatials.positionXY(340, 204).width(16).height(16),
new TextComponent("↓ j").withStyle(ChatFormatting.GOLD),
LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(downLabel);
String text = """
Colored triangles
represent groups
of modifiers.
If 2 modifiers are
in the same group,
they can't be
rolled together.
""";
String[] array = text.split("\n");
int labelY = 120;
for (String s : array) {
LabelElement<?> textLabel = new LabelElement<>(
Spatials.positionXY(-100, labelY).width(20).height(15),
new TextComponent(s).withStyle(ChatFormatting.GOLD), LabelTextStyle.shadow()
).layout(this.translateWorldSpatial());
this.addElement(textLabel);
labelY += 10;
}
}
private ILayoutStrategy translateWorldSpatial() {
return (screen, gui, parent, world) -> world.translateXY(gui);
}
}

View file

@ -0,0 +1,12 @@
package com.radimous.vhatcaniroll.ui;
import com.radimous.vhatcaniroll.logic.ModifierCategory;
import iskallia.vault.client.gui.framework.element.spi.IElement;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import net.minecraft.world.item.ItemStack;
public interface InnerGearScreen extends IElement {
float getScroll();
void setScroll(float scroll);
InnerGearScreen create(ISpatial spatial, int lvl, ModifierCategory modifierCategory, ItemStack gearPiece);
}

View file

@ -0,0 +1,76 @@
package com.radimous.vhatcaniroll.ui;
import com.radimous.vhatcaniroll.logic.ModifierCategory;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.VerticalScrollClipContainer;
import iskallia.vault.client.gui.framework.spatial.Padding;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import iskallia.vault.config.gear.VaultGearTierConfig;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.item.ItemStack;
import java.util.Optional;
import com.radimous.vhatcaniroll.logic.Modifiers;
public class ModifierListContainer extends VerticalScrollClipContainer<ModifierListContainer> implements InnerGearScreen {
public ModifierListContainer(ISpatial spatial, int lvl, ModifierCategory modifierCategory, ItemStack gearPiece) {
super(spatial, Padding.ZERO, ScreenTextures.INSET_BLACK_BACKGROUND);
int labelX = 9;
int labelY = 20;
// Label for the item name and level (GOLD if legendary, AQUA if greater, WHITE if common)
LabelElement<?> itemName = new LabelElement<>(
Spatials.positionXY(labelX, 5).width(this.innerWidth() - labelX).height(15), new TextComponent(
gearPiece.getItem().toString().toUpperCase() + " - LVL " + lvl)
.withStyle(ChatFormatting.UNDERLINE).withStyle(modifierCategory.getStyle()), LabelTextStyle.defaultStyle()
);
this.addElement(itemName);
Optional<VaultGearTierConfig> optCfg = VaultGearTierConfig.getConfig(gearPiece);
if (optCfg.isPresent()) {
VaultGearTierConfig cfg = optCfg.get();
for (Component modifier : Modifiers.getModifierList(lvl, cfg, modifierCategory)) {
LabelElement<?> labelelement = new LabelElement<>(
Spatials.positionXY(labelX, labelY).width(this.innerWidth() - labelX).height(15), modifier, LabelTextStyle.defaultStyle()
);
/* TODO: display individual modifier tiers
I want to display
<groupPrefix> <value> <name> <chance>
and make it expandable to show all tiers of the modifier
<groupPrefix> <value> <name> <chance>
<groupPrefix> <value> <name> <chance>
<groupPrefix> <value> <name> <chance>
<groupPrefix> <value> <name> <chance>
*/
this.addElement(labelelement);
labelY += 10;
}
} else {
LabelElement<?> labelelement = new LabelElement<>(
Spatials.positionXY(labelX, labelY).width(this.innerWidth() - labelX).height(15), new TextComponent(
"Modifier config for " + gearPiece.getItem() + " not found"), LabelTextStyle.defaultStyle()
);
this.addElement(labelelement);
}
}
public float getScroll() {
return this.verticalScrollBarElement.getValue();
}
public void setScroll(float scroll) {
this.verticalScrollBarElement.setValue(scroll);
}
@Override
public InnerGearScreen create(ISpatial spatial, int lvl, ModifierCategory modifierCategory, ItemStack gearPiece) {
return new ModifierListContainer(spatial, lvl, modifierCategory, gearPiece);
}
}

View file

@ -0,0 +1,88 @@
package com.radimous.vhatcaniroll.ui;
import com.radimous.vhatcaniroll.Config;
import iskallia.vault.client.gui.framework.element.TextInputElement;
import iskallia.vault.client.gui.framework.screen.layout.ScreenLayout;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.overlay.VaultBarOverlay;
import iskallia.vault.init.ModConfigs;
import net.minecraft.client.gui.Font;
public class ScrollableLvlInputElement extends TextInputElement<ScrollableLvlInputElement> {
public ScrollableLvlInputElement(ISpatial spatial, Font font) {
super(spatial, font);
this.adjustEditBox(editBox -> {
editBox.setFilter(s -> isValidLevel(parseInt(s)));
editBox.setMaxLength(3);
editBox.setValue(String.valueOf(VaultBarOverlay.vaultLevel));
});
}
@Override
public boolean onMouseScrolled(double mouseX, double mouseY, double delta) {
if (this.isMouseOver(mouseX, mouseY)) {
int val = parseInt(this.getInput());
val += delta > 0 ? 1 : -1;
this.setInput(String.valueOf(val));
return true;
}
return super.onMouseScrolled(mouseX, mouseY, delta);
}
@Override public boolean onMouseClicked(double mouseX, double mouseY, int buttonIndex) {
if (buttonIndex == 1) { // right
this.setInput("");
}
if (buttonIndex == 2){ // middle
this.setValue(VaultBarOverlay.vaultLevel);
}
return super.onMouseClicked(mouseX, mouseY, buttonIndex);
}
@Override public boolean charTyped(char charTyped, int keyCode) {
if (!Character.isDigit(charTyped)) {
return false;
}
return super.charTyped(charTyped, keyCode);
}
public int getValue() {
return parseInt(this.getInput());
}
public void setValue(int value) {
if (isValidLevel(value)) {
this.setInput(String.valueOf(value));
}
}
public void increment() {
this.setValue(getValue() + 1);
}
public void decrement() {
this.setValue(getValue() - 1);
}
private int parseInt(String val) {
try {
return Integer.parseInt(val);
} catch (NumberFormatException e) {
return 0;
}
}
public boolean isValidLevel(int lvl){
if (lvl <= ModConfigs.LEVELS_META.getMaxLevel() && lvl >= 0) {
ScreenLayout.requestLayout();
return true;
}
if (lvl <= Config.MAX_LEVEL_OVERRIDE.get() && lvl >= 0) {
ScreenLayout.requestLayout();
return true;
}
return false;
}
}

View file

@ -0,0 +1,93 @@
package com.radimous.vhatcaniroll.ui;
import com.radimous.vhatcaniroll.logic.ModifierCategory;
import iskallia.vault.block.TransmogTableBlock;
import iskallia.vault.client.ClientDiscoveredEntriesData;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.DiscoveredModelSelectElement;
import iskallia.vault.client.gui.framework.element.FakeItemSlotElement;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.NineSliceButtonElement;
import iskallia.vault.client.gui.framework.element.VerticalScrollClipContainer;
import iskallia.vault.client.gui.framework.spatial.Padding;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import iskallia.vault.gear.VaultGearRarity;
import iskallia.vault.gear.VaultGearState;
import iskallia.vault.gear.data.VaultGearData;
import iskallia.vault.init.ModConfigs;
import iskallia.vault.init.ModDynamicModels;
import iskallia.vault.init.ModGearAttributes;
import iskallia.vault.util.SideOnlyFixer;
import iskallia.vault.util.function.ObservableSupplier;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import java.util.List;
import java.util.Set;
import static iskallia.vault.client.gui.framework.ScreenTextures.BUTTON_EMPTY;
import static iskallia.vault.client.gui.framework.ScreenTextures.BUTTON_EMPTY_DISABLED;
public class TransmogListContainer extends VerticalScrollClipContainer<TransmogListContainer> implements InnerGearScreen {
public TransmogListContainer(ISpatial spatial, ItemStack gearPiece) {
super(spatial, Padding.ZERO, ScreenTextures.INSET_BLACK_BACKGROUND);
int labelX = 9;
int labelY = 0;
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) {
return;
}
Set<ResourceLocation> discoveredModelIds = ClientDiscoveredEntriesData.Models.getDiscoveredModels();
ObservableSupplier<Set<ResourceLocation>> discoveredModelObserverIds = ClientDiscoveredEntriesData.Models.getObserverModels();
DiscoveredModelSelectElement.DiscoveredModelSelectorModel model = new DiscoveredModelSelectElement.DiscoveredModelSelectorModel(
ObservableSupplier.of(() -> gearPiece, SideOnlyFixer::stackEqualExact), discoveredModelObserverIds, x -> {});
List<DiscoveredModelSelectElement.TransmogModelEntry> mEntries = model.getEntries();
for (DiscoveredModelSelectElement.TransmogModelEntry x : mEntries) {
ItemStack displayStack = new ItemStack(gearPiece.getItem());
VaultGearData gearData = VaultGearData.read(displayStack);
gearData.setState(VaultGearState.IDENTIFIED);
gearData.createOrReplaceAttributeValue(ModGearAttributes.GEAR_MODEL, x.getModelId());
gearData.write(displayStack);
this.addElement(new FakeItemSlotElement<>(Spatials.positionXY(labelX, labelY).width(16).height(16), () -> displayStack, () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY));
var oMod = ModDynamicModels.REGISTRIES.getModel(gearPiece.getItem(), x.getModelId());
if (oMod.isPresent()) {
var mod = oMod.get();
VaultGearRarity rollRarity = ModConfigs.GEAR_MODEL_ROLL_RARITIES.getRarityOf(gearPiece, mod.getId());
this.addElement(new LabelElement<>(
Spatials.positionXY(labelX + 20, labelY + 6).width(this.innerWidth() - labelX).height(15),
new TextComponent(mod.getDisplayName()).withStyle(Style.EMPTY.withColor(rollRarity.getColor().getValue())), LabelTextStyle.defaultStyle()));
NineSliceButtonElement<?> btn = new NineSliceButtonElement<>(Spatials.positionXY(0, labelY ).width(innerWidth()).height(18),
new NineSliceButtonElement.NineSliceButtonTextures(BUTTON_EMPTY, BUTTON_EMPTY, BUTTON_EMPTY, BUTTON_EMPTY_DISABLED), () -> {});
boolean canTransmog = TransmogTableBlock.canTransmogModel(player, discoveredModelIds, x.getModelId());
btn.setDisabled(!canTransmog);
this.addElement(btn);
}
labelY += 18;
}
}
public float getScroll() {
return this.verticalScrollBarElement.getValue();
}
public void setScroll(float scroll) {
this.verticalScrollBarElement.setValue(scroll);
}
@Override
public InnerGearScreen create(ISpatial spatial, int lvl, ModifierCategory modifierCategory, ItemStack gearPiece) {
return new TransmogListContainer(spatial, gearPiece);
}
}

View file

@ -0,0 +1,136 @@
package com.radimous.vhatcaniroll.ui;
import com.radimous.vhatcaniroll.Config;
import com.radimous.vhatcaniroll.logic.ModifierCategory;
import com.radimous.vhatcaniroll.logic.Modifiers;
import com.radimous.vhatcaniroll.mixin.UniqueGearConfigAccessor;
import com.radimous.vhatcaniroll.mixin.VaultGearTierConfigAccessor;
import iskallia.vault.client.gui.framework.ScreenTextures;
import iskallia.vault.client.gui.framework.element.FakeItemSlotElement;
import iskallia.vault.client.gui.framework.element.LabelElement;
import iskallia.vault.client.gui.framework.element.NineSliceElement;
import iskallia.vault.client.gui.framework.element.VerticalScrollClipContainer;
import iskallia.vault.client.gui.framework.spatial.Padding;
import iskallia.vault.client.gui.framework.spatial.Spatials;
import iskallia.vault.client.gui.framework.spatial.spi.ISpatial;
import iskallia.vault.client.gui.framework.text.LabelTextStyle;
import iskallia.vault.config.UniqueGearConfig;
import iskallia.vault.config.gear.VaultGearTierConfig;
import iskallia.vault.gear.VaultGearState;
import iskallia.vault.gear.data.VaultGearData;
import iskallia.vault.init.ModConfigs;
import iskallia.vault.init.ModGearAttributes;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import java.util.List;
import java.util.Map;
public class UniqueGearListContainer extends VerticalScrollClipContainer<UniqueGearListContainer> implements InnerGearScreen {
public UniqueGearListContainer(ISpatial spatial, int lvl, ModifierCategory modifierCategory, ItemStack gearPiece) {
super(spatial, Padding.ZERO, ScreenTextures.INSET_BLACK_BACKGROUND);
int labelX = 9;
int labelY = 9;
Map<ResourceLocation, UniqueGearConfig.Entry> uniqueRegistry = ((UniqueGearConfigAccessor) ModConfigs.UNIQUE_GEAR).getRegistry();
ResourceLocation regName = gearPiece.getItem().getRegistryName();
if (regName == null) {
return;
}
String regPath = regName.getPath();
var goodEntries = uniqueRegistry.entrySet().stream().filter(entry -> entry.getValue().getModel() != null && entry.getValue().getModel().toString().contains(regPath)).toList();
VaultGearTierConfigAccessor uniqueConfig1 = (VaultGearTierConfigAccessor) ModConfigs.VAULT_GEAR_CONFIG.get(VaultGearTierConfig.UNIQUE_ITEM);
if (uniqueConfig1 == null) {
return;
}
for (Map.Entry<ResourceLocation, UniqueGearConfig.Entry> entry : goodEntries) {
UniqueGearConfig.Entry value = entry.getValue();
String name = value.getName();
if (name == null) {
continue;
}
ResourceLocation model = value.getModel();
if (model == null) {
continue;
}
Map<UniqueGearConfig.AffixTargetType, List<ResourceLocation>> modifierIdentifiers = value.getModifierIdentifiers();
if (modifierIdentifiers == null) {
continue;
}
int iconHeight = labelY;
labelY += 5;
LabelElement<?> nameLabel = new LabelElement<>(
Spatials.positionXY(labelX + 20, labelY).width(this.innerWidth() - labelX).height(15),
new TextComponent(name), LabelTextStyle.defaultStyle()
);
this.addElement(nameLabel);
labelY += 20;
ItemStack displayStack = new ItemStack(gearPiece.getItem());
VaultGearData gearData = VaultGearData.read(displayStack);
gearData.setState(VaultGearState.IDENTIFIED);
gearData.createOrReplaceAttributeValue(ModGearAttributes.GEAR_MODEL, model);
gearData.write(displayStack);
this.addElement(new FakeItemSlotElement<>(Spatials.positionXY(labelX - 4, iconHeight).width(16).height(16), () -> displayStack, () -> false, ScreenTextures.EMPTY, ScreenTextures.EMPTY));
List<Component> mlist = Modifiers.getUniqueModifierList(lvl, modifierCategory, modifierIdentifiers);
for (Component mc : mlist) {
LabelElement<?> mcl = new LabelElement<>(
Spatials.positionXY(labelX, labelY).width(this.innerWidth() - labelX).height(15),
mc, LabelTextStyle.defaultStyle());
this.addElement(mcl);
labelY += 10;
}
this.addElement(new NineSliceElement<>(
Spatials.positionXY(0, labelY).width(this.innerWidth()).height(3),
ScreenTextures.BUTTON_EMPTY));
labelY += 10;
}
if (Config.DEBUG_UNIQUE_GEAR.get()) {
var badEntries = uniqueRegistry.entrySet().stream().filter(entry -> entry.getValue().getModel() == null || !entry.getValue().getModel().toString().contains(regPath)).toList();
this.addElement(new LabelElement<>(
Spatials.positionXY(labelX, labelY).width(this.innerWidth() - labelX).height(15),
new TextComponent("[DEBUG] BAD ENTRIES:").withStyle(ChatFormatting.RED), LabelTextStyle.defaultStyle()));
labelY += 10;
for (Map.Entry<ResourceLocation, UniqueGearConfig.Entry> entry : badEntries) {
this.addElement(new LabelElement<>(
Spatials.positionXY(labelX, labelY).width(this.innerWidth() - labelX).height(15),
new TextComponent("ID: " + entry.getKey().toString()).withStyle(ChatFormatting.RED),
LabelTextStyle.defaultStyle()));
labelY += 10;
UniqueGearConfig.Entry value = entry.getValue();
if (value == null) {
continue;
}
this.addElement(new LabelElement<>(
Spatials.positionXY(labelX, labelY).width(this.innerWidth() - labelX).height(15),
new TextComponent("Model: " + value.getModel()).withStyle(ChatFormatting.RED),
LabelTextStyle.defaultStyle()));
labelY += 16;
}
}
}
public float getScroll() {
return this.verticalScrollBarElement.getValue();
}
public void setScroll(float scroll) {
this.verticalScrollBarElement.setValue(scroll);
}
@Override
public InnerGearScreen create(ISpatial spatial, int lvl, ModifierCategory modifierCategory, ItemStack gearPiece) {
return new UniqueGearListContainer(spatial, lvl, modifierCategory, gearPiece);
}
}

View file

@ -1,5 +1,8 @@
{
"vhatcaniroll.screen.title": "Gear Modifiers",
"vhatcaniroll.screen.title.random": "Random Modifiers",
"vhatcaniroll.screen.title.crafted": "Crafted Modifiers",
"vhatcaniroll.screen.title.unique": "Unique Gear",
"vhatcaniroll.screen.title.transmogs": "Transmogs",
"vhatcaniroll.openmodscreen": "Open VHat can I roll? screen",
"key.categories.vhatcaniroll": "VHat can I roll?"
}

View file

@ -5,11 +5,13 @@
"compatibilityLevel": "JAVA_8",
"refmap": "vhatcaniroll.refmap.json",
"mixins": [
"EffectConfigAccessor"
],
"client": [
"EffectConfigAccessor",
"StatisticsElementContainerScreenMixin",
"VaultGearTierConfigAccessor"
"UniqueGearConfigAccessor",
"VaultGearTierConfigAccessor",
"AbilityFloatValueAttributeReaderInvoker"
],
"injectors": {
"defaultRequire": 1