diff --git a/src/main/java/fi/dy/masa/tweakeroo/config/FeatureToggle.java b/src/main/java/fi/dy/masa/tweakeroo/config/FeatureToggle.java index 6de4ed8..6ac9c06 100644 --- a/src/main/java/fi/dy/masa/tweakeroo/config/FeatureToggle.java +++ b/src/main/java/fi/dy/masa/tweakeroo/config/FeatureToggle.java @@ -16,290 +16,254 @@ import fi.dy.masa.malilib.util.StringUtils; import fi.dy.masa.tweakeroo.Tweakeroo; -public enum FeatureToggle implements IHotkeyTogglable, IConfigNotifiable -{ - TWEAK_ACCURATE_BLOCK_PLACEMENT ("tweakAccurateBlockPlacement", false, "", "Enables a simpler version of Flexible placement, similar to\nthe Carpet mod, so basically either facing into or out\nfrom the block face clicked on."), - TWEAK_AFTER_CLICKER ("tweakAfterClicker", false, "", KeybindSettings.INGAME_BOTH, "Enables a \"after clicker\" tweak, which does automatic right\nclicks on the just-placed block.\nUseful for example for Repeaters (setting the delay).\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), - TWEAK_AIM_LOCK ("tweakAimLock", false, "", "Enables an aim lock, locking the yaw and pitch rotations\nto the current values.\nThis is separate from the snap aim lock,\nwhich locks them to the snapped value.\nThis allows locking them \"freely\" to the current value."), - TWEAK_ANGEL_BLOCK ("tweakAngelBlock", false, "", "Enables an \"Angel Block\" tweak, which allows\nplacing blocks in mid-air in Creative mode"), - TWEAK_BLOCK_REACH_OVERRIDE ("tweakBlockReachOverride", false, "", "Overrides the block reach distance with\nthe one set in Generic -> blockReachDistance"), - TWEAK_BLOCK_TYPE_BREAK_RESTRICTION("tweakBlockTypeBreakRestriction", false, "", "Restricts which blocks you are able to break (manually).\nSee the corresponding 'blockBreakRestriction*' configs in the Lists category."), - TWEAK_BREAKING_GRID ("tweakBreakingGrid", false, "", KeybindSettings.INGAME_BOTH, "When enabled, you can only break blocks in\na grid pattern, with a configurable interval.\nTo quickly adjust the interval, scroll while\nholding down the tweak toggle keybind."), - TWEAK_BREAKING_RESTRICTION ("tweakBreakingRestriction", false, "", "Enables the Breaking Restriction mode\n (Plane, Layer, Face, Column, Line, Diagonal).\nBasically only allows you to break blocks\nin those patterns, while holding down the attack key."), - TWEAK_CHAT_BACKGROUND_COLOR ("tweakChatBackgroundColor", false, "", "Overrides the default chat background color\nwith the one from Generics -> 'chatBackgroundColor'"), - TWEAK_CHAT_PERSISTENT_TEXT ("tweakChatPersistentText", false, "", "Stores the text from the chat input text field\nand restores it when the chat is opened again"), - TWEAK_CHAT_TIMESTAMP ("tweakChatTimestamp", false, "", "Adds timestamps to chat messages"), - TWEAK_COMMAND_BLOCK_EXTRA_FIELDS("tweakCommandBlockExtraFields", false, "", "Adds extra fields to the Command Block GUI, for settings\nthe name of the command block, and seeing the stats results"), - // TODO 1.19.3+ - //TWEAK_CREATIVE_EXTRA_ITEMS ("tweakCreativeExtraItems", false, "", "Adds custom items to item groups.\nSee Lists -> 'creativeExtraItems' to control which items are added to the groups.\nNote: Currently these will be added to the Transportation group\n(because it has the elast items), but in the future\nthe groups will be configurable per added item"), - // TODO/FIXME 1.19+ the mixin needs an access widener now - //TWEAK_CUSTOM_FLAT_PRESETS ("tweakCustomFlatPresets", false, "", "Allows adding custom flat world presets to the list.\nThe presets are defined in Lists -> flatWorldPresets"), - TWEAK_CUSTOM_FLY_DECELERATION ("tweakCustomFlyDeceleration", false, "", "Allows changing the fly deceleration in creative or spectator mode.\nThis is mainly meant for faster deceleration ie. less \"glide\"\nwhen releasing the movement keys.\nSee Generic -> flyDecelerationRampValue"), - TWEAK_CUSTOM_INVENTORY_GUI_SCALE("tweakCustomInventoryScreenScale", false, "", "Allows using a custom GUI scale for any inventory screen.\nSee Generic -> §ecustomInventoryGuiScale§r for the scale value"), - TWEAK_ELYTRA_CAMERA ("tweakElytraCamera", false, "", "Allows locking the real player rotations while holding the 'elytraCamera' activation key.\nThe controls will then only affect the separate 'camera rotations' for the rendering/camera.\nMeant for things like looking down/around while elytra flying nice and straight."), - TWEAK_ENTITY_TYPE_ATTACK_RESTRICTION("tweakEntityTypeAttackRestriction",false, "", "Restricts which entities you are able to attack (manually).\nSee the corresponding 'entityAttackRestriction*' configs in the Lists category."), - TWEAK_SHULKERBOX_STACKING ("tweakEmptyShulkerBoxesStack", false, true, "", "Enables empty Shulker Boxes stacking up to 64.\nNOTE: They will also stack inside inventories!\nOn servers this will cause desyncs/glitches\nunless the server has a mod that does the same.\nIn single player this changes shulker box based system behaviour."), - TWEAK_SHULKERBOX_STACK_GROUND ("tweakEmptyShulkerBoxesStackOnGround", false, true, "", "Enables empty Shulker Boxes stacking up to 64\nwhen as items on the ground"), - TWEAK_EXPLOSION_REDUCED_PARTICLES ("tweakExplosionReducedParticles", false, "", "If enabled, then all explosion particles will use the\nEXPLOSION_NORMAL particle instead of possibly\nthe EXPLOSION_LARGE or EXPLOSION_HUGE particles"), - TWEAK_F3_CURSOR ("tweakF3Cursor", false, "", "Enables always rendering the F3 screen cursor"), - TWEAK_FAKE_SNEAKING ("tweakFakeSneaking", false, "", "Enables \"fake sneaking\" ie. prevents you from falling from edges\nwithout slowing down the movement speed"), - TWEAK_FAKE_SNEAK_PLACEMENT ("tweakFakeSneakPlacement", false, "", "This tweak offsets the click position to the adjacent air block\nfrom the block that you actually click on.\nThis basically allows you to place blocks against blocks\nthat have a click action, such as opening inventory GUIs,\nwithout having to sneak. Note that this doesn't not actually\nfake sneaking in any way, just the apparent effect is similar."), - TWEAK_FAST_BLOCK_PLACEMENT ("tweakFastBlockPlacement", false, "", "Enables fast/convenient block placement when moving\nthe cursor over new blocks"), - TWEAK_FAST_LEFT_CLICK ("tweakFastLeftClick", false, "", "Enables automatic fast left clicking while holding down\nthe attack button (left click).\nThe number of clicks per tick is set in the Generic configs."), - TWEAK_FAST_RIGHT_CLICK ("tweakFastRightClick", false, "", "Enables automatic fast right clicking while holding down\nthe use button (right click).\nThe number of clicks per tick is set in the Generic configs."), - TWEAK_FILL_CLONE_LIMIT ("tweakFillCloneLimit", false, true, "", "Enables overriding the /fill and /clone command\nblock limits in single player.\nThe new limit can be set in the Generic configs,\nin the 'fillCloneLimit' config value"), - TWEAK_FLY_SPEED ("tweakFlySpeed", false, "", KeybindSettings.INGAME_BOTH, "Enables overriding the fly speed in creative or spectator mode\nand using some presets for it"), - TWEAK_FLEXIBLE_BLOCK_PLACEMENT ("tweakFlexibleBlockPlacement", false, "", "Enables placing blocks in different orientations\nor with an offset, while holding down the\nhotkeys for those modes."), - TWEAK_FREE_CAMERA ("tweakFreeCamera", false, "", "Enables a free camera mode, similar to spectator mode,\nbut where the player will remain in place where\nyou first activate the free camera mode"), - TWEAK_GAMMA_OVERRIDE ("tweakGammaOverride", false, "", "Overrides the video settings gamma value with\nthe one set in the Generic configs"), - TWEAK_HAND_RESTOCK ("tweakHandRestock", false, "", "Enables swapping a new stack to the main or the offhand\nwhen the previous stack runs out"), - TWEAK_HANGABLE_ENTITY_BYPASS ("tweakHangableEntityBypass", false, "", "Allows not targeting hangable entities (Item Frames and Paintings).\nThe Generic -> hangableEntityBypassInverse option can be used to control\nwhether you must be sneaking or not sneaking to be able to target the entity."), - TWEAK_HOLD_ATTACK ("tweakHoldAttack", false, "", "Emulates holding down the attack button"), - TWEAK_HOLD_USE ("tweakHoldUse", false, "", "Emulates holding down the use button"), - TWEAK_HOTBAR_SCROLL ("tweakHotbarScroll", false, "", "Enables the hotbar swapping via scrolling feature"), - TWEAK_HOTBAR_SLOT_CYCLE ("tweakHotbarSlotCycle", false, "", KeybindSettings.INGAME_BOTH, "Enables cycling the selected hotbar slot after each placed\nblock, up to the set max slot number.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), - TWEAK_HOTBAR_SLOT_RANDOMIZER ("tweakHotbarSlotRandomizer", false, "", KeybindSettings.INGAME_BOTH, "Enables randomizing the selected hotbar slot after each placed\nblock, up to the set max slot number.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), - TWEAK_HOTBAR_SWAP ("tweakHotbarSwap", false, "", "Enables the hotbar swapping via hotkeys feature"), - TWEAK_INVENTORY_PREVIEW ("tweakInventoryPreview", false, true, "", "Enables an inventory preview while having the cursor over\na block or an entity with an inventory and holding down\nthe configured hotkey."), - TWEAK_ITEM_UNSTACKING_PROTECTION("tweakItemUnstackingProtection", false, "", "If enabled, then items configured in Lists -> unstackingItems\nwon't be allowed to spill out when using.\nThis is meant for example to prevent throwing buckets\ninto lava when filling them."), - TWEAK_LAVA_VISIBILITY ("tweakLavaVisibility", false, "", "If enabled, then the level of Respiration and Aqua Affinity enchantments,\nand having the Fire Resistance effect active,\nwill greatly increase the visibility under lava."), - TWEAK_MAP_PREVIEW ("tweakMapPreview", false, "", "If enabled, then holding shift over maps in an inventory\nwill render a preview of the map"), - TWEAK_MOVEMENT_KEYS ("tweakMovementKeysLast", false, "", "If enabled, then opposite movement keys won't cancel each other,\nbut instead the last pressed key is the active input."), - TWEAK_PERIODIC_ATTACK ("tweakPeriodicAttack", false, "", "Enables periodic attacks (left clicks)\nConfigure the interval in Generic -> periodicAttackInterval"), - TWEAK_PERIODIC_USE ("tweakPeriodicUse", false, "", "Enables periodic uses (right clicks)\nConfigure the interval in Generic -> periodicUseInterval"), - TWEAK_PERIODIC_HOLD_ATTACK ("tweakPeriodicHoldAttack", false, "", "Enables periodically holding attack for a configurable amount of time.\nConfigure the interval in Generic -> periodicHoldAttackInterval\nand the duration in periodicHoldAttackDuration\n§6Note: You should not use the normal hold attack\n§6or the periodic attack at the same time"), - TWEAK_PERIODIC_HOLD_USE ("tweakPeriodicHoldUse", false, "", "Enables periodically holding use for a configurable amount of time.\nConfigure the interval in Generic -> periodicHoldUseInterval\nand the duration in periodicHoldUseDuration\\n§6Note: You should not use the normal hold use\n§6or the periodic use at the same time"), - TWEAK_PERMANENT_SNEAK ("tweakPermanentSneak", false, "", "If enabled, the player will be sneaking the entire time"), - TWEAK_PERMANENT_SPRINT ("tweakPermanentSprint", false, "", "If enabled, the player will be always sprinting when moving forward"), - TWEAK_PLACEMENT_GRID ("tweakPlacementGrid", false, "", KeybindSettings.INGAME_BOTH, "When enabled, you can only place blocks in\na grid pattern, with a configurable interval.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), - TWEAK_PLACEMENT_LIMIT ("tweakPlacementLimit", false, "", KeybindSettings.INGAME_BOTH, "When enabled, you can only place a set number\nof blocks per use/right click.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), - TWEAK_PLACEMENT_RESTRICTION ("tweakPlacementRestriction", false, "", "Enables the Placement Restriction mode\n (Plane, Layer, Face, Column, Line, Diagonal)"), - TWEAK_PLACEMENT_REST_FIRST ("tweakPlacementRestrictionFirst", false, "", "Restricts block placement so that you can only\nplace blocks against the same block type\nyou first clicked on"), - TWEAK_PLACEMENT_REST_HAND ("tweakPlacementRestrictionHand", false, "", "Restricts block placement so that you can only\nplace blocks against the same block type\nyou are holding in your hand"), - TWEAK_PLAYER_INVENTORY_PEEK ("tweakPlayerInventoryPeek", false, "", "Enables a player inventory peek/preview, while holding the\nconfigured hotkey key for it."), - TWEAK_POTION_WARNING ("tweakPotionWarning", false, "", "Prints a warning message to the hotbar when\nnon-ambient potion effects are about to run out"), - TWEAK_PRINT_DEATH_COORDINATES ("tweakPrintDeathCoordinates", false, "", "Enables printing the player's coordinates to chat on death.\nThis feature is originally from usefulmod by nessie."), - TWEAK_PICK_BEFORE_PLACE ("tweakPickBeforePlace", false, "", "If enabled, then before each block placement, the same block\nis switched to hand that you are placing against"), - TWEAK_PLAYER_LIST_ALWAYS_ON ("tweakPlayerListAlwaysVisible", false, "", "If enabled, then the player list is always rendered without\nhaving to hold down the key (tab by default)"), - TWEAK_RENDER_EDGE_CHUNKS ("tweakRenderEdgeChunks", false, "", "Allows the edge-most client-loaded chunks to render.\nVanilla doesn't allow rendering chunks that don't have\nall the adjacent chunks loaded, meaning that the edge-most chunk\nof the client's loaded won't render in vanilla.\n§lThis is also very helpful in the Free Camera mode!§r"), - TWEAK_RENDER_INVISIBLE_ENTITIES ("tweakRenderInvisibleEntities", false, "", "When enabled, invisible entities are rendered like\nthey would be in spectator mode."), - TWEAK_RENDER_LIMIT_ENTITIES ("tweakRenderLimitEntities", false, "", "Enables limiting the number of certain types of entities\nto render per frame. Currently XP Orbs and Item entities\nare supported, see Generic configs for the limits."), - TWEAK_REPAIR_MODE ("tweakRepairMode", false, "", "If enabled, then fully repaired items held in hand will\nbe swapped to damaged items that have Mending on them."), - TWEAK_SCULK_PULSE_LENGTH ("tweakSculkPulseLength", false, true, "", "Allows modifying the Sculk Sensor pulse length. Set the pulse length in Generic -> sculkSensorPulseLength"), - TWEAK_SHULKERBOX_DISPLAY ("tweakShulkerBoxDisplay", false, "", "Enables the Shulker Box contents display when hovering\nover them in an inventory and holding shift"), - TWEAK_SIGN_COPY ("tweakSignCopy", false, "", "When enabled, placed signs will use the text from\nthe previously placed sign.\nCan be combined with tweakNoSignGui to quickly place copies\nof a sign, by enabling that tweak after making the first sign."), - TWEAK_SNAP_AIM ("tweakSnapAim", false, "", KeybindSettings.INGAME_BOTH, "Enabled a snap aim tweak, to make the player face to pre-set exact yaw rotations"), - TWEAK_SNAP_AIM_LOCK ("tweakSnapAimLock", false, "", "Enables a snap aim lock, locking the yaw and/or pitch rotations\nto the currently snapped value"), - TWEAK_SNEAK_1_15_2 ("tweakSneak_1.15.2", false, "", "Restores the 1.15.2 sneaking behavior"), - TWEAK_SPECTATOR_TELEPORT ("tweakSpectatorTeleport", false, "", "Allows spectators to teleport to other spectators.\nThis is originally from usefulmod by nessie."), - TWEAK_STRUCTURE_BLOCK_LIMIT ("tweakStructureBlockLimit", false, true, "", "Allows overriding the structure block limit.\nThe new limit is set in Generic -> structureBlockMaxSize"), - TWEAK_SWAP_ALMOST_BROKEN_TOOLS ("tweakSwapAlmostBrokenTools", false, "", "If enabled, then any damageable items held in the hand that\nare about to break will be swapped to fresh ones"), - TWEAK_TAB_COMPLETE_COORDINATE ("tweakTabCompleteCoordinate", false, "", "If enabled, then tab-completing coordinates while not\nlooking at a block, will use the player's position\ninstead of adding the ~ character."), - TWEAK_TOOL_SWITCH ("tweakToolSwitch", false, "", "Enables automatically switching to an effective tool for the targeted block"), - TWEAK_WEAPON_SWITCH ("tweakWeaponSwitch", false, "", "Enables automatically switching to a weapon for the targeted entity"), - TWEAK_Y_MIRROR ("tweakYMirror", false, "", "Mirrors the targeted y-position within the block bounds.\nThis is basically for placing slabs or stairs\nin the opposite top/bottom state from normal,\nif you have to place them against another slab for example."), - TWEAK_ZOOM ("tweakZoom", false, "", KeybindSettings.INGAME_BOTH, "Enables using the zoom hotkey to, well, zoom in"); - - public static final ImmutableList VALUES = ImmutableList.copyOf(values()); - - private final String name; - private final String comment; - private final String prettyName; - private final IKeybind keybind; - private final boolean defaultValueBoolean; - private final boolean singlePlayer; - private boolean valueBoolean; - private IValueChangeCallback callback; - - FeatureToggle(String name, boolean defaultValue, String defaultHotkey, String comment) - { - this(name, defaultValue, false, defaultHotkey, KeybindSettings.DEFAULT, comment); +public enum FeatureToggle implements IHotkeyTogglable, IConfigNotifiable { + TWEAK_ACCURATE_BLOCK_PLACEMENT("tweakAccurateBlockPlacement", false, "", "Enables a simpler version of Flexible placement, similar to\nthe Carpet mod, so basically either facing into or out\nfrom the block face clicked on."), + TWEAK_AFTER_CLICKER("tweakAfterClicker", false, "", KeybindSettings.INGAME_BOTH, "Enables a \"after clicker\" tweak, which does automatic right\nclicks on the just-placed block.\nUseful for example for Repeaters (setting the delay).\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), + TWEAK_AIM_LOCK("tweakAimLock", false, "", "Enables an aim lock, locking the yaw and pitch rotations\nto the current values.\nThis is separate from the snap aim lock,\nwhich locks them to the snapped value.\nThis allows locking them \"freely\" to the current value."), + TWEAK_ANGEL_BLOCK("tweakAngelBlock", false, "", "Enables an \"Angel Block\" tweak, which allows\nplacing blocks in mid-air in Creative mode"), + TWEAK_AUTO_SWITCH_ELYTRA("tweakAutoSwitchElytra", false, "", "Automatically switches to the Elytra when falling\\nand back to the previous chest equipment when landing."), + TWEAK_BLOCK_REACH_OVERRIDE("tweakBlockReachOverride", false, "", "Overrides the block reach distance with\nthe one set in Generic -> blockReachDistance"), + TWEAK_BLOCK_TYPE_BREAK_RESTRICTION("tweakBlockTypeBreakRestriction", false, "", "Restricts which blocks you are able to break (manually).\nSee the corresponding 'blockBreakRestriction*' configs in the Lists category."), + TWEAK_BREAKING_GRID("tweakBreakingGrid", false, "", KeybindSettings.INGAME_BOTH, "When enabled, you can only break blocks in\na grid pattern, with a configurable interval.\nTo quickly adjust the interval, scroll while\nholding down the tweak toggle keybind."), + TWEAK_BREAKING_RESTRICTION("tweakBreakingRestriction", false, "", "Enables the Breaking Restriction mode\n (Plane, Layer, Face, Column, Line, Diagonal).\nBasically only allows you to break blocks\nin those patterns, while holding down the attack key."), + TWEAK_CHAT_BACKGROUND_COLOR("tweakChatBackgroundColor", false, "", "Overrides the default chat background color\nwith the one from Generics -> 'chatBackgroundColor'"), + TWEAK_CHAT_PERSISTENT_TEXT("tweakChatPersistentText", false, "", "Stores the text from the chat input text field\nand restores it when the chat is opened again"), + TWEAK_CHAT_TIMESTAMP("tweakChatTimestamp", false, "", "Adds timestamps to chat messages"), + TWEAK_COMMAND_BLOCK_EXTRA_FIELDS("tweakCommandBlockExtraFields", false, "", "Adds extra fields to the Command Block GUI, for settings\nthe name of the command block, and seeing the stats results"), + // TODO 1.19.3+ + //TWEAK_CREATIVE_EXTRA_ITEMS ("tweakCreativeExtraItems", false, "", "Adds custom items to item groups.\nSee Lists -> 'creativeExtraItems' to control which items are added to the groups.\nNote: Currently these will be added to the Transportation group\n(because it has the elast items), but in the future\nthe groups will be configurable per added item"), + // TODO/FIXME 1.19+ the mixin needs an access widener now + //TWEAK_CUSTOM_FLAT_PRESETS ("tweakCustomFlatPresets", false, "", "Allows adding custom flat world presets to the list.\nThe presets are defined in Lists -> flatWorldPresets"), + TWEAK_CUSTOM_FLY_DECELERATION("tweakCustomFlyDeceleration", false, "", "Allows changing the fly deceleration in creative or spectator mode.\nThis is mainly meant for faster deceleration ie. less \"glide\"\nwhen releasing the movement keys.\nSee Generic -> flyDecelerationRampValue"), + TWEAK_CUSTOM_INVENTORY_GUI_SCALE("tweakCustomInventoryScreenScale", false, "", "Allows using a custom GUI scale for any inventory screen.\nSee Generic -> §ecustomInventoryGuiScale§r for the scale value"), + TWEAK_ELYTRA_CAMERA("tweakElytraCamera", false, "", "Allows locking the real player rotations while holding the 'elytraCamera' activation key.\nThe controls will then only affect the separate 'camera rotations' for the rendering/camera.\nMeant for things like looking down/around while elytra flying nice and straight."), + TWEAK_ENTITY_TYPE_ATTACK_RESTRICTION("tweakEntityTypeAttackRestriction", false, "", "Restricts which entities you are able to attack (manually).\nSee the corresponding 'entityAttackRestriction*' configs in the Lists category."), + TWEAK_SHULKERBOX_STACKING("tweakEmptyShulkerBoxesStack", false, true, "", "Enables empty Shulker Boxes stacking up to 64.\nNOTE: They will also stack inside inventories!\nOn servers this will cause desyncs/glitches\nunless the server has a mod that does the same.\nIn single player this changes shulker box based system behaviour."), + TWEAK_SHULKERBOX_STACK_GROUND("tweakEmptyShulkerBoxesStackOnGround", false, true, "", "Enables empty Shulker Boxes stacking up to 64\nwhen as items on the ground"), + TWEAK_EXPLOSION_REDUCED_PARTICLES("tweakExplosionReducedParticles", false, "", "If enabled, then all explosion particles will use the\nEXPLOSION_NORMAL particle instead of possibly\nthe EXPLOSION_LARGE or EXPLOSION_HUGE particles"), + TWEAK_F3_CURSOR("tweakF3Cursor", false, "", "Enables always rendering the F3 screen cursor"), + TWEAK_FAKE_SNEAKING("tweakFakeSneaking", false, "", "Enables \"fake sneaking\" ie. prevents you from falling from edges\nwithout slowing down the movement speed"), + TWEAK_FAKE_SNEAK_PLACEMENT("tweakFakeSneakPlacement", false, "", "This tweak offsets the click position to the adjacent air block\nfrom the block that you actually click on.\nThis basically allows you to place blocks against blocks\nthat have a click action, such as opening inventory GUIs,\nwithout having to sneak. Note that this doesn't not actually\nfake sneaking in any way, just the apparent effect is similar."), + TWEAK_FAST_BLOCK_PLACEMENT("tweakFastBlockPlacement", false, "", "Enables fast/convenient block placement when moving\nthe cursor over new blocks"), + TWEAK_FAST_LEFT_CLICK("tweakFastLeftClick", false, "", "Enables automatic fast left clicking while holding down\nthe attack button (left click).\nThe number of clicks per tick is set in the Generic configs."), + TWEAK_FAST_RIGHT_CLICK("tweakFastRightClick", false, "", "Enables automatic fast right clicking while holding down\nthe use button (right click).\nThe number of clicks per tick is set in the Generic configs."), + TWEAK_FILL_CLONE_LIMIT("tweakFillCloneLimit", false, true, "", "Enables overriding the /fill and /clone command\nblock limits in single player.\nThe new limit can be set in the Generic configs,\nin the 'fillCloneLimit' config value"), + TWEAK_FLY_SPEED("tweakFlySpeed", false, "", KeybindSettings.INGAME_BOTH, "Enables overriding the fly speed in creative or spectator mode\nand using some presets for it"), + TWEAK_FLEXIBLE_BLOCK_PLACEMENT("tweakFlexibleBlockPlacement", false, "", "Enables placing blocks in different orientations\nor with an offset, while holding down the\nhotkeys for those modes."), + TWEAK_FREE_CAMERA("tweakFreeCamera", false, "", "Enables a free camera mode, similar to spectator mode,\nbut where the player will remain in place where\nyou first activate the free camera mode"), + TWEAK_GAMMA_OVERRIDE("tweakGammaOverride", false, "", "Overrides the video settings gamma value with\nthe one set in the Generic configs"), + TWEAK_HAND_RESTOCK("tweakHandRestock", false, "", "Enables swapping a new stack to the main or the offhand\nwhen the previous stack runs out"), + TWEAK_HANGABLE_ENTITY_BYPASS("tweakHangableEntityBypass", false, "", "Allows not targeting hangable entities (Item Frames and Paintings).\nThe Generic -> hangableEntityBypassInverse option can be used to control\nwhether you must be sneaking or not sneaking to be able to target the entity."), + TWEAK_HOLD_ATTACK("tweakHoldAttack", false, "", "Emulates holding down the attack button"), + TWEAK_HOLD_USE("tweakHoldUse", false, "", "Emulates holding down the use button"), + TWEAK_HOTBAR_SCROLL("tweakHotbarScroll", false, "", "Enables the hotbar swapping via scrolling feature"), + TWEAK_HOTBAR_SLOT_CYCLE("tweakHotbarSlotCycle", false, "", KeybindSettings.INGAME_BOTH, "Enables cycling the selected hotbar slot after each placed\nblock, up to the set max slot number.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), + TWEAK_HOTBAR_SLOT_RANDOMIZER("tweakHotbarSlotRandomizer", false, "", KeybindSettings.INGAME_BOTH, "Enables randomizing the selected hotbar slot after each placed\nblock, up to the set max slot number.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), + TWEAK_HOTBAR_SWAP("tweakHotbarSwap", false, "", "Enables the hotbar swapping via hotkeys feature"), + TWEAK_INVENTORY_PREVIEW("tweakInventoryPreview", false, true, "", "Enables an inventory preview while having the cursor over\na block or an entity with an inventory and holding down\nthe configured hotkey."), + TWEAK_ITEM_UNSTACKING_PROTECTION("tweakItemUnstackingProtection", false, "", "If enabled, then items configured in Lists -> unstackingItems\nwon't be allowed to spill out when using.\nThis is meant for example to prevent throwing buckets\ninto lava when filling them."), + TWEAK_LAVA_VISIBILITY("tweakLavaVisibility", false, "", "If enabled, then the level of Respiration and Aqua Affinity enchantments,\nand having the Fire Resistance effect active,\nwill greatly increase the visibility under lava."), + TWEAK_MAP_PREVIEW("tweakMapPreview", false, "", "If enabled, then holding shift over maps in an inventory\nwill render a preview of the map"), + TWEAK_MOVEMENT_KEYS("tweakMovementKeysLast", false, "", "If enabled, then opposite movement keys won't cancel each other,\nbut instead the last pressed key is the active input."), + TWEAK_PERIODIC_ATTACK("tweakPeriodicAttack", false, "", "Enables periodic attacks (left clicks)\nConfigure the interval in Generic -> periodicAttackInterval"), + TWEAK_PERIODIC_USE("tweakPeriodicUse", false, "", "Enables periodic uses (right clicks)\nConfigure the interval in Generic -> periodicUseInterval"), + TWEAK_PERIODIC_HOLD_ATTACK("tweakPeriodicHoldAttack", false, "", "Enables periodically holding attack for a configurable amount of time.\nConfigure the interval in Generic -> periodicHoldAttackInterval\nand the duration in periodicHoldAttackDuration\n§6Note: You should not use the normal hold attack\n§6or the periodic attack at the same time"), + TWEAK_PERIODIC_HOLD_USE("tweakPeriodicHoldUse", false, "", "Enables periodically holding use for a configurable amount of time.\nConfigure the interval in Generic -> periodicHoldUseInterval\nand the duration in periodicHoldUseDuration\\n§6Note: You should not use the normal hold use\n§6or the periodic use at the same time"), + TWEAK_PERMANENT_SNEAK("tweakPermanentSneak", false, "", "If enabled, the player will be sneaking the entire time"), + TWEAK_PERMANENT_SPRINT("tweakPermanentSprint", false, "", "If enabled, the player will be always sprinting when moving forward"), + TWEAK_PLACEMENT_GRID("tweakPlacementGrid", false, "", KeybindSettings.INGAME_BOTH, "When enabled, you can only place blocks in\na grid pattern, with a configurable interval.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), + TWEAK_PLACEMENT_LIMIT("tweakPlacementLimit", false, "", KeybindSettings.INGAME_BOTH, "When enabled, you can only place a set number\nof blocks per use/right click.\nTo quickly adjust the value, scroll while\nholding down the tweak toggle keybind."), + TWEAK_PLACEMENT_RESTRICTION("tweakPlacementRestriction", false, "", "Enables the Placement Restriction mode\n (Plane, Layer, Face, Column, Line, Diagonal)"), + TWEAK_PLACEMENT_REST_FIRST("tweakPlacementRestrictionFirst", false, "", "Restricts block placement so that you can only\nplace blocks against the same block type\nyou first clicked on"), + TWEAK_PLACEMENT_REST_HAND("tweakPlacementRestrictionHand", false, "", "Restricts block placement so that you can only\nplace blocks against the same block type\nyou are holding in your hand"), + TWEAK_PLAYER_INVENTORY_PEEK("tweakPlayerInventoryPeek", false, "", "Enables a player inventory peek/preview, while holding the\nconfigured hotkey key for it."), + TWEAK_POTION_WARNING("tweakPotionWarning", false, "", "Prints a warning message to the hotbar when\nnon-ambient potion effects are about to run out"), + TWEAK_PRINT_DEATH_COORDINATES("tweakPrintDeathCoordinates", false, "", "Enables printing the player's coordinates to chat on death.\nThis feature is originally from usefulmod by nessie."), + TWEAK_PICK_BEFORE_PLACE("tweakPickBeforePlace", false, "", "If enabled, then before each block placement, the same block\nis switched to hand that you are placing against"), + TWEAK_PLAYER_LIST_ALWAYS_ON("tweakPlayerListAlwaysVisible", false, "", "If enabled, then the player list is always rendered without\nhaving to hold down the key (tab by default)"), + TWEAK_RENDER_EDGE_CHUNKS("tweakRenderEdgeChunks", false, "", "Allows the edge-most client-loaded chunks to render.\nVanilla doesn't allow rendering chunks that don't have\nall the adjacent chunks loaded, meaning that the edge-most chunk\nof the client's loaded won't render in vanilla.\n§lThis is also very helpful in the Free Camera mode!§r"), + TWEAK_RENDER_INVISIBLE_ENTITIES("tweakRenderInvisibleEntities", false, "", "When enabled, invisible entities are rendered like\nthey would be in spectator mode."), + TWEAK_RENDER_LIMIT_ENTITIES("tweakRenderLimitEntities", false, "", "Enables limiting the number of certain types of entities\nto render per frame. Currently XP Orbs and Item entities\nare supported, see Generic configs for the limits."), + TWEAK_REPAIR_MODE("tweakRepairMode", false, "", "If enabled, then fully repaired items held in hand will\nbe swapped to damaged items that have Mending on them."), + TWEAK_SCULK_PULSE_LENGTH("tweakSculkPulseLength", false, true, "", "Allows modifying the Sculk Sensor pulse length. Set the pulse length in Generic -> sculkSensorPulseLength"), + TWEAK_SHULKERBOX_DISPLAY("tweakShulkerBoxDisplay", false, "", "Enables the Shulker Box contents display when hovering\nover them in an inventory and holding shift"), + TWEAK_SIGN_COPY("tweakSignCopy", false, "", "When enabled, placed signs will use the text from\nthe previously placed sign.\nCan be combined with tweakNoSignGui to quickly place copies\nof a sign, by enabling that tweak after making the first sign."), + TWEAK_SNAP_AIM("tweakSnapAim", false, "", KeybindSettings.INGAME_BOTH, "Enabled a snap aim tweak, to make the player face to pre-set exact yaw rotations"), + TWEAK_SNAP_AIM_LOCK("tweakSnapAimLock", false, "", "Enables a snap aim lock, locking the yaw and/or pitch rotations\nto the currently snapped value"), + TWEAK_SNEAK_1_15_2("tweakSneak_1.15.2", false, "", "Restores the 1.15.2 sneaking behavior"), + TWEAK_SPECTATOR_TELEPORT("tweakSpectatorTeleport", false, "", "Allows spectators to teleport to other spectators.\nThis is originally from usefulmod by nessie."), + TWEAK_STRUCTURE_BLOCK_LIMIT("tweakStructureBlockLimit", false, true, "", "Allows overriding the structure block limit.\nThe new limit is set in Generic -> structureBlockMaxSize"), + TWEAK_SWAP_ALMOST_BROKEN_TOOLS("tweakSwapAlmostBrokenTools", false, "", "If enabled, then any damageable items held in the hand that\nare about to break will be swapped to fresh ones"), + TWEAK_TAB_COMPLETE_COORDINATE("tweakTabCompleteCoordinate", false, "", "If enabled, then tab-completing coordinates while not\nlooking at a block, will use the player's position\ninstead of adding the ~ character."), + TWEAK_TOOL_SWITCH("tweakToolSwitch", false, "", "Enables automatically switching to an effective tool for the targeted block"), + TWEAK_WEAPON_SWITCH("tweakWeaponSwitch", false, "", "Enables automatically switching to a weapon for the targeted entity"), + TWEAK_Y_MIRROR("tweakYMirror", false, "", "Mirrors the targeted y-position within the block bounds.\nThis is basically for placing slabs or stairs\nin the opposite top/bottom state from normal,\nif you have to place them against another slab for example."), + TWEAK_ZOOM("tweakZoom", false, "", KeybindSettings.INGAME_BOTH, "Enables using the zoom hotkey to, well, zoom in"); + + public static final ImmutableList VALUES = ImmutableList.copyOf(values()); + + private final String name; + private final String comment; + private final String prettyName; + private final IKeybind keybind; + private final boolean defaultValueBoolean; + private final boolean singlePlayer; + private boolean valueBoolean; + private IValueChangeCallback callback; + + FeatureToggle(String name, boolean defaultValue, String defaultHotkey, String comment) { + this(name, defaultValue, false, defaultHotkey, KeybindSettings.DEFAULT, comment); + } + + FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, String comment) { + this(name, defaultValue, singlePlayer, defaultHotkey, KeybindSettings.DEFAULT, comment); + } + + FeatureToggle(String name, boolean defaultValue, String defaultHotkey, KeybindSettings settings, String comment) { + this(name, defaultValue, false, defaultHotkey, settings, comment); + } + + FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, KeybindSettings settings, String comment) { + this(name, defaultValue, singlePlayer, defaultHotkey, settings, comment, StringUtils.splitCamelCase(name.substring(5))); + } + + FeatureToggle(String name, boolean defaultValue, String defaultHotkey, String comment, String prettyName) { + this(name, defaultValue, false, defaultHotkey, comment, prettyName); + } + + FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, String comment, String prettyName) { + this(name, defaultValue, singlePlayer, defaultHotkey, KeybindSettings.DEFAULT, comment, prettyName); + } + + FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, KeybindSettings settings, String comment, String prettyName) { + this.name = name; + this.valueBoolean = defaultValue; + this.defaultValueBoolean = defaultValue; + this.singlePlayer = singlePlayer; + this.comment = comment; + this.prettyName = prettyName; + this.keybind = KeybindMulti.fromStorageString(defaultHotkey, settings); + this.keybind.setCallback(new KeyCallbackToggleBooleanConfigWithMessage(this)); + } + + @Override + public ConfigType getType() { + return ConfigType.HOTKEY; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getConfigGuiDisplayName() { + String name = StringUtils.getTranslatedOrFallback("config.name." + this.getName().toLowerCase(), this.getName()); + + if (this.singlePlayer) { + return GuiBase.TXT_GOLD + name + GuiBase.TXT_RST; } - FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, String comment) - { - this(name, defaultValue, singlePlayer, defaultHotkey, KeybindSettings.DEFAULT, comment); - } - - FeatureToggle(String name, boolean defaultValue, String defaultHotkey, KeybindSettings settings, String comment) - { - this(name, defaultValue, false, defaultHotkey, settings, comment); - } - - FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, KeybindSettings settings, String comment) - { - this(name, defaultValue, singlePlayer, defaultHotkey, settings, comment, StringUtils.splitCamelCase(name.substring(5))); - } - - FeatureToggle(String name, boolean defaultValue, String defaultHotkey, String comment, String prettyName) - { - this(name, defaultValue, false, defaultHotkey, comment, prettyName); - } - - FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, String comment, String prettyName) - { - this(name, defaultValue, singlePlayer, defaultHotkey, KeybindSettings.DEFAULT, comment, prettyName); - } - - FeatureToggle(String name, boolean defaultValue, boolean singlePlayer, String defaultHotkey, KeybindSettings settings, String comment, String prettyName) - { - this.name = name; - this.valueBoolean = defaultValue; - this.defaultValueBoolean = defaultValue; - this.singlePlayer = singlePlayer; - this.comment = comment; - this.prettyName = prettyName; - this.keybind = KeybindMulti.fromStorageString(defaultHotkey, settings); - this.keybind.setCallback(new KeyCallbackToggleBooleanConfigWithMessage(this)); - } - - @Override - public ConfigType getType() - { - return ConfigType.HOTKEY; - } - - @Override - public String getName() - { - return this.name; - } - - @Override - public String getConfigGuiDisplayName() - { - String name = StringUtils.getTranslatedOrFallback("config.name." + this.getName().toLowerCase(), this.getName()); - - if (this.singlePlayer) - { - return GuiBase.TXT_GOLD + name + GuiBase.TXT_RST; - } - - return name; - } - - @Override - public String getPrettyName() - { - return this.prettyName; - } - - @Override - public String getStringValue() - { - return String.valueOf(this.valueBoolean); - } - - @Override - public String getDefaultStringValue() - { - return String.valueOf(this.defaultValueBoolean); - } - - @Override - public void setValueFromString(String value) - { - } + return name; + } - @Override - public void onValueChanged() - { - if (this.callback != null) - { - this.callback.onValueChanged(this); - } - } + @Override + public String getPrettyName() { + return this.prettyName; + } - @Override - public void setValueChangeCallback(IValueChangeCallback callback) - { - this.callback = callback; - } + @Override + public String getStringValue() { + return String.valueOf(this.valueBoolean); + } - @Override - public String getComment() - { - String comment = StringUtils.getTranslatedOrFallback("config.comment." + this.getName().toLowerCase(), this.comment); + @Override + public String getDefaultStringValue() { + return String.valueOf(this.defaultValueBoolean); + } - if (comment != null && this.singlePlayer) - { - return comment + "\n" + StringUtils.translate("tweakeroo.label.config_comment.single_player_only"); - } + @Override + public void setValueFromString(String value) { + } - return comment; + @Override + public void onValueChanged() { + if (this.callback != null) { + this.callback.onValueChanged(this); } + } - @Override - public IKeybind getKeybind() - { - return this.keybind; - } + @Override + public void setValueChangeCallback(IValueChangeCallback callback) { + this.callback = callback; + } - @Override - public boolean getBooleanValue() - { - return this.valueBoolean; - } + @Override + public String getComment() { + String comment = StringUtils.getTranslatedOrFallback("config.comment." + this.getName().toLowerCase(), this.comment); - @Override - public boolean getDefaultBooleanValue() - { - return this.defaultValueBoolean; + if (comment != null && this.singlePlayer) { + return comment + "\n" + StringUtils.translate("tweakeroo.label.config_comment.single_player_only"); } - @Override - public void setBooleanValue(boolean value) - { - boolean oldValue = this.valueBoolean; - this.valueBoolean = value; + return comment; + } - if (oldValue != this.valueBoolean) - { - this.onValueChanged(); - } - } + @Override + public IKeybind getKeybind() { + return this.keybind; + } - @Override - public boolean isModified() - { - return this.valueBoolean != this.defaultValueBoolean; - } + @Override + public boolean getBooleanValue() { + return this.valueBoolean; + } - @Override - public boolean isModified(String newValue) - { - return Boolean.parseBoolean(newValue) != this.defaultValueBoolean; - } + @Override + public boolean getDefaultBooleanValue() { + return this.defaultValueBoolean; + } - @Override - public void resetToDefault() - { - this.valueBoolean = this.defaultValueBoolean; - } + @Override + public void setBooleanValue(boolean value) { + boolean oldValue = this.valueBoolean; + this.valueBoolean = value; - @Override - public JsonElement getAsJsonElement() - { - return new JsonPrimitive(this.valueBoolean); + if (oldValue != this.valueBoolean) { + this.onValueChanged(); } - - @Override - public void setValueFromJsonElement(JsonElement element) - { - try - { - if (element.isJsonPrimitive()) - { - this.valueBoolean = element.getAsBoolean(); - } - else - { - Tweakeroo.logger.warn("Failed to set config value for '{}' from the JSON element '{}'", this.getName(), element); - } - } - catch (Exception e) - { - Tweakeroo.logger.warn("Failed to set config value for '{}' from the JSON element '{}'", this.getName(), element, e); - } + } + + @Override + public boolean isModified() { + return this.valueBoolean != this.defaultValueBoolean; + } + + @Override + public boolean isModified(String newValue) { + return Boolean.parseBoolean(newValue) != this.defaultValueBoolean; + } + + @Override + public void resetToDefault() { + this.valueBoolean = this.defaultValueBoolean; + } + + @Override + public JsonElement getAsJsonElement() { + return new JsonPrimitive(this.valueBoolean); + } + + @Override + public void setValueFromJsonElement(JsonElement element) { + try { + if (element.isJsonPrimitive()) { + this.valueBoolean = element.getAsBoolean(); + } else { + Tweakeroo.logger.warn("Failed to set config value for '{}' from the JSON element '{}'", this.getName(), element); + } + } catch (Exception e) { + Tweakeroo.logger.warn("Failed to set config value for '{}' from the JSON element '{}'", this.getName(), element, e); } + } } diff --git a/src/main/java/fi/dy/masa/tweakeroo/mixin/MixinClientPlayerEntity.java b/src/main/java/fi/dy/masa/tweakeroo/mixin/MixinClientPlayerEntity.java index d628504..92fc778 100644 --- a/src/main/java/fi/dy/masa/tweakeroo/mixin/MixinClientPlayerEntity.java +++ b/src/main/java/fi/dy/masa/tweakeroo/mixin/MixinClientPlayerEntity.java @@ -1,9 +1,15 @@ package fi.dy.masa.tweakeroo.mixin; import com.mojang.authlib.GameProfile; +import fi.dy.masa.tweakeroo.util.InventoryUtils; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -33,9 +39,11 @@ public abstract class MixinClientPlayerEntity extends AbstractClientPlayerEntity @Shadow public float prevNauseaIntensity; @Shadow public float nauseaIntensity; + @Shadow private boolean falling; private final DummyMovementInput dummyMovementInput = new DummyMovementInput(null); private Input realInput; private float realNauseaIntensity; + @Unique private ItemStack autoSwitchElytraChestplate = ItemStack.EMPTY; private MixinClientPlayerEntity(ClientWorld world, GameProfile profile) { @@ -118,6 +126,63 @@ private void disableDoubleTapSprint(CallbackInfo ci) } } + @Inject(method = "tickMovement", + at = @At(value = "INVOKE", shift = At.Shift.BEFORE, + target = "Lnet/minecraft/client/network/ClientPlayerEntity;getEquippedStack(Lnet/minecraft/entity/EquipmentSlot;)Lnet/minecraft/item/ItemStack;")) + private void onFallFlyingCheckChestSlot(CallbackInfo ci) + { + if (FeatureToggle.TWEAK_AUTO_SWITCH_ELYTRA.getBooleanValue()) + { + // PlayerEntity#checkFallFlying + if (!this.isOnGround() && !this.isFallFlying() && !this.isInFluidType() && !this.hasStatusEffect(StatusEffects.LEVITATION)) + { + if (!this.getEquippedStack(EquipmentSlot.CHEST).isOf(Items.ELYTRA) || + this.getEquippedStack(EquipmentSlot.CHEST).getDamage() > this.getEquippedStack(EquipmentSlot.CHEST).getMaxDamage() - 10) + { + InventoryUtils.equipBestElytra(this); + } + } + } + else + { + // reset auto switch item if the feature is disabled. + this.autoSwitchElytraChestplate = ItemStack.EMPTY; + } + } + + + @Inject(method = "onTrackedDataSet", at = @At("RETURN")) + private void onStopFlying(TrackedData data, CallbackInfo ci) + { + if (FeatureToggle.TWEAK_AUTO_SWITCH_ELYTRA.getBooleanValue()) + { + if (FLAGS.equals(data) && this.falling) + { + if (!this.isFallFlying() && this.getEquippedStack(EquipmentSlot.CHEST).isOf(Items.ELYTRA)) + { + if (!this.autoSwitchElytraChestplate.isEmpty() && !this.autoSwitchElytraChestplate.isOf(Items.ELYTRA)) + { + if (this.playerScreenHandler.getCursorStack().isEmpty()) + { + int targetSlot = InventoryUtils.findSlotWithItem(this.playerScreenHandler, this.autoSwitchElytraChestplate, true, false); + + if (targetSlot >= 0) + { + InventoryUtils.swapItemToEquipmentSlot(this, EquipmentSlot.CHEST, targetSlot); + this.autoSwitchElytraChestplate = ItemStack.EMPTY; + } + } + } + else + { + // if cached previous item is empty, try to swap back to the default chest plate. + InventoryUtils.swapElytraAndChestPlate(this); + } + } + } + } + } + @Inject(method = "tick", at = @At("HEAD")) private void disableMovementInputsPre(CallbackInfo ci) { diff --git a/src/main/java/fi/dy/masa/tweakeroo/util/InventoryUtils.java b/src/main/java/fi/dy/masa/tweakeroo/util/InventoryUtils.java index d894630..dab9d25 100644 --- a/src/main/java/fi/dy/masa/tweakeroo/util/InventoryUtils.java +++ b/src/main/java/fi/dy/masa/tweakeroo/util/InventoryUtils.java @@ -1,33 +1,27 @@ package fi.dy.masa.tweakeroo.util; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.annotation.Nullable; - +import com.google.common.collect.Multimap; +import fi.dy.masa.malilib.gui.Message; +import fi.dy.masa.malilib.util.Constants; +import fi.dy.masa.malilib.util.GuiUtils; +import fi.dy.masa.malilib.util.InfoUtils; +import fi.dy.masa.tweakeroo.Tweakeroo; +import fi.dy.masa.tweakeroo.config.Configs; +import fi.dy.masa.tweakeroo.config.FeatureToggle; +import fi.dy.masa.tweakeroo.tweaks.PlacementTweaks; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; +import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.attribute.EntityAttribute; +import net.minecraft.entity.attribute.EntityAttributeModifier; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.item.ArmorItem; -import net.minecraft.item.ElytraItem; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.item.MiningToolItem; -import net.minecraft.item.SwordItem; -import net.minecraft.item.ToolItem; +import net.minecraft.item.*; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; import net.minecraft.registry.Registries; @@ -44,17 +38,14 @@ import net.minecraft.util.math.intprovider.UniformIntProvider; import net.minecraft.world.World; -import fi.dy.masa.malilib.gui.Message; -import fi.dy.masa.malilib.util.Constants; -import fi.dy.masa.malilib.util.GuiUtils; -import fi.dy.masa.malilib.util.InfoUtils; -import fi.dy.masa.tweakeroo.Tweakeroo; -import fi.dy.masa.tweakeroo.config.Configs; -import fi.dy.masa.tweakeroo.config.FeatureToggle; -import fi.dy.masa.tweakeroo.tweaks.PlacementTweaks; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -public class InventoryUtils -{ +public class InventoryUtils { private static final List REPAIR_MODE_SLOTS = new ArrayList<>(); private static final List REPAIR_MODE_SLOT_NUMBERS = new ArrayList<>(); private static final HashSet UNSTACKING_ITEMS = new HashSet<>(); @@ -62,134 +53,117 @@ public class InventoryUtils private static final List TOOL_SWITCH_IGNORED_SLOTS = new ArrayList<>(); private static final HashMap, HashSet> WEAPON_MAPPING = new HashMap<>(); - public static void setToolSwitchableSlots(String configStr) - { + public static void setToolSwitchableSlots(String configStr) { parseSlotsFromString(configStr, TOOL_SWITCHABLE_SLOTS); } - public static void setToolSwitchIgnoreSlots(String configStr) - { + public static void setToolSwitchIgnoreSlots(String configStr) { parseSlotsFromString(configStr, TOOL_SWITCH_IGNORED_SLOTS); } - public static void parseSlotsFromString(String configStr, Collection output) - { + public static void parseSlotsFromString(String configStr, Collection output) { String[] parts = configStr.split(","); Pattern patternRange = Pattern.compile("^(?[0-9])-(?[0-9])$"); output.clear(); - if (configStr.isBlank()) - { + if (configStr.isBlank()) { return; } - for (String str : parts) - { - try - { + for (String str : parts) { + try { Matcher matcher = patternRange.matcher(str); - if (matcher.matches()) - { + if (matcher.matches()) { int slotStart = Integer.parseInt(matcher.group("start")) - 1; int slotEnd = Integer.parseInt(matcher.group("end")) - 1; if (slotStart <= slotEnd && - PlayerInventory.isValidHotbarIndex(slotStart) && - PlayerInventory.isValidHotbarIndex(slotEnd)) - { - for (int slotNum = slotStart; slotNum <= slotEnd; ++slotNum) - { - if (output.contains(slotNum) == false) - { + PlayerInventory.isValidHotbarIndex(slotStart) && + PlayerInventory.isValidHotbarIndex(slotEnd)) { + for (int slotNum = slotStart; slotNum <= slotEnd; ++slotNum) { + if (output.contains(slotNum) == false) { output.add(slotNum); } } } - } - else - { + } else { int slotNum = Integer.parseInt(str) - 1; - if (PlayerInventory.isValidHotbarIndex(slotNum) && output.contains(slotNum) == false) - { + if (PlayerInventory.isValidHotbarIndex(slotNum) && output.contains(slotNum) == false) { output.add(slotNum); } } - } - catch (NumberFormatException ignore) - { + } catch (NumberFormatException ignore) { InfoUtils.showGuiOrInGameMessage(Message.MessageType.ERROR, "Failed to parse slots from string %s", configStr); } } } - public static void setUnstackingItems(List names) - { + public static void setUnstackingItems(List names) { UNSTACKING_ITEMS.clear(); - for (String name : names) - { - try - { + for (String name : names) { + try { Item item = Registries.ITEM.get(new Identifier(name)); - if (item != null && item != Items.AIR) - { + if (item != null && item != Items.AIR) { UNSTACKING_ITEMS.add(item); } - } - catch (Exception e) - { + } catch (Exception e) { Tweakeroo.logger.warn("Failed to set an unstacking protected item from name '{}'", name, e); } } } - public static void setRepairModeSlots(List names) - { + public static void setRepairModeSlots(List names) { REPAIR_MODE_SLOTS.clear(); REPAIR_MODE_SLOT_NUMBERS.clear(); - for (String name : names) - { + for (String name : names) { EquipmentSlot type = null; - switch (name) - { - case "mainhand": type = EquipmentSlot.MAINHAND; break; - case "offhand": type = EquipmentSlot.OFFHAND; break; - case "head": type = EquipmentSlot.HEAD; break; - case "chest": type = EquipmentSlot.CHEST; break; - case "legs": type = EquipmentSlot.LEGS; break; - case "feet": type = EquipmentSlot.FEET; break; + switch (name) { + case "mainhand": + type = EquipmentSlot.MAINHAND; + break; + case "offhand": + type = EquipmentSlot.OFFHAND; + break; + case "head": + type = EquipmentSlot.HEAD; + break; + case "chest": + type = EquipmentSlot.CHEST; + break; + case "legs": + type = EquipmentSlot.LEGS; + break; + case "feet": + type = EquipmentSlot.FEET; + break; } - if (type != null) - { + if (type != null) { REPAIR_MODE_SLOTS.add(type); int slotNum = getSlotNumberForEquipmentType(type, null); - if (slotNum >= 0) - { + if (slotNum >= 0) { REPAIR_MODE_SLOT_NUMBERS.add(slotNum); } } } } - public static void setWeaponMapping(List mappings) - { + public static void setWeaponMapping(List mappings) { WEAPON_MAPPING.clear(); - for (String mapping : mappings) - { + for (String mapping : mappings) { String[] split = mapping.replaceAll(" ", "").split("=>"); - if (split.length != 2) - { + if (split.length != 2) { Tweakeroo.logger.warn("Expected weapon mapping to be `entity_ids => weapon_ids` got '{}'", mapping); continue; } @@ -197,46 +171,36 @@ public static void setWeaponMapping(List mappings) HashSet weapons = new HashSet<>(); String entities = split[0].trim(); String items = split[1].trim(); - - if (items.equals("") == false) - { - for (String itemId : items.split(",")) - { - try - { + + if (items.equals("") == false) { + for (String itemId : items.split(",")) { + try { Optional weapon = Registries.ITEM.getOrEmpty(new Identifier(itemId)); - if (weapon.isPresent()) - { + if (weapon.isPresent()) { weapons.add(weapon.get()); continue; } + } catch (Exception ignore) { } - catch (Exception ignore) {} Tweakeroo.logger.warn("Unable to find item to use as weapon: '{}'", itemId); } } - if (entities.equalsIgnoreCase("")) - { + if (entities.equalsIgnoreCase("")) { WEAPON_MAPPING.computeIfAbsent(null, s -> new HashSet<>()).addAll(weapons); - } - else - { - for (String entity_id : entities.split(",")) - { - try - { + } else { + for (String entity_id : entities.split(",")) { + try { Optional> entity = Registries.ENTITY_TYPE.getOrEmpty(new Identifier(entity_id)); - if (entity.isPresent()) - { + if (entity.isPresent()) { WEAPON_MAPPING.computeIfAbsent(entity.get(), s -> new HashSet<>()).addAll(weapons); continue; } + } catch (Exception ignore) { } - catch (Exception ignore) {} Tweakeroo.logger.warn("Unable to find entity: '{}'", entity_id); } @@ -244,11 +208,9 @@ public static void setWeaponMapping(List mappings) } } - private static boolean isConfiguredRepairSlot(int slotNum, PlayerEntity player) - { + private static boolean isConfiguredRepairSlot(int slotNum, PlayerEntity player) { if (REPAIR_MODE_SLOTS.contains(EquipmentSlot.MAINHAND) && - (slotNum - 36) == player.getInventory().selectedSlot) - { + (slotNum - 36) == player.getInventory().selectedSlot) { return true; } @@ -260,21 +222,23 @@ private static boolean isConfiguredRepairSlot(int slotNum, PlayerEntity player) * assuming that the slot number is for the player's main inventory container */ @Nullable - private static EquipmentSlot getEquipmentTypeForSlot(int slotNum, PlayerEntity player) - { + private static EquipmentSlot getEquipmentTypeForSlot(int slotNum, PlayerEntity player) { if (REPAIR_MODE_SLOTS.contains(EquipmentSlot.MAINHAND) && - (slotNum - 36) == player.getInventory().selectedSlot) - { + (slotNum - 36) == player.getInventory().selectedSlot) { return EquipmentSlot.MAINHAND; } - switch (slotNum) - { - case 45: return EquipmentSlot.OFFHAND; - case 5: return EquipmentSlot.HEAD; - case 6: return EquipmentSlot.CHEST; - case 7: return EquipmentSlot.LEGS; - case 8: return EquipmentSlot.FEET; + switch (slotNum) { + case 45: + return EquipmentSlot.OFFHAND; + case 5: + return EquipmentSlot.HEAD; + case 6: + return EquipmentSlot.CHEST; + case 7: + return EquipmentSlot.LEGS; + case 8: + return EquipmentSlot.FEET; } return null; @@ -284,85 +248,77 @@ private static EquipmentSlot getEquipmentTypeForSlot(int slotNum, PlayerEntity p * Returns the slot number for the given equipment type * in the player's inventory container */ - private static int getSlotNumberForEquipmentType(EquipmentSlot type, @Nullable PlayerEntity player) - { - switch (type) - { - case MAINHAND: return player != null ? player.getInventory().selectedSlot + 36 : -1; - case OFFHAND: return 45; - case HEAD: return 5; - case CHEST: return 6; - case LEGS: return 7; - case FEET: return 8; + private static int getSlotNumberForEquipmentType(EquipmentSlot type, @Nullable PlayerEntity player) { + switch (type) { + case MAINHAND: + return player != null ? player.getInventory().selectedSlot + 36 : -1; + case OFFHAND: + return 45; + case HEAD: + return 5; + case CHEST: + return 6; + case LEGS: + return 7; + case FEET: + return 8; } return -1; } - public static void swapHotbarWithInventoryRow(PlayerEntity player, int row) - { + public static void swapHotbarWithInventoryRow(PlayerEntity player, int row) { ScreenHandler container = player.playerScreenHandler; row = MathHelper.clamp(row, 0, 2); int slot = row * 9 + 9; - for (int hotbarSlot = 0; hotbarSlot < 9; hotbarSlot++) - { + for (int hotbarSlot = 0; hotbarSlot < 9; hotbarSlot++) { fi.dy.masa.malilib.util.InventoryUtils.swapSlots(container, slot, hotbarSlot); slot++; } } - public static void restockNewStackToHand(PlayerEntity player, Hand hand, ItemStack stackReference, boolean allowHotbar) - { + public static void restockNewStackToHand(PlayerEntity player, Hand hand, ItemStack stackReference, boolean allowHotbar) { int slotWithItem; - if (stackReference.getItem().isDamageable()) - { + if (stackReference.getItem().isDamageable()) { int minDurability = getMinDurability(stackReference); slotWithItem = findSlotWithSuitableReplacementToolWithDurabilityLeft(player.playerScreenHandler, stackReference, minDurability); - } - else - { + } else { slotWithItem = findSlotWithItem(player.playerScreenHandler, stackReference, allowHotbar, true); } - if (slotWithItem != -1) - { + if (slotWithItem != -1) { swapItemToHand(player, hand, slotWithItem); } } - public static void preRestockHand(PlayerEntity player, Hand hand, boolean allowHotbar) - { + public static void preRestockHand(PlayerEntity player, Hand hand, boolean allowHotbar) { ItemStack stackHand = player.getStackInHand(hand); int threshold = Configs.Generic.HAND_RESTOCK_PRE_THRESHOLD.getIntegerValue(); if (FeatureToggle.TWEAK_HAND_RESTOCK.getBooleanValue() && - Configs.Generic.HAND_RESTOCK_PRE.getBooleanValue() && - stackHand.isEmpty() == false && - stackHand.getCount() <= threshold && stackHand.getMaxCount() > threshold && - PlacementTweaks.canUseItemWithRestriction(PlacementTweaks.HAND_RESTOCK_RESTRICTION, stackHand) && - player.currentScreenHandler == player.playerScreenHandler && - player.currentScreenHandler.getCursorStack().isEmpty()) - { + Configs.Generic.HAND_RESTOCK_PRE.getBooleanValue() && + stackHand.isEmpty() == false && + stackHand.getCount() <= threshold && stackHand.getMaxCount() > threshold && + PlacementTweaks.canUseItemWithRestriction(PlacementTweaks.HAND_RESTOCK_RESTRICTION, stackHand) && + player.currentScreenHandler == player.playerScreenHandler && + player.currentScreenHandler.getCursorStack().isEmpty()) { MinecraftClient mc = MinecraftClient.getInstance(); ScreenHandler container = player.playerScreenHandler; int endSlot = allowHotbar ? 44 : 35; int currentMainHandSlot = player.getInventory().selectedSlot + 36; int currentSlot = hand == Hand.MAIN_HAND ? currentMainHandSlot : 45; - for (int slotNum = 9; slotNum <= endSlot; ++slotNum) - { - if (slotNum == currentMainHandSlot) - { + for (int slotNum = 9; slotNum <= endSlot; ++slotNum) { + if (slotNum == currentMainHandSlot) { continue; } Slot slot = container.slots.get(slotNum); ItemStack stackSlot = slot.getStack(); - if (fi.dy.masa.malilib.util.InventoryUtils.areStacksEqualIgnoreDurability(stackSlot, stackHand)) - { + if (fi.dy.masa.malilib.util.InventoryUtils.areStacksEqualIgnoreDurability(stackSlot, stackHand)) { // If all the items from the found slot can fit into the current // stack in hand, then left click, otherwise right click to split the stack int button = stackSlot.getCount() + stackHand.getCount() <= stackHand.getMaxCount() ? 0 : 1; @@ -376,188 +332,150 @@ public static void preRestockHand(PlayerEntity player, Hand hand, boolean allowH } } - public static void trySwapCurrentToolIfNearlyBroken() - { + public static void trySwapCurrentToolIfNearlyBroken() { PlayerEntity player = MinecraftClient.getInstance().player; - if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue() && player != null) - { + if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue() && player != null) { trySwapCurrentToolIfNearlyBroken(Hand.MAIN_HAND, player); trySwapCurrentToolIfNearlyBroken(Hand.OFF_HAND, player); } } - public static void trySwapCurrentToolIfNearlyBroken(Hand hand, PlayerEntity player) - { + public static void trySwapCurrentToolIfNearlyBroken(Hand hand, PlayerEntity player) { ItemStack stack = player.getStackInHand(hand); - if (stack.isEmpty() == false) - { + if (stack.isEmpty() == false) { int minDurability = getMinDurability(stack); - if (isItemAtLowDurability(stack, minDurability)) - { + if (isItemAtLowDurability(stack, minDurability)) { swapItemWithHigherDurabilityToHand(player, hand, stack, minDurability + 1); } } } - public static void trySwitchToWeapon(Entity entity) - { + public static void trySwitchToWeapon(Entity entity) { MinecraftClient mc = MinecraftClient.getInstance(); PlayerEntity player = mc.player; if (player != null && mc.world != null && - TOOL_SWITCH_IGNORED_SLOTS.contains(player.getInventory().selectedSlot) == false) - { + TOOL_SWITCH_IGNORED_SLOTS.contains(player.getInventory().selectedSlot) == false) { ScreenHandler container = player.playerScreenHandler; ItemPickerTest test; - if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue()) - { + if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue()) { test = (currentStack, previous) -> InventoryUtils.isBetterWeaponAndHasDurability(currentStack, previous, entity); - } - else - { + } else { test = (currentStack, previous) -> InventoryUtils.isBetterWeapon(currentStack, previous, entity); } int slotNumber = findSlotWithBestItemMatch(container, test, UniformIntProvider.create(36, 44), UniformIntProvider.create(9, 35)); - if (slotNumber != -1 && (slotNumber - 36) != player.getInventory().selectedSlot) - { + if (slotNumber != -1 && (slotNumber - 36) != player.getInventory().selectedSlot) { swapToolToHand(slotNumber, mc); PlacementTweaks.cacheStackInHand(Hand.MAIN_HAND); } } } - private static boolean isBetterWeapon(ItemStack testedStack, ItemStack previousWeapon, Entity entity) - { + private static boolean isBetterWeapon(ItemStack testedStack, ItemStack previousWeapon, Entity entity) { return testedStack.isEmpty() == false && matchesWeaponMapping(testedStack, entity) && (makesMoreDamage(testedStack, previousWeapon) || matchesWeaponMapping(previousWeapon, entity) == false); } - private static boolean isBetterWeaponAndHasDurability(ItemStack testedStack, ItemStack previousTool, Entity entity) - { + private static boolean isBetterWeaponAndHasDurability(ItemStack testedStack, ItemStack previousTool, Entity entity) { return hasEnoughDurability(testedStack) && isBetterWeapon(testedStack, previousTool, entity); } - private static boolean makesMoreDamage(ItemStack testedStack, ItemStack previousTool) - { + private static boolean makesMoreDamage(ItemStack testedStack, ItemStack previousTool) { return getBaseAttackDamage(testedStack) > getBaseAttackDamage(previousTool); } - private static float getBaseAttackDamage(ItemStack stack) - { + private static float getBaseAttackDamage(ItemStack stack) { Item item = stack.getItem(); - if (item instanceof SwordItem) - { + if (item instanceof SwordItem) { return ((SwordItem) item).getAttackDamage(); - } - else if (item instanceof MiningToolItem) - { + } else if (item instanceof MiningToolItem) { return ((MiningToolItem) item).getAttackDamage(); } return 0F; } - protected static boolean matchesWeaponMapping(ItemStack stack, Entity entity) - { + protected static boolean matchesWeaponMapping(ItemStack stack, Entity entity) { HashSet weapons = WEAPON_MAPPING.getOrDefault(entity.getType(), WEAPON_MAPPING.get(null)); return weapons != null && weapons.contains(stack.getItem()); } - public static void trySwitchToEffectiveTool(BlockPos pos) - { + public static void trySwitchToEffectiveTool(BlockPos pos) { MinecraftClient mc = MinecraftClient.getInstance(); PlayerEntity player = mc.player; if (player != null && mc.world != null && - TOOL_SWITCH_IGNORED_SLOTS.contains(player.getInventory().selectedSlot) == false) - { + TOOL_SWITCH_IGNORED_SLOTS.contains(player.getInventory().selectedSlot) == false) { BlockState state = mc.world.getBlockState(pos); ScreenHandler container = player.playerScreenHandler; ItemPickerTest test; - if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue()) - { + if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue()) { test = (currentStack, previous) -> InventoryUtils.isBetterToolAndHasDurability(currentStack, previous, state); - } - else - { + } else { test = (currentStack, previous) -> InventoryUtils.isBetterTool(currentStack, previous, state); } int slotNumber = findSlotWithBestItemMatch(container, test, UniformIntProvider.create(36, 44), UniformIntProvider.create(9, 35)); - if (slotNumber != -1 && (slotNumber - 36) != player.getInventory().selectedSlot) - { + if (slotNumber != -1 && (slotNumber - 36) != player.getInventory().selectedSlot) { swapToolToHand(slotNumber, mc); } } } - private static boolean isBetterTool(ItemStack testedStack, ItemStack previousTool, BlockState state) - { + private static boolean isBetterTool(ItemStack testedStack, ItemStack previousTool, BlockState state) { return testedStack.isEmpty() == false && isMoreEffectiveTool(testedStack, previousTool, state); } - private static boolean isBetterToolAndHasDurability(ItemStack testedStack, ItemStack previousTool, BlockState state) - { + private static boolean isBetterToolAndHasDurability(ItemStack testedStack, ItemStack previousTool, BlockState state) { return hasEnoughDurability(testedStack) && isBetterTool(testedStack, previousTool, state); } - private static boolean isMoreEffectiveTool(ItemStack testedStack, ItemStack previousTool, BlockState state) - { + private static boolean isMoreEffectiveTool(ItemStack testedStack, ItemStack previousTool, BlockState state) { return getBaseBlockBreakingSpeed(testedStack, state) > getBaseBlockBreakingSpeed(previousTool, state); } - protected static float getBaseBlockBreakingSpeed(ItemStack stack, BlockState state) - { + protected static float getBaseBlockBreakingSpeed(ItemStack stack, BlockState state) { float speed = stack.getMiningSpeedMultiplier(state); - if (speed > 1.0f) - { + if (speed > 1.0f) { int effLevel = EnchantmentHelper.getLevel(Enchantments.EFFICIENCY, stack); - if (effLevel > 0) - { + if (effLevel > 0) { speed += (effLevel * effLevel) + 1; } } - if (state.isToolRequired() && stack.isSuitableFor(state) == false) - { + if (state.isToolRequired() && stack.isSuitableFor(state) == false) { speed /= (100F / 30F); } return speed; } - protected static boolean hasEnoughDurability(ItemStack stack) - { + protected static boolean hasEnoughDurability(ItemStack stack) { return stack.getMaxDamage() - stack.getDamage() > getMinDurability(stack); } - private static int findSuitableSlot(ScreenHandler container, Predicate itemTest) - { + private static int findSuitableSlot(ScreenHandler container, Predicate itemTest) { return findSuitableSlot(container, itemTest, UniformIntProvider.create(9, container.slots.size() - 1)); } - private static int findSuitableSlot(ScreenHandler container, Predicate itemTest, UniformIntProvider... ranges) - { + private static int findSuitableSlot(ScreenHandler container, Predicate itemTest, UniformIntProvider... ranges) { final int max = container.slots.size() - 1; - for (UniformIntProvider range : ranges) - { + for (UniformIntProvider range : ranges) { int end = Math.min(max, range.getMax()); - for (int slotNumber = range.getMin(); slotNumber <= end; ++slotNumber) - { - if (itemTest.test(container.getSlot(slotNumber).getStack())) - { + for (int slotNumber = range.getMin(); slotNumber <= end; ++slotNumber) { + if (itemTest.test(container.getSlot(slotNumber).getStack())) { return slotNumber; } } @@ -566,22 +484,18 @@ private static int findSuitableSlot(ScreenHandler container, Predicate slotNumbers) - { + private static int findEmptySlot(ScreenHandler container, Collection slotNumbers) { final int maxSlot = container.slots.size() - 1; - for (int slotNumber : slotNumbers) - { + for (int slotNumber : slotNumbers) { if (slotNumber >= 0 && slotNumber <= maxSlot && - container.getSlot(slotNumber).hasStack() == false) - { + container.getSlot(slotNumber).hasStack() == false) { return slotNumber; } } @@ -607,20 +518,16 @@ private static int findEmptySlot(ScreenHandler container, Collection sl return -1; } - public interface ItemPickerTest - { + public interface ItemPickerTest { boolean isBetterMatch(ItemStack testedStack, ItemStack previousBestMatch); } - private static boolean isItemAtLowDurability(ItemStack stack, int minDurability) - { + private static boolean isItemAtLowDurability(ItemStack stack, int minDurability) { return stack.isDamageable() && (stack.getMaxDamage() - stack.getDamage()) <= minDurability; } - private static int getMinDurability(ItemStack stack) - { - if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue() == false) - { + private static int getMinDurability(ItemStack stack) { + if (FeatureToggle.TWEAK_SWAP_ALMOST_BROKEN_TOOLS.getBooleanValue() == false) { return 0; } @@ -629,21 +536,18 @@ private static int getMinDurability(ItemStack stack) // For items with low maximum durability, use 8% as the threshold, // if the configured durability threshold is over that. if (stack.getMaxDamage() <= 100 && minDurability <= 20 && - (double) minDurability / (double) stack.getMaxDamage() > 0.08) - { + (double) minDurability / (double) stack.getMaxDamage() > 0.08) { minDurability = (int) Math.ceil(stack.getMaxDamage() * 0.08); } return minDurability; } - private static void swapItemWithHigherDurabilityToHand(PlayerEntity player, Hand hand, ItemStack stackReference, int minDurabilityLeft) - { + private static void swapItemWithHigherDurabilityToHand(PlayerEntity player, Hand hand, ItemStack stackReference, int minDurabilityLeft) { ScreenHandler container = player.playerScreenHandler; int slotWithItem = findSlotWithSuitableReplacementToolWithDurabilityLeft(container, stackReference, minDurabilityLeft); - if (slotWithItem != -1) - { + if (slotWithItem != -1) { swapItemToHand(player, hand, slotWithItem); InfoUtils.printActionbarMessage("tweakeroo.message.swapped_low_durability_item_for_better_durability"); return; @@ -651,8 +555,7 @@ private static void swapItemWithHigherDurabilityToHand(PlayerEntity player, Hand slotWithItem = fi.dy.masa.malilib.util.InventoryUtils.findEmptySlotInPlayerInventory(container, false, false); - if (slotWithItem != -1) - { + if (slotWithItem != -1) { swapItemToHand(player, hand, slotWithItem); InfoUtils.printActionbarMessage("tweakeroo.message.swapped_low_durability_item_off_players_hand"); return; @@ -660,66 +563,54 @@ private static void swapItemWithHigherDurabilityToHand(PlayerEntity player, Hand slotWithItem = findSuitableSlot(container, (s) -> s.isDamageable() == false); - if (slotWithItem != -1) - { + if (slotWithItem != -1) { swapItemToHand(player, hand, slotWithItem); InfoUtils.printActionbarMessage("tweakeroo.message.swapped_low_durability_item_for_dummy_item"); } } - public static void repairModeSwapItems(PlayerEntity player) - { - if (player.currentScreenHandler == player.playerScreenHandler) - { - for (EquipmentSlot type : REPAIR_MODE_SLOTS) - { + public static void repairModeSwapItems(PlayerEntity player) { + if (player.currentScreenHandler == player.playerScreenHandler) { + for (EquipmentSlot type : REPAIR_MODE_SLOTS) { repairModeHandleSlot(player, type); } } } - private static void repairModeHandleSlot(PlayerEntity player, EquipmentSlot type) - { + private static void repairModeHandleSlot(PlayerEntity player, EquipmentSlot type) { int slotNum = getSlotNumberForEquipmentType(type, player); - if (slotNum == -1) - { + if (slotNum == -1) { return; } ItemStack stack = player.getEquippedStack(type); if (stack.isEmpty() == false && - (stack.isDamageable() == false || - stack.isDamaged() == false || - EnchantmentHelper.getLevel(Enchantments.MENDING, stack) <= 0)) - { + (stack.isDamageable() == false || + stack.isDamaged() == false || + EnchantmentHelper.getLevel(Enchantments.MENDING, stack) <= 0)) { Slot slot = player.currentScreenHandler.getSlot(slotNum); int slotRepairableItem = findRepairableItemNotInRepairableSlot(slot, player); - if (slotRepairableItem != -1) - { + if (slotRepairableItem != -1) { swapItemToEquipmentSlot(player, type, slotRepairableItem); InfoUtils.printActionbarMessage("tweakeroo.message.repair_mode.swapped_repairable_item_to_slot", type.getName()); } } } - private static int findRepairableItemNotInRepairableSlot(Slot targetSlot, PlayerEntity player) - { + private static int findRepairableItemNotInRepairableSlot(Slot targetSlot, PlayerEntity player) { ScreenHandler containerPlayer = player.currentScreenHandler; - for (Slot slot : containerPlayer.slots) - { - if (slot.hasStack() && isConfiguredRepairSlot(slot.id, player) == false) - { + for (Slot slot : containerPlayer.slots) { + if (slot.hasStack() && isConfiguredRepairSlot(slot.id, player) == false) { ItemStack stack = slot.getStack(); // Don't take items from the current hotbar slot if ((slot.id - 36) != player.getInventory().selectedSlot && - stack.isDamageable() && stack.isDamaged() && targetSlot.canInsert(stack) && - EnchantmentHelper.getLevel(Enchantments.MENDING, stack) > 0) - { + stack.isDamageable() && stack.isDamaged() && targetSlot.canInsert(stack) && + EnchantmentHelper.getLevel(Enchantments.MENDING, stack) > 0) { return slot.id; } } @@ -728,10 +619,8 @@ private static int findRepairableItemNotInRepairableSlot(Slot targetSlot, Player return -1; } - public static void swapElytraWithChestPlate(@Nullable PlayerEntity player) - { - if (player == null || GuiUtils.getCurrentScreen() != null) - { + public static void swapElytraWithChestPlate(@Nullable PlayerEntity player) { + if (player == null || GuiUtils.getCurrentScreen() != null) { return; } @@ -744,35 +633,31 @@ public static void swapElytraWithChestPlate(@Nullable PlayerEntity player) Predicate finalFilter = (s) -> s.isEmpty() == false && stackFilter.test(s) && s.getDamage() < s.getMaxDamage() - 10; int targetSlot = findSuitableSlot(container, finalFilter); - if (targetSlot >= 0) - { + if (targetSlot >= 0) { //targetSlots.sort(); swapItemToEquipmentSlot(player, EquipmentSlot.CHEST, targetSlot); } } /** - * * Finds a slot with an identical item than stackReference, ignoring the durability * of damageable items. Does not allow crafting or armor slots or the offhand slot * in the ContainerPlayer container. + * * @return the slot number, or -1 if none were found */ - public static int findSlotWithItem(ScreenHandler container, ItemStack stackReference, boolean allowHotbar, boolean reverse) - { + public static int findSlotWithItem(ScreenHandler container, ItemStack stackReference, boolean allowHotbar, boolean reverse) { final int startSlot = reverse ? container.slots.size() - 1 : 0; final int endSlot = reverse ? -1 : container.slots.size(); final int increment = reverse ? -1 : 1; final boolean isPlayerInv = container instanceof PlayerScreenHandler; - for (int slotNum = startSlot; slotNum != endSlot; slotNum += increment) - { + for (int slotNum = startSlot; slotNum != endSlot; slotNum += increment) { Slot slot = container.slots.get(slotNum); if ((isPlayerInv == false || fi.dy.masa.malilib.util.InventoryUtils.isRegularInventorySlot(slot.id, false)) && - (allowHotbar || isHotbarSlot(slot) == false) && - fi.dy.masa.malilib.util.InventoryUtils.areStacksEqualIgnoreDurability(slot.getStack(), stackReference)) - { + (allowHotbar || isHotbarSlot(slot) == false) && + fi.dy.masa.malilib.util.InventoryUtils.areStacksEqualIgnoreDurability(slot.getStack(), stackReference)) { return slot.id; } } @@ -780,51 +665,39 @@ public static int findSlotWithItem(ScreenHandler container, ItemStack stackRefer return -1; } - private static boolean isHotbarSlot(Slot slot) - { + private static boolean isHotbarSlot(Slot slot) { return isHotbarSlot(slot.id); } - private static boolean isHotbarSlot(int slot) - { + private static boolean isHotbarSlot(int slot) { return slot >= 36 && slot < (36 + PlayerInventory.getHotbarSize()); } - private static void swapItemToHand(PlayerEntity player, Hand hand, int slotNumber) - { + private static void swapItemToHand(PlayerEntity player, Hand hand, int slotNumber) { ScreenHandler container = player.currentScreenHandler; - if (slotNumber != -1 && container == player.playerScreenHandler) - { + if (slotNumber != -1 && container == player.playerScreenHandler) { MinecraftClient mc = MinecraftClient.getInstance(); PlayerInventory inventory = player.getInventory(); - if (hand == Hand.MAIN_HAND) - { + if (hand == Hand.MAIN_HAND) { int currentHotbarSlot = inventory.selectedSlot; - if (isHotbarSlot(slotNumber)) - { + if (isHotbarSlot(slotNumber)) { inventory.selectedSlot = slotNumber - 36; mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(inventory.selectedSlot)); - } - else - { + } else { mc.interactionManager.clickSlot(container.syncId, slotNumber, currentHotbarSlot, SlotActionType.SWAP, mc.player); } - } - else if (hand == Hand.OFF_HAND) - { + } else if (hand == Hand.OFF_HAND) { mc.interactionManager.clickSlot(container.syncId, slotNumber, 40, SlotActionType.SWAP, mc.player); } } } - private static void swapItemToEquipmentSlot(PlayerEntity player, EquipmentSlot type, int sourceSlotNumber) - { - if (sourceSlotNumber != -1 && player.currentScreenHandler == player.playerScreenHandler) - { + public static void swapItemToEquipmentSlot(PlayerEntity player, EquipmentSlot type, int sourceSlotNumber) { + if (sourceSlotNumber != -1 && player.currentScreenHandler == player.playerScreenHandler) { MinecraftClient mc = MinecraftClient.getInstance(); ScreenHandler container = player.playerScreenHandler; int equipmentSlotNumber = getSlotNumberForEquipmentType(type, player); @@ -834,29 +707,22 @@ private static void swapItemToEquipmentSlot(PlayerEntity player, EquipmentSlot t } } - private static void swapToolToHand(int slotNumber, MinecraftClient mc) - { + private static void swapToolToHand(int slotNumber, MinecraftClient mc) { PlayerEntity player = mc.player; - if (slotNumber >= 0 && player.currentScreenHandler == player.playerScreenHandler) - { + if (slotNumber >= 0 && player.currentScreenHandler == player.playerScreenHandler) { PlayerInventory inventory = player.getInventory(); ScreenHandler container = player.playerScreenHandler; - if (isHotbarSlot(slotNumber)) - { + if (isHotbarSlot(slotNumber)) { inventory.selectedSlot = slotNumber - 36; mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(inventory.selectedSlot)); - } - else - { + } else { int selectedSlot = inventory.selectedSlot; int hotbarSlot = getUsableHotbarSlotForTool(selectedSlot, TOOL_SWITCHABLE_SLOTS, container); - if (PlayerInventory.isValidHotbarIndex(hotbarSlot)) - { - if (hotbarSlot != selectedSlot) - { + if (PlayerInventory.isValidHotbarIndex(hotbarSlot)) { + if (hotbarSlot != selectedSlot) { inventory.selectedSlot = hotbarSlot; mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(inventory.selectedSlot)); } @@ -867,42 +733,34 @@ private static void swapToolToHand(int slotNumber, MinecraftClient mc) } } - private static int getUsableHotbarSlotForTool(int currentHotbarSlot, Collection validSlots, ScreenHandler container) - { + private static int getUsableHotbarSlotForTool(int currentHotbarSlot, Collection validSlots, ScreenHandler container) { int first = -1; int nonTool = -1; - if (validSlots.contains(currentHotbarSlot)) - { + if (validSlots.contains(currentHotbarSlot)) { ItemStack stack = container.getSlot(currentHotbarSlot + 36).getStack(); - if (stack.isEmpty()) - { + if (stack.isEmpty()) { return currentHotbarSlot; } - if ((stack.getItem() instanceof ToolItem) == false) - { + if ((stack.getItem() instanceof ToolItem) == false) { nonTool = currentHotbarSlot; } } - for (int hotbarSlot : validSlots) - { + for (int hotbarSlot : validSlots) { ItemStack stack = container.getSlot(hotbarSlot + 36).getStack(); - if (stack.isEmpty()) - { + if (stack.isEmpty()) { return hotbarSlot; } - if (nonTool == -1 && (stack.getItem() instanceof ToolItem) == false) - { + if (nonTool == -1 && (stack.getItem() instanceof ToolItem) == false) { nonTool = hotbarSlot; } - if (first == -1) - { + if (first == -1) { first = hotbarSlot; } } @@ -910,18 +768,15 @@ private static int getUsableHotbarSlotForTool(int currentHotbarSlot, Collection< return nonTool >= 0 ? nonTool : first; } - private static int findSlotWithSuitableReplacementToolWithDurabilityLeft(ScreenHandler container, ItemStack stackReference, int minDurabilityLeft) - { - for (Slot slot : container.slots) - { + private static int findSlotWithSuitableReplacementToolWithDurabilityLeft(ScreenHandler container, ItemStack stackReference, int minDurabilityLeft) { + for (Slot slot : container.slots) { ItemStack stackSlot = slot.getStack(); // Only accept regular inventory slots (no crafting, armor slots, or offhand) if (fi.dy.masa.malilib.util.InventoryUtils.isRegularInventorySlot(slot.id, false) && - ItemStack.areItemsEqual(stackSlot, stackReference) && - stackSlot.getMaxDamage() - stackSlot.getDamage() >= minDurabilityLeft && - hasSameIshEnchantments(stackReference, stackSlot)) - { + ItemStack.areItemsEqual(stackSlot, stackReference) && + stackSlot.getMaxDamage() - stackSlot.getDamage() >= minDurabilityLeft && + hasSameIshEnchantments(stackReference, stackSlot)) { return slot.id; } } @@ -929,56 +784,46 @@ private static int findSlotWithSuitableReplacementToolWithDurabilityLeft(ScreenH return -1; } - private static boolean hasSameIshEnchantments(ItemStack stackReference, ItemStack stack) - { + private static boolean hasSameIshEnchantments(ItemStack stackReference, ItemStack stack) { int level = EnchantmentHelper.getLevel(Enchantments.SILK_TOUCH, stackReference); - if (level > 0) - { + if (level > 0) { return EnchantmentHelper.getLevel(Enchantments.SILK_TOUCH, stack) >= level; } level = EnchantmentHelper.getLevel(Enchantments.FORTUNE, stackReference); - if (level > 0) - { + if (level > 0) { return EnchantmentHelper.getLevel(Enchantments.FORTUNE, stack) >= level; } return true; } - private static int findSlotWithEffectiveItemWithDurabilityLeft(ScreenHandler container, BlockState state) - { + private static int findSlotWithEffectiveItemWithDurabilityLeft(ScreenHandler container, BlockState state) { int slotNum = -1; float bestSpeed = -1f; - for (Slot slot : container.slots) - { + for (Slot slot : container.slots) { // Don't consider armor and crafting slots - if (slot.id <= 8 || slot.hasStack() == false) - { + if (slot.id <= 8 || slot.hasStack() == false) { continue; } ItemStack stack = slot.getStack(); - if (stack.getMaxDamage() - stack.getDamage() > getMinDurability(stack)) - { + if (stack.getMaxDamage() - stack.getDamage() > getMinDurability(stack)) { float speed = stack.getMiningSpeedMultiplier(state); - if (speed > 1.0f) - { + if (speed > 1.0f) { int effLevel = EnchantmentHelper.getLevel(Enchantments.EFFICIENCY, stack); - if (effLevel > 0) - { + if (effLevel > 0) { speed += (effLevel * effLevel) + 1; } } - if (speed > 1f && (slotNum == -1 || speed > bestSpeed)) - { + if (speed > 1f && (slotNum == -1 || speed > bestSpeed)) { slotNum = slot.id; bestSpeed = speed; } @@ -988,72 +833,59 @@ private static int findSlotWithEffectiveItemWithDurabilityLeft(ScreenHandler con return slotNum; } - private static void tryCombineStacksInInventory(PlayerEntity player, ItemStack stackReference) - { + private static void tryCombineStacksInInventory(PlayerEntity player, ItemStack stackReference) { List slots = new ArrayList<>(); ScreenHandler container = player.playerScreenHandler; MinecraftClient mc = MinecraftClient.getInstance(); - for (Slot slot : container.slots) - { + for (Slot slot : container.slots) { // Inventory crafting and armor slots are not valid - if (slot.id < 8) - { + if (slot.id < 8) { continue; } ItemStack stack = slot.getStack(); - if (stack.getCount() < stack.getMaxCount() && fi.dy.masa.malilib.util.InventoryUtils.areStacksEqual(stackReference, stack)) - { + if (stack.getCount() < stack.getMaxCount() && fi.dy.masa.malilib.util.InventoryUtils.areStacksEqual(stackReference, stack)) { slots.add(slot); } } - for (int i = 0; i < slots.size(); ++i) - { + for (int i = 0; i < slots.size(); ++i) { Slot slot1 = slots.get(i); - for (int j = i + 1; j < slots.size(); ++j) - { + for (int j = i + 1; j < slots.size(); ++j) { Slot slot2 = slots.get(j); ItemStack stack = slot1.getStack(); - if (stack.getCount() < stack.getMaxCount()) - { + if (stack.getCount() < stack.getMaxCount()) { // Pick up the item from slot1 and try to put it in slot2 mc.interactionManager.clickSlot(container.syncId, slot1.id, 0, SlotActionType.PICKUP, player); mc.interactionManager.clickSlot(container.syncId, slot2.id, 0, SlotActionType.PICKUP, player); // If the items didn't all fit, return the rest - if (player.getInventory().getMainHandStack().isEmpty() == false) - { + if (player.getInventory().getMainHandStack().isEmpty() == false) { mc.interactionManager.clickSlot(container.syncId, slot1.id, 0, SlotActionType.PICKUP, player); } - if (slot2.getStack().getCount() >= slot2.getStack().getMaxCount()) - { + if (slot2.getStack().getCount() >= slot2.getStack().getMaxCount()) { slots.remove(j); --j; } } - if (slot1.hasStack() == false) - { + if (slot1.hasStack() == false) { break; } } } } - public static boolean canUnstackingItemNotFitInInventory(ItemStack stack, PlayerEntity player) - { + public static boolean canUnstackingItemNotFitInInventory(ItemStack stack, PlayerEntity player) { if (FeatureToggle.TWEAK_ITEM_UNSTACKING_PROTECTION.getBooleanValue() && - stack.getCount() > 1 && - UNSTACKING_ITEMS.contains(stack.getItem())) - { - if (fi.dy.masa.malilib.util.InventoryUtils.findEmptySlotInPlayerInventory(player.playerScreenHandler, false, false) == -1) - { + stack.getCount() > 1 && + UNSTACKING_ITEMS.contains(stack.getItem())) { + if (fi.dy.masa.malilib.util.InventoryUtils.findEmptySlotInPlayerInventory(player.playerScreenHandler, false, false) == -1) { tryCombineStacksInInventory(player, stack); return fi.dy.masa.malilib.util.InventoryUtils.findEmptySlotInPlayerInventory(player.playerScreenHandler, false, false) == -1; @@ -1063,14 +895,12 @@ public static boolean canUnstackingItemNotFitInInventory(ItemStack stack, Player return false; } - public static void switchToPickedBlock() - { - MinecraftClient mc = MinecraftClient.getInstance(); + public static void switchToPickedBlock() { + MinecraftClient mc = MinecraftClient.getInstance(); PlayerEntity player = mc.player; World world = mc.world; - if (player == null || world == null || player.currentScreenHandler != player.playerScreenHandler) - { + if (player == null || world == null || player.currentScreenHandler != player.playerScreenHandler) { return; } @@ -1078,15 +908,13 @@ public static void switchToPickedBlock() boolean isCreative = player.isCreative(); HitResult trace = player.raycast(reach, mc.getTickDelta(), false); - if (trace != null && trace.getType() == HitResult.Type.BLOCK) - { + if (trace != null && trace.getType() == HitResult.Type.BLOCK) { BlockPos pos = ((BlockHitResult) trace).getBlockPos(); BlockState stateTargeted = world.getBlockState(pos); ItemStack stack = stateTargeted.getBlock().getPickStack(world, pos, stateTargeted); if (stack.isEmpty() == false && - fi.dy.masa.malilib.util.InventoryUtils.areStacksEqual(stack, player.getMainHandStack()) == false) - { + fi.dy.masa.malilib.util.InventoryUtils.areStacksEqual(stack, player.getMainHandStack()) == false) { ScreenHandler container = player.currentScreenHandler; PlayerInventory inventory = player.getInventory(); /* @@ -1101,18 +929,14 @@ public static void switchToPickedBlock() } */ - if (isCreative) - { + if (isCreative) { inventory.addPickBlock(stack); mc.interactionManager.clickCreativeStack(player.getStackInHand(Hand.MAIN_HAND), 36 + inventory.selectedSlot); - } - else - { + } else { //player.getInventory().getSlotFor(stack); int slotNumber = fi.dy.masa.malilib.util.InventoryUtils.findSlotWithItem(container, stack, true); - if (slotNumber != -1) - { + if (slotNumber != -1) { swapItemToHand(player, Hand.MAIN_HAND, slotNumber); } } @@ -1120,32 +944,26 @@ public static void switchToPickedBlock() } } - public static boolean cleanUpShulkerBoxNBT(ItemStack stack) - { + public static boolean cleanUpShulkerBoxNBT(ItemStack stack) { boolean changed = false; NbtCompound nbt = stack.getNbt(); - if (nbt != null) - { - if (nbt.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) - { + if (nbt != null) { + if (nbt.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) { NbtCompound tag = nbt.getCompound("BlockEntityTag"); if (tag.contains("Items", Constants.NBT.TAG_LIST) && - tag.getList("Items", Constants.NBT.TAG_COMPOUND).size() == 0) - { + tag.getList("Items", Constants.NBT.TAG_COMPOUND).size() == 0) { tag.remove("Items"); changed = true; } - if (tag.isEmpty()) - { + if (tag.isEmpty()) { nbt.remove("BlockEntityTag"); } } - if (nbt.isEmpty()) - { + if (nbt.isEmpty()) { stack.setNbt(null); changed = true; } @@ -1153,4 +971,100 @@ public static boolean cleanUpShulkerBoxNBT(ItemStack stack) return changed; } + + public static void equipBestElytra(PlayerEntity player) { + if (player == null || GuiUtils.getCurrentScreen() != null) { + return; + } + + ScreenHandler container = player.currentScreenHandler; + + Predicate filter = (s) -> s.getItem() instanceof ElytraItem && ElytraItem.isUsable(s) && s.getDamage() < s.getMaxDamage() - 10; + int targetSlot = findSlotWithBestItemMatch(container, (testedStack, previousBestMatch) -> { + if (!filter.test(testedStack)) return false; + if (!filter.test(previousBestMatch)) return true; + if (getEnchantmentLevel(testedStack, Enchantments.UNBREAKING) > getEnchantmentLevel(previousBestMatch, Enchantments.UNBREAKING)) { + return true; + } + if (getEnchantmentLevel(testedStack, Enchantments.UNBREAKING) < getEnchantmentLevel(previousBestMatch, Enchantments.UNBREAKING)) { + return false; + } + return testedStack.getDamage() <= previousBestMatch.getDamage(); + }, UniformIntProvider.create(9, container.slots.size() - 1)); + + if (targetSlot >= 0) { + swapItemToEquipmentSlot(player, EquipmentSlot.CHEST, targetSlot); + } + } + + public static void swapElytraAndChestPlate(@Nullable PlayerEntity player) { + if (player == null || GuiUtils.getCurrentScreen() != null) { + return; + } + + ScreenHandler container = player.currentScreenHandler; + ItemStack currentStack = player.getEquippedStack(EquipmentSlot.CHEST); + + Predicate stackFilterChestPlate = (s) -> s.getItem() instanceof ArmorItem && ((ArmorItem) s.getItem()).getSlotType() == EquipmentSlot.CHEST; + + if (currentStack.isEmpty() || stackFilterChestPlate.test(currentStack)) { + equipBestElytra(player); + } else { + Predicate finalFilter = (s) -> stackFilterChestPlate.test(s) && s.getDamage() < s.getMaxDamage() - 10; + + int targetSlot = findSlotWithBestItemMatch(container, (testedStack, previousBestMatch) -> { + if (!finalFilter.test(testedStack)) return false; + if (!finalFilter.test(previousBestMatch)) return true; + if (getArmorAndArmorToughnessValue(previousBestMatch, 1, EquipmentSlot.CHEST) < getArmorAndArmorToughnessValue(testedStack, 1, EquipmentSlot.CHEST)) { + return true; + } + if (getArmorAndArmorToughnessValue(previousBestMatch, 1, EquipmentSlot.CHEST) > getArmorAndArmorToughnessValue(testedStack, 1, EquipmentSlot.CHEST)) { + return false; + } + return getEnchantmentLevel(previousBestMatch, Enchantments.PROTECTION) <= getEnchantmentLevel(testedStack, Enchantments.PROTECTION); + }, UniformIntProvider.create(9, container.slots.size() - 1)); + + if (targetSlot >= 0) { + swapItemToEquipmentSlot(player, EquipmentSlot.CHEST, targetSlot); + } + } + } + + private static double getArmorAndArmorToughnessValue(ItemStack stack, double base, EquipmentSlot slot) { + final double[] total = {base}; + Multimap attributeModifiers = stack.getAttributeModifiers(slot); + + for (Map.Entry entry : attributeModifiers.entries()) { + EntityAttribute attribute = entry.getKey(); + EntityAttributeModifier modifier = entry.getValue(); + + switch (modifier.getOperation()) { + case ADDITION: + total[0] += modifier.getValue(); + break; + case MULTIPLY_BASE: + total[0] += modifier.getValue() * base; + break; + case MULTIPLY_TOTAL: + total[0] += modifier.getValue() * total[0]; + break; + default: + throw new IllegalStateException("Unexpected value: " + modifier.getOperation()); + } + } + + return total[0]; + } + + + public static int getEnchantmentLevel(ItemStack stack, @Nonnull Enchantment enchantment) { + if (stack.hasEnchantments()) { + Map enchantments = EnchantmentHelper.get(stack); + if (enchantments.containsKey(enchantment)) { + return enchantments.get(enchantment); + } + } + return -1; + } + } diff --git a/src/main/resources/assets/tweakerge/lang/zh_cn.json b/src/main/resources/assets/tweakerge/lang/zh_cn.json index 7c60e7c..b73c50e 100644 --- a/src/main/resources/assets/tweakerge/lang/zh_cn.json +++ b/src/main/resources/assets/tweakerge/lang/zh_cn.json @@ -166,6 +166,8 @@ "Enables an aim lock, locking the yaw and pitch rotations\nto the current values.\nThis is separate from the snap aim lock,\nwhich locks them to the snapped value.\nThis allows locking them \"freely\" to the current value.":"开启后会锁定当前视角。", "tweakAngelBlock":"AngelBlock | 凭空放置", "Enables an \"Angel Block\" tweak, which allows\nplacing blocks in mid-air in Creative mode":"此功能将让你能够在空中放置方块。\n§6该功能仅在创造模式生效。§f", + "tweakAutoSwitchElytra":"AutoSwitchElytra | 自动切换鞘翅", + "Automatically switches to the Elytra when falling\\nand back to the previous chest equipment when landing.": "当玩家腾空时自动切换到鞘翅,并在着陆时切换回之前的胸甲。", "tweakBlockReachOverride":"BlockReachOverride | 方块放置间距", "Overrides the block reach distance with\nthe one set in Generic -> blockReachDistance":"在通用当功能中的“blockReachDistance”设置放置距离\n将覆盖默认的放置距离", "tweakBreakingGrid":"BreakingGrid | 破坏间距限制",