├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── minitweaks │ ├── MiniTweaks.java │ ├── MiniTweaksCarpetExtension.java │ ├── MiniTweaksSettings.java │ ├── dispenser │ ├── MiniTweaksDispenserBehaviors.java │ └── behaviors │ │ ├── AmethystShardDispenserBehavior.java │ │ ├── DyeItemDispenserBehavior.java │ │ ├── GoldenAppleDispenserBehavior.java │ │ ├── IronIngotDispenserBehavior.java │ │ ├── NameTagDispenserBehavior.java │ │ ├── WaterBottleDispenserBehavior.java │ │ └── WaterBucketDispenserBehavior.java │ ├── mixins │ ├── block │ │ ├── all │ │ │ └── AbstractBlockMixin.java │ │ ├── anvil │ │ │ └── crushing │ │ │ │ └── AnvilBlockMixin.java │ │ ├── dispenser │ │ │ └── behavior │ │ │ │ └── DispenserBlockMixin.java │ │ ├── farmland │ │ │ └── trample │ │ │ │ └── FarmlandBlockMixin.java │ │ ├── grindstone │ │ │ └── curses │ │ │ │ └── GrindstoneScreenHandlerMixin.java │ │ ├── oxidizable │ │ │ └── OxidizableBlockMixin.java │ │ ├── piston │ │ │ └── waterlog │ │ │ │ └── PistonBlockEntityMixin.java │ │ ├── shrieker │ │ │ └── activate │ │ │ │ └── SculkShriekerBlockMixin.java │ │ └── snow │ │ │ └── shave │ │ │ └── ShovelItemMixin.java │ ├── command │ │ └── seed │ │ │ └── permission │ │ │ └── SeedCommandMixin.java │ ├── crafting │ │ └── curses │ │ │ └── RepairItemRecipeMixin.java │ ├── item │ │ └── hoe │ │ │ └── harvest │ │ │ └── HoeItemMixin.java │ └── mob │ │ ├── all │ │ ├── interact │ │ │ └── MobEntityMixin.java │ │ ├── lightning │ │ │ └── EntityMixin.java │ │ └── nametag │ │ │ └── LivingEntityMixin.java │ │ ├── allay │ │ └── duplicate │ │ │ └── AllayEntityInvoker.java │ │ ├── blaze │ │ └── fire │ │ │ └── SmallFireballEntityMixin.java │ │ ├── creeper │ │ ├── block_damage │ │ │ └── CreeperEntityMixin.java │ │ └── head_drops │ │ │ └── CreeperEntityMixin.java │ │ ├── dragon │ │ ├── block_damage │ │ │ └── EnderDragonEntityMixin.java │ │ └── egg │ │ │ └── EnderDragonFightMixin.java │ │ ├── ghast │ │ └── block_damage │ │ │ └── FireballEntityMixin.java │ │ ├── phantom │ │ └── spawning │ │ │ └── PhantomSpawnerMixin.java │ │ ├── pickup │ │ └── SkeletonZombieEntityMixin.java │ │ ├── player │ │ ├── drops │ │ │ ├── ItemEntityAccessor.java │ │ │ └── PlayerInventoryMixin.java │ │ └── xp │ │ │ └── PlayerEntityMixin.java │ │ ├── sheep │ │ └── dye │ │ │ └── DyeItemMixin.java │ │ ├── shulker │ │ └── dye │ │ │ ├── DyeItemMixin.java │ │ │ ├── ShulkerEntityInvoker.java │ │ │ └── ShulkerEntityMixin.java │ │ ├── slime │ │ └── looting │ │ │ └── SlimeEntityMixin.java │ │ ├── snowgolem │ │ └── melt │ │ │ └── SnowGolemEntityMixin.java │ │ ├── squid │ │ └── lightning │ │ │ └── SquidEntityMixin.java │ │ ├── vex │ │ └── nerf │ │ │ └── VexEntityMixin.java │ │ ├── villager │ │ ├── bed_explode │ │ │ └── VillagerEntityMixin.java │ │ └── lightning │ │ │ └── VillagerEntityMixin.java │ │ └── zombie │ │ └── convert │ │ ├── ZombieEntityMixin.java │ │ └── ZombieVillagerEntityInvoker.java │ └── util │ └── AnvilCrushing.java └── resources ├── assets └── minitweaks │ ├── icon.png │ └── lang │ └── en_us.json ├── fabric.mod.json └── minitweaks.mixins.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: build 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-24.04 12 | steps: 13 | - name: checkout repository 14 | uses: actions/checkout@v4 15 | - name: validate gradle wrapper 16 | uses: gradle/actions/wrapper-validation@v4 17 | - name: setup jdk 18 | uses: actions/setup-java@v4 19 | with: 20 | java-version: '21' 21 | distribution: 'microsoft' 22 | - name: make gradle wrapper executable 23 | run: chmod +x ./gradlew 24 | - name: build 25 | run: ./gradlew build 26 | - name: capture build artifacts 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: Artifacts 30 | path: build/libs/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Minitweaks 5 | Minitweaks is an extension for [Carpet Mod](https://github.com/gnembon/fabric-carpet) with many small, mostly survival-related features and game mechanic changes 6 | 7 | 8 | 9 | # Downloads 10 | [](https://modrinth.com/mod/minitweaks) 11 | [](https://github.com/manyrandomthings/minitweaks/releases) 12 | [](https://www.curseforge.com/minecraft/mc-mods/carpet-minitweaks) 13 | 14 | # Minitweaks Carpet Rules List 15 | ## allChargedCreeperHeadsDrop 16 | All mobs killed by a charged creeper drop their head instead of only one 17 | * Type: `boolean` 18 | * Default value: `false` 19 | * Required options: `true`, `false` 20 | * Categories: `minitweaks`, `mobs` 21 | 22 | ## commandSeed 23 | Permission level required to use /seed 24 | Only available server-side 25 | ops/2 for only ops, true/0 for anyone 26 | * Type: `String` 27 | * Default value: `ops` 28 | * Suggested options: `true`, `false`, `ops` 29 | * Categories: `minitweaks`, `command` 30 | 31 | ## deathItemsDespawnMinutes 32 | How many minutes it takes for a player's items to despawn after death 33 | -1 for infinte, 0 for instant despawn, max value 32 34 | * Type: `int` 35 | * Default value: `5` 36 | * Suggested options: `5`, `10`, `15`, `30`, `-1` 37 | * Categories: `minitweaks`, `survival` 38 | 39 | ## disableBlazeFire 40 | Disables fire made from blaze fireballs 41 | * Type: `boolean` 42 | * Default value: `false` 43 | * Required options: `true`, `false` 44 | * Categories: `minitweaks`, `mobs`, `survival` 45 | 46 | ## disableGhastFire 47 | Disables fire made from ghast fireballs 48 | * Type: `boolean` 49 | * Default value: `false` 50 | * Required options: `true`, `false` 51 | * Categories: `minitweaks`, `mobs`, `survival` 52 | 53 | ## dispensersBucketMobs 54 | Dispensers can pick up bucketable mobs 55 | * Type: `boolean` 56 | * Default value: `false` 57 | * Required options: `true`, `false` 58 | * Categories: `minitweaks`, `mobs`, `dispenser` 59 | 60 | ## dispensersCureVillagers 61 | Dispensers feed golden apples to zombie villagers with weakness 62 | Note: dispensers curing a villager does not lower the villager's prices due to gossips being player-specific 63 | * Type: `boolean` 64 | * Default value: `false` 65 | * Required options: `true`, `false` 66 | * Categories: `minitweaks`, `mobs`, `dispenser`, `survival` 67 | 68 | ## dispensersDuplicateAllays 69 | Dispensers duplicate dancing allays with amethyst shards 70 | * Type: `boolean` 71 | * Default value: `false` 72 | * Required options: `true`, `false` 73 | * Categories: `minitweaks`, `mobs`, `dispenser` 74 | 75 | ## dispensersDyeMobs 76 | Dispensers can dye sheep (and shulkers if dyeableShulkers is enabled) 77 | * Type: `boolean` 78 | * Default value: `false` 79 | * Required options: `true`, `false` 80 | * Categories: `minitweaks`, `mobs`, `dispenser` 81 | 82 | ## dispensersNameMobs 83 | Dispensers use name tags on mobs 84 | * Type: `boolean` 85 | * Default value: `false` 86 | * Required options: `true`, `false` 87 | * Categories: `minitweaks`, `mobs`, `dispenser` 88 | 89 | ## dispensersRepairGolems 90 | Dispensers can repair Iron Golems with iron ingots 91 | * Type: `boolean` 92 | * Default value: `false` 93 | * Required options: `true`, `false` 94 | * Categories: `minitweaks`, `mobs`, `dispenser` 95 | 96 | ## dragonBlockDamage 97 | Set dragon block damage breaking type, regardless of mobGriefing gamerule 98 | default: default block breaking 99 | none: no blocks are broken 100 | break: broken blocks are dropped 101 | destroy: broken blocks are destroyed and not dropped 102 | * Type: `String` 103 | * Default value: `default` 104 | * Required options: `default`, `none`, `break`, `destroy` 105 | * Categories: `minitweaks`, `mobs`, `survival` 106 | 107 | ## dyeableShearedSheep 108 | Sheared sheep can be dyed 109 | * Type: `boolean` 110 | * Default value: `false` 111 | * Required options: `true`, `false` 112 | * Categories: `minitweaks`, `mobs` 113 | 114 | ## dyeableShulkers 115 | Shulkers can be dyed 116 | Color can be reset to default using a water bottle 117 | * Type: `boolean` 118 | * Default value: `false` 119 | * Required options: `true`, `false` 120 | * Categories: `minitweaks`, `mobs` 121 | 122 | ## echoShardsEnableShriekers 123 | Using an echo shard on a sculk shrieker allows it to summon wardens 124 | * Type: `boolean` 125 | * Default value: `false` 126 | * Required options: `true`, `false` 127 | * Categories: `minitweaks`, `feature` 128 | 129 | ## fasterOxidation 130 | Blocks oxidize much faster when touching water 131 | * Type: `boolean` 132 | * Default value: `false` 133 | * Required options: `true`, `false` 134 | * Categories: `minitweaks`, `survival`, `feature` 135 | 136 | ## infinityMendingStacking 137 | Allows infinity and mending to stack on bows, like in 1.9 to 1.11 138 | * Type: `boolean` 139 | * Default value: `false` 140 | * Required options: `true`, `false` 141 | * Categories: `minitweaks`, `enchantment`, `survival` 142 | 143 | ## lightningGlowifiesSquids 144 | Squids struck by lightning convert to glow squids 145 | * Type: `boolean` 146 | * Default value: `false` 147 | * Required options: `true`, `false` 148 | * Categories: `minitweaks`, `mobs` 149 | 150 | ## maxPlayerXpDrop 151 | Maximum amount of xp players drop on death 152 | * Type: `int` 153 | * Default value: `100` 154 | * Suggested options: `0`, `100`, `1000`, `10000` 155 | * Categories: `minitweaks`, `survival` 156 | * Additional notes: 157 | * Must be a positive number 158 | 159 | ## mobItemPickup 160 | Overwrites random default pickup chance when mob spawns 161 | Only zombie and skeleton type mobs are affected 162 | default: uses default pickup 163 | always: mobs pick up items 164 | never: mobs don't pick up items 165 | * Type: `String` 166 | * Default value: `default` 167 | * Required options: `default`, `always`, `never` 168 | * Categories: `minitweaks`, `mobs` 169 | 170 | ## mobsDropNametag 171 | Named mobs drop their name tag on death 172 | Note: mobs will drop a name tag even if they weren't named with one 173 | This may also cause name tags to be able to be duped 174 | * Type: `boolean` 175 | * Default value: `false` 176 | * Required options: `true`, `false` 177 | * Categories: `minitweaks`, `mobs`, `survival` 178 | 179 | ## moveableWaterloggedBlocks 180 | Waterlogged blocks stay waterlogged when moved with a piston 181 | * Type: `boolean` 182 | * Default value: `false` 183 | * Required options: `true`, `false` 184 | * Categories: `minitweaks`, `feature` 185 | 186 | ## noCreeperBlockBreaking 187 | Disables creeper explosion block breaking 188 | * Type: `boolean` 189 | * Default value: `false` 190 | * Required options: `true`, `false` 191 | * Categories: `minitweaks`, `mobs`, `survival` 192 | 193 | ## noFeatherFallingTrample 194 | Prevents farmland from being trampled if you have feather falling 195 | * Type: `boolean` 196 | * Default value: `false` 197 | * Required options: `true`, `false` 198 | * Categories: `minitweaks`, `survival` 199 | 200 | ## noGhastBlockBreaking 201 | Disables ghast fireball explosion block breaking 202 | * Type: `boolean` 203 | * Default value: `false` 204 | * Required options: `true`, `false` 205 | * Categories: `minitweaks`, `mobs`, `survival` 206 | 207 | ## noRepairCost 208 | Removes additional cost for using an item in an anvil multiple times 209 | * Type: `boolean` 210 | * Default value: `false` 211 | * Required options: `true`, `false` 212 | * Categories: `minitweaks`, `survival` 213 | 214 | ## noSnowGolemMelting 215 | Prevents Snow Golems from melting in hot biomes 216 | * Type: `boolean` 217 | * Default value: `false` 218 | * Required options: `true`, `false` 219 | * Categories: `minitweaks`, `mobs` 220 | 221 | ## noVillagerWitchConversion 222 | Villagers don't convert to witches when struck by lightning 223 | * Type: `boolean` 224 | * Default value: `false` 225 | * Required options: `true`, `false` 226 | * Categories: `minitweaks`, `mobs` 227 | 228 | ## phantomSpawningTime 229 | Amount of ticks before Phantoms start having a chance to spawn 230 | * Type: `int` 231 | * Default value: `72000` 232 | * Suggested options: `72000`, `360000`, `720000` 233 | * Categories: `minitweaks`, `mobs`, `survival` 234 | * Additional notes: 235 | * Must be a positive number 236 | 237 | ## protectionStacking 238 | Allows all the different protection types to stack on the same piece of armor, like in 1.14 to 1.14.2 239 | * Type: `boolean` 240 | * Default value: `false` 241 | * Required options: `true`, `false` 242 | * Categories: `minitweaks`, `enchantment`, `survival` 243 | 244 | ## quickHarvesting 245 | Right click crops with a hoe to harvest and replant 246 | * Type: `boolean` 247 | * Default value: `false` 248 | * Required options: `true`, `false` 249 | * Categories: `minitweaks`, `survival`, `feature` 250 | 251 | ## removableCurses 252 | Curses are also removed when using grindstones or repair crafting 253 | * Type: `boolean` 254 | * Default value: `false` 255 | * Required options: `true`, `false` 256 | * Categories: `minitweaks`, `enchantment`, `survival` 257 | 258 | ## renewableDragonEgg 259 | Dragon eggs will always be placed on the portal after defeating the dragon 260 | * Type: `boolean` 261 | * Default value: `false` 262 | * Required options: `true`, `false` 263 | * Categories: `minitweaks`, `mobs`, `survival` 264 | 265 | ## renewableRawOres 266 | An anvil landing on Iron/Gold/Copper blocks turns them into the raw ore block version 267 | * Type: `boolean` 268 | * Default value: `false` 269 | * Required options: `true`, `false` 270 | * Categories: `minitweaks`, `survival`, `feature` 271 | 272 | ## shaveSnowLayers 273 | Snow layers can be shaved, removing one layer, when right clicked with a shovel 274 | * Type: `boolean` 275 | * Default value: `false` 276 | * Required options: `true`, `false` 277 | * Categories: `minitweaks`, `survival`, `feature` 278 | 279 | ## slimeLooting 280 | Bigger slimes spawn more smaller slimes when killed with looting 281 | Additional slimes can be up to as many levels of looting as you have (up to +3 with looting 3, etc) 282 | * Type: `boolean` 283 | * Default value: `false` 284 | * Required options: `true`, `false` 285 | * Categories: `minitweaks`, `mobs`, `enchantment`, `survival` 286 | 287 | ## vexesNerf 288 | Vexes will start to die after the evoker that summoned them dies 289 | * Type: `boolean` 290 | * Default value: `false` 291 | * Required options: `true`, `false` 292 | * Categories: `minitweaks`, `mobs`, `survival` 293 | 294 | ## villagersAlwaysConvert 295 | Villagers will always convert to Zombie Villagers when killed by a zombie, even on easy and normal difficulty 296 | * Type: `boolean` 297 | * Default value: `false` 298 | * Required options: `true`, `false` 299 | * Categories: `minitweaks`, `mobs`, `survival` 300 | 301 | ## villagersExplodeBeds 302 | Villagers cause explosions when trying to use beds in the nether or end, like players 303 | "But why?" Idk, it's just a funny idea I had 304 | * Type: `boolean` 305 | * Default value: `false` 306 | * Required options: `true`, `false` 307 | * Categories: `minitweaks`, `mobs` 308 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.10-SNAPSHOT' 3 | id 'maven-publish' 4 | } 5 | 6 | version = project.mod_version 7 | group = project.maven_group 8 | 9 | base { 10 | archivesName = "${project.archives_base_name}-${project.minecraft_version}" 11 | } 12 | 13 | repositories { 14 | // Add repositories to retrieve artifacts from in here. 15 | // You should only use this when depending on other mods because 16 | // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. 17 | // See https://docs.gradle.org/current/userguide/declaring_repositories.html 18 | // for more information about repositories. 19 | maven { 20 | url 'https://masa.dy.fi/maven' 21 | } 22 | } 23 | 24 | loom { 25 | splitEnvironmentSourceSets() 26 | 27 | mods { 28 | "minitweaks" { 29 | sourceSet sourceSets.main 30 | } 31 | } 32 | 33 | } 34 | 35 | dependencies { 36 | // To change the versions see the gradle.properties file 37 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 38 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" 39 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" 40 | modImplementation "carpet:fabric-carpet:${project.carpet_core_version}" 41 | } 42 | 43 | processResources { 44 | inputs.property "version", project.version 45 | 46 | filesMatching("fabric.mod.json") { 47 | expand "version": inputs.properties.version 48 | } 49 | } 50 | 51 | tasks.withType(JavaCompile).configureEach { 52 | it.options.release = 21 53 | } 54 | 55 | java { 56 | // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task 57 | // if it is present. 58 | // If you remove this line, sources will not be generated. 59 | withSourcesJar() 60 | 61 | sourceCompatibility = JavaVersion.VERSION_21 62 | targetCompatibility = JavaVersion.VERSION_21 63 | } 64 | 65 | jar { 66 | inputs.property "archivesName", project.base.archivesName 67 | 68 | from("LICENSE") { 69 | rename { "${it}_${inputs.properties.archivesName}"} 70 | } 71 | } 72 | 73 | // configure the maven publication 74 | publishing { 75 | publications { 76 | create("mavenJava", MavenPublication) { 77 | artifactId = project.archives_base_name 78 | from components.java 79 | } 80 | } 81 | 82 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 83 | repositories { 84 | // Add repositories to publish to here. 85 | // Notice: This block does NOT have the same function as the block in the top level. 86 | // The repositories here will be used for publishing your artifact, not for 87 | // retrieving dependencies. 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/develop 6 | minecraft_version=1.21.5 7 | yarn_mappings=1.21.5+build.1 8 | loader_version=0.16.10 9 | # check available versions on maven for the given minecraft version you are using: https://masa.dy.fi/maven/carpet/fabric-carpet/ 10 | carpet_core_version=1.21.5-1.4.169+v250325 11 | 12 | # Mod Properties 13 | mod_version=1.5.2 14 | maven_group=minitweaks 15 | archives_base_name=minitweaks 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manyrandomthings/minitweaks/15691919818b51915b6a3da4c473cd49e39b39fe/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | org.gradle.wrapper.GradleWrapperMain \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | mavenCentral() 8 | gradlePluginPortal() 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/java/minitweaks/MiniTweaks.java: -------------------------------------------------------------------------------- 1 | package minitweaks; 2 | 3 | import carpet.CarpetExtension; 4 | import carpet.CarpetServer; 5 | import net.fabricmc.api.ModInitializer; 6 | 7 | public class MiniTweaks implements ModInitializer { 8 | public static final String MOD_ID = "minitweaks"; 9 | public static CarpetExtension EXTENSION = new MiniTweaksCarpetExtension(); 10 | 11 | @Override 12 | public void onInitialize() { 13 | CarpetServer.manageExtension(EXTENSION); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/MiniTweaksCarpetExtension.java: -------------------------------------------------------------------------------- 1 | package minitweaks; 2 | 3 | import carpet.CarpetExtension; 4 | import carpet.CarpetServer; 5 | import carpet.api.settings.CarpetRule; 6 | import carpet.api.settings.InvalidRuleValueException; 7 | import carpet.api.settings.SettingsManager; 8 | import com.google.gson.Gson; 9 | import com.google.gson.GsonBuilder; 10 | import com.google.gson.Strictness; 11 | import com.google.gson.reflect.TypeToken; 12 | import net.fabricmc.loader.api.FabricLoader; 13 | import net.fabricmc.loader.api.ModContainer; 14 | import org.apache.commons.io.IOUtils; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.nio.charset.StandardCharsets; 21 | import java.util.Collections; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | public class MiniTweaksCarpetExtension implements CarpetExtension { 26 | private static final Logger LOGGER = LoggerFactory.getLogger(MiniTweaks.MOD_ID); 27 | public static SettingsManager settingsManager; 28 | 29 | @Override 30 | public void onGameStarted() { 31 | ModContainer mod = FabricLoader.getInstance().getModContainer(MiniTweaks.MOD_ID).orElseThrow(NullPointerException::new); 32 | 33 | // create settings manager 34 | settingsManager = new SettingsManager(mod.getMetadata().getVersion().getFriendlyString(), MiniTweaks.MOD_ID, mod.getMetadata().getName()); 35 | 36 | // load carpet rules into settings manager(s) 37 | settingsManager.parseSettingsClass(MiniTweaksSettings.class); 38 | CarpetServer.settingsManager.parseSettingsClass(MiniTweaksSettings.class); 39 | 40 | this.createRuleOverwriteWorkaround(); 41 | } 42 | 43 | private void createRuleOverwriteWorkaround() { 44 | // workaround for rule being overwritten: https://github.com/gnembon/fabric-carpet/issues/802 45 | SettingsManager.registerGlobalRuleObserver((source, changedRule, userInput) -> { 46 | CarpetRule minitweaksRule = settingsManager.getCarpetRule(changedRule.name()); 47 | CarpetRule carpetRule = CarpetServer.settingsManager.getCarpetRule(changedRule.name()); 48 | 49 | // check if the rule being changed exists in minitweaks, but isn't the same rule as the one in carpet's settingsManager 50 | // if so, update the rule (if types are the same) 51 | if(minitweaksRule != null && carpetRule != null && minitweaksRule != carpetRule && minitweaksRule.type() == carpetRule.type()) { 52 | try { 53 | minitweaksRule.set(source, userInput); 54 | } catch (InvalidRuleValueException e) { 55 | LOGGER.error("Error setting rule", e); 56 | } 57 | } 58 | }); 59 | } 60 | 61 | @Override 62 | public Map canHasTranslations(String lang) { 63 | InputStream langFile = MiniTweaks.class.getClassLoader().getResourceAsStream("assets/minitweaks/lang/%s.json".formatted(lang)); 64 | if (langFile == null) { 65 | return Collections.emptyMap(); 66 | } 67 | String jsonData; 68 | try { 69 | jsonData = IOUtils.toString(langFile, StandardCharsets.UTF_8); 70 | } catch (IOException e) { 71 | return Collections.emptyMap(); 72 | } 73 | Gson gson = new GsonBuilder().setStrictness(Strictness.LENIENT).create(); 74 | 75 | Map map = gson.fromJson(jsonData, new TypeToken>() {}.getType()); 76 | Map map2 = new HashMap<>(); 77 | 78 | // create translation keys for both carpet and minitweaks settingsManagers 79 | map.forEach((key, value) -> { 80 | map2.put(key, value); 81 | if(key.startsWith("minitweaks.rule.")) { 82 | map2.put(key.replace("minitweaks.rule.", "carpet.rule."), value); 83 | } 84 | }); 85 | 86 | return map2; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/MiniTweaksSettings.java: -------------------------------------------------------------------------------- 1 | package minitweaks; 2 | 3 | import carpet.api.settings.CarpetRule; 4 | import carpet.api.settings.Rule; 5 | import carpet.api.settings.RuleCategory; 6 | import carpet.api.settings.Validator; 7 | import carpet.api.settings.Validators; 8 | import net.fabricmc.api.EnvType; 9 | import net.fabricmc.loader.api.FabricLoader; 10 | import net.minecraft.server.command.ServerCommandSource; 11 | 12 | public class MiniTweaksSettings { 13 | // rule categories 14 | public static final String MODNAME = "minitweaks"; 15 | public static final String MOBS = "mobs"; 16 | public static final String ENCHANTMENT = "enchantment"; 17 | public static final String BACKPORT = "backport"; 18 | 19 | 20 | public enum BlockBreakingType { 21 | DEFAULT, NONE, BREAK, DESTROY; 22 | } 23 | 24 | public enum ItemPickupType { 25 | DEFAULT, ALWAYS, NEVER; 26 | } 27 | 28 | private static class ServerSideOnlyRuleCondition implements Rule.Condition { 29 | @Override 30 | public boolean shouldRegister() { 31 | return FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER; 32 | } 33 | } 34 | 35 | private static class ItemDespawnTimeValidator extends Validator { 36 | @Override 37 | public Integer validate(ServerCommandSource source, CarpetRule currentRule, Integer newValue, String typedString) { 38 | if(newValue >= -1 && newValue <= 32) { 39 | return newValue; 40 | } 41 | return null; 42 | } 43 | } 44 | 45 | // all charged creeper heads drop 46 | @Rule( 47 | categories = {MODNAME, MOBS} 48 | ) 49 | public static boolean allChargedCreeperHeadsDrop; 50 | 51 | // seed command 52 | @Rule( 53 | categories = {MODNAME, RuleCategory.COMMAND}, 54 | conditions = ServerSideOnlyRuleCondition.class 55 | ) 56 | public static String commandSeed = "ops"; 57 | 58 | // death items despawn time 59 | @Rule( 60 | options = {"5", "10", "15", "30", "-1"}, 61 | validators = ItemDespawnTimeValidator.class, 62 | strict = false, 63 | categories = {MODNAME, RuleCategory.SURVIVAL} 64 | ) 65 | public static int deathItemsDespawnMinutes = 5; 66 | 67 | // blaze fireball fire 68 | @Rule( 69 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 70 | ) 71 | public static boolean disableBlazeFire = false; 72 | 73 | // ghast fireball fire 74 | @Rule( 75 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 76 | ) 77 | public static boolean disableGhastFire = false; 78 | 79 | // dispensers bucket mobs 80 | @Rule( 81 | categories = {MODNAME, MOBS, RuleCategory.DISPENSER} 82 | ) 83 | public static boolean dispensersBucketMobs = false; 84 | 85 | // dispensers cure villagers 86 | @Rule( 87 | categories = {MODNAME, MOBS, RuleCategory.DISPENSER, RuleCategory.SURVIVAL} 88 | ) 89 | public static boolean dispensersCureVillagers = false; 90 | 91 | // dispensers duplicate allays 92 | @Rule( 93 | categories = {MODNAME, MOBS, RuleCategory.DISPENSER} 94 | ) 95 | public static boolean dispensersDuplicateAllays = false; 96 | 97 | // dispensers dye mobs 98 | @Rule( 99 | categories = {MODNAME, MOBS, RuleCategory.DISPENSER} 100 | ) 101 | public static boolean dispensersDyeMobs = false; 102 | 103 | // dispensers use name tags 104 | @Rule( 105 | categories = {MODNAME, MOBS, RuleCategory.DISPENSER} 106 | ) 107 | public static boolean dispensersNameMobs = false; 108 | 109 | // dispensers repair iron golems 110 | @Rule( 111 | categories = {MODNAME, MOBS, RuleCategory.DISPENSER} 112 | ) 113 | public static boolean dispensersRepairGolems = false; 114 | 115 | // dragon block breaking 116 | @Rule( 117 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 118 | ) 119 | public static BlockBreakingType dragonBlockDamage = BlockBreakingType.DEFAULT; 120 | 121 | // dye sheared sheep 122 | @Rule( 123 | categories = {MODNAME, MOBS} 124 | ) 125 | public static boolean dyeableShearedSheep = false; 126 | 127 | // dye shulkers 128 | @Rule( 129 | categories = {MODNAME, MOBS} 130 | ) 131 | public static boolean dyeableShulkers = false; 132 | 133 | // echo shards enable sculk shriekers 134 | @Rule( 135 | categories = {MODNAME, RuleCategory.FEATURE} 136 | ) 137 | public static boolean echoShardsEnableShriekers = false; 138 | 139 | // faster oxidation 140 | @Rule( 141 | categories = {MODNAME, RuleCategory.SURVIVAL, RuleCategory.FEATURE} 142 | ) 143 | public static boolean fasterOxidation = false; 144 | 145 | // lightning glowifies squids 146 | @Rule( 147 | categories = {MODNAME, MOBS} 148 | ) 149 | public static boolean lightningGlowifiesSquids = false; 150 | 151 | // max xp drop 152 | @Rule( 153 | options = {"0", "100", "1000", "10000"}, 154 | validators = Validators.NonNegativeNumber.class, 155 | strict = false, 156 | categories = {MODNAME, RuleCategory.SURVIVAL} 157 | ) 158 | public static int maxPlayerXpDrop = 100; 159 | 160 | // mob item pickup 161 | @Rule( 162 | categories = {MODNAME, MOBS} 163 | ) 164 | public static ItemPickupType mobItemPickup = ItemPickupType.DEFAULT; 165 | 166 | // mobs drop name tag 167 | @Rule( 168 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 169 | ) 170 | public static boolean mobsDropNametag = false; 171 | 172 | // moveable waterlogged blocks 173 | @Rule( 174 | categories = {MODNAME, RuleCategory.FEATURE} 175 | ) 176 | public static boolean moveableWaterloggedBlocks = false; 177 | 178 | // no creeper block breaking 179 | @Rule( 180 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 181 | ) 182 | public static boolean noCreeperBlockBreaking = false; 183 | 184 | // farmland feather falling 185 | @Rule( 186 | categories = {MODNAME, RuleCategory.SURVIVAL} 187 | ) 188 | public static boolean noFeatherFallingTrample = false; 189 | 190 | // no ghast fireball block breaking 191 | @Rule( 192 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 193 | ) 194 | public static boolean noGhastBlockBreaking = false; 195 | 196 | // no snow golem melting 197 | @Rule( 198 | categories = {MODNAME, MOBS} 199 | ) 200 | public static boolean noSnowGolemMelting = false; 201 | 202 | @Rule( 203 | categories = {MODNAME, MOBS} 204 | ) 205 | public static boolean noVillagerWitchConversion = false; 206 | 207 | // phantom spawning 208 | @Rule( 209 | options = {"72000", "360000", "720000"}, 210 | validators = Validators.NonNegativeNumber.class, 211 | strict = false, 212 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 213 | ) 214 | public static int phantomSpawningTime = 72000; 215 | 216 | // quick harvesting 217 | @Rule( 218 | categories = {MODNAME, RuleCategory.SURVIVAL, RuleCategory.FEATURE} 219 | ) 220 | public static boolean quickHarvesting = false; 221 | 222 | // removable curses 223 | @Rule( 224 | categories = {MODNAME, ENCHANTMENT, RuleCategory.SURVIVAL} 225 | ) 226 | public static boolean removableCurses = false; 227 | 228 | // dragon egg placing 229 | @Rule( 230 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 231 | ) 232 | public static boolean renewableDragonEgg = false; 233 | 234 | // renewable raw ores 235 | @Rule( 236 | categories = {MODNAME, RuleCategory.SURVIVAL, RuleCategory.FEATURE} 237 | ) 238 | public static boolean renewableRawOres = false; 239 | 240 | // shave snow layers 241 | @Rule( 242 | categories = {MODNAME, RuleCategory.SURVIVAL, RuleCategory.FEATURE} 243 | ) 244 | public static boolean shaveSnowLayers = false; 245 | 246 | // slime looting 247 | @Rule( 248 | categories = {MODNAME, MOBS, ENCHANTMENT, RuleCategory.SURVIVAL} 249 | ) 250 | public static boolean slimeLooting = false; 251 | 252 | // vex nerf 253 | @Rule( 254 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 255 | ) 256 | public static boolean vexesNerf = false; 257 | 258 | // villagers always convert 259 | @Rule( 260 | categories = {MODNAME, MOBS, RuleCategory.SURVIVAL} 261 | ) 262 | public static boolean villagersAlwaysConvert = false; 263 | 264 | // villagers explode beds 265 | @Rule( 266 | categories = {MODNAME, MOBS} 267 | ) 268 | public static boolean villagersExplodeBeds = false; 269 | } 270 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/MiniTweaksDispenserBehaviors.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import minitweaks.dispenser.behaviors.AmethystShardDispenserBehavior; 5 | import minitweaks.dispenser.behaviors.DyeItemDispenserBehavior; 6 | import minitweaks.dispenser.behaviors.GoldenAppleDispenserBehavior; 7 | import minitweaks.dispenser.behaviors.IronIngotDispenserBehavior; 8 | import minitweaks.dispenser.behaviors.NameTagDispenserBehavior; 9 | import minitweaks.dispenser.behaviors.WaterBottleDispenserBehavior; 10 | import minitweaks.dispenser.behaviors.WaterBucketDispenserBehavior; 11 | import net.minecraft.block.DispenserBlock; 12 | import net.minecraft.block.dispenser.DispenserBehavior; 13 | import net.minecraft.block.entity.DispenserBlockEntity; 14 | import net.minecraft.component.DataComponentTypes; 15 | import net.minecraft.component.type.PotionContentsComponent; 16 | import net.minecraft.entity.Bucketable; 17 | import net.minecraft.entity.EntityType; 18 | import net.minecraft.entity.LivingEntity; 19 | import net.minecraft.entity.mob.PathAwareEntity; 20 | import net.minecraft.entity.mob.ShulkerEntity; 21 | import net.minecraft.entity.passive.SheepEntity; 22 | import net.minecraft.entity.player.PlayerEntity; 23 | import net.minecraft.item.DyeItem; 24 | import net.minecraft.item.Item; 25 | import net.minecraft.item.ItemStack; 26 | import net.minecraft.item.Items; 27 | import net.minecraft.potion.Potions; 28 | import net.minecraft.predicate.entity.EntityPredicates; 29 | import net.minecraft.server.world.ServerWorld; 30 | import net.minecraft.util.math.BlockPointer; 31 | import net.minecraft.util.math.BlockPos; 32 | import net.minecraft.util.math.Box; 33 | 34 | public class MiniTweaksDispenserBehaviors { 35 | public static final DispenserBehavior NAME_TAG = new NameTagDispenserBehavior(); 36 | public static final DispenserBehavior DYE_ITEM = new DyeItemDispenserBehavior(); 37 | public static final DispenserBehavior GOLDEN_APPLE = new GoldenAppleDispenserBehavior(); 38 | public static final DispenserBehavior IRON_INGOT = new IronIngotDispenserBehavior(); 39 | public static final DispenserBehavior WATER_BUCKET = new WaterBucketDispenserBehavior(); 40 | public static final DispenserBehavior AMETHYST_SHARD = new AmethystShardDispenserBehavior(); 41 | public static final DispenserBehavior WATER_BOTTLE = new WaterBottleDispenserBehavior(); 42 | 43 | // get dispenser behavior 44 | public static DispenserBehavior getCustomDispenserBehavior(ServerWorld serverWorld, BlockPos pos, BlockPointer blockPointer, DispenserBlockEntity dispenserBlockEntity, ItemStack stack) { 45 | Item item = stack.getItem(); 46 | BlockPos frontPos = pos.offset(blockPointer.state().get(DispenserBlock.FACING)); 47 | Box frontBox = new Box(frontPos); 48 | 49 | // name tag (with name) behavior 50 | if(MiniTweaksSettings.dispensersNameMobs && stack.isOf(Items.NAME_TAG) && stack.contains(DataComponentTypes.CUSTOM_NAME)) { 51 | boolean hasNameableMobs = !serverWorld.getEntitiesByClass(LivingEntity.class, frontBox, EntityPredicates.VALID_LIVING_ENTITY.and(entity -> !(entity instanceof PlayerEntity))).isEmpty(); 52 | 53 | if(hasNameableMobs) { 54 | return NAME_TAG; 55 | } 56 | } 57 | // dye items behavior 58 | else if(MiniTweaksSettings.dispensersDyeMobs && item instanceof DyeItem) { 59 | boolean hasDyeableMobs = !serverWorld.getEntitiesByClass(PathAwareEntity.class, frontBox, EntityPredicates.VALID_LIVING_ENTITY.and(entity -> { 60 | return entity instanceof SheepEntity || (MiniTweaksSettings.dyeableShulkers && entity instanceof ShulkerEntity); 61 | })).isEmpty(); 62 | 63 | if(hasDyeableMobs) { 64 | return DYE_ITEM; 65 | } 66 | } 67 | // undye shulker behavior 68 | else if(MiniTweaksSettings.dyeableShulkers && MiniTweaksSettings.dispensersDyeMobs && stack.isOf(Items.POTION) && stack.getOrDefault(DataComponentTypes.POTION_CONTENTS, PotionContentsComponent.DEFAULT).matches(Potions.WATER)) { 69 | boolean hasShulkers = !serverWorld.getEntitiesByType(EntityType.SHULKER, frontBox, EntityPredicates.VALID_LIVING_ENTITY).isEmpty(); 70 | 71 | if(hasShulkers) { 72 | return WATER_BOTTLE; 73 | } 74 | } 75 | // golden apple behavior 76 | else if(MiniTweaksSettings.dispensersCureVillagers && stack.isOf(Items.GOLDEN_APPLE)) { 77 | boolean hasZombieVillagers = !serverWorld.getEntitiesByType(EntityType.ZOMBIE_VILLAGER, frontBox, EntityPredicates.VALID_LIVING_ENTITY).isEmpty(); 78 | 79 | if(hasZombieVillagers) { 80 | return GOLDEN_APPLE; 81 | } 82 | } 83 | // iron ingot behavior 84 | else if(MiniTweaksSettings.dispensersRepairGolems && stack.isOf(Items.IRON_INGOT)) { 85 | boolean hasIronGolems = !serverWorld.getEntitiesByType(EntityType.IRON_GOLEM, frontBox, EntityPredicates.VALID_LIVING_ENTITY).isEmpty(); 86 | 87 | if(hasIronGolems) { 88 | return IRON_INGOT; 89 | } 90 | } 91 | // pick up bucketable mob 92 | else if(MiniTweaksSettings.dispensersBucketMobs && stack.isOf(Items.WATER_BUCKET)) { 93 | boolean hasBucketableMobs = !serverWorld.getEntitiesByClass(LivingEntity.class, frontBox, EntityPredicates.VALID_LIVING_ENTITY.and(entity -> { 94 | return entity instanceof Bucketable; 95 | })).isEmpty(); 96 | 97 | if(hasBucketableMobs) { 98 | return WATER_BUCKET; 99 | } 100 | } 101 | else if(MiniTweaksSettings.dispensersDuplicateAllays && stack.isOf(Items.AMETHYST_SHARD)) { 102 | boolean hasAllays = !serverWorld.getEntitiesByType(EntityType.ALLAY, frontBox, EntityPredicates.VALID_LIVING_ENTITY).isEmpty(); 103 | 104 | if(hasAllays) { 105 | return AMETHYST_SHARD; 106 | } 107 | } 108 | 109 | // no available dispenser behaviors, return null 110 | return null; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/behaviors/AmethystShardDispenserBehavior.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser.behaviors; 2 | 3 | import minitweaks.mixins.mob.allay.duplicate.AllayEntityInvoker; 4 | import net.minecraft.block.DispenserBlock; 5 | import net.minecraft.block.dispenser.FallibleItemDispenserBehavior; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.passive.AllayEntity; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.predicate.entity.EntityPredicates; 10 | import net.minecraft.registry.tag.ItemTags; 11 | import net.minecraft.server.world.ServerWorld; 12 | import net.minecraft.sound.SoundCategory; 13 | import net.minecraft.sound.SoundEvents; 14 | import net.minecraft.util.Util; 15 | import net.minecraft.util.math.BlockPointer; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.util.math.Box; 18 | 19 | import java.util.List; 20 | 21 | public class AmethystShardDispenserBehavior extends FallibleItemDispenserBehavior { 22 | @Override 23 | protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { 24 | this.setSuccess(true); 25 | 26 | // get block in front of dispenser 27 | BlockPos blockPos = pointer.pos().offset(pointer.state().get(DispenserBlock.FACING)); 28 | // get valid allays in front of dispenser 29 | List list = pointer.world().getEntitiesByType(EntityType.ALLAY, new Box(blockPos), EntityPredicates.VALID_LIVING_ENTITY.and((entity) -> { 30 | AllayEntity allayEntity = (AllayEntity) entity; 31 | AllayEntityInvoker allayEntityInvoker = (AllayEntityInvoker) allayEntity; 32 | return allayEntity.isDancing() && stack.isIn(ItemTags.DUPLICATES_ALLAYS) && allayEntityInvoker.invokeCanDuplicate(); 33 | })); 34 | 35 | if(!list.isEmpty()) { 36 | ServerWorld serverWorld = pointer.world(); 37 | AllayEntity randomAllay = Util.getRandom(list, serverWorld.getRandom()); 38 | AllayEntityInvoker allayInvoker = (AllayEntityInvoker) randomAllay; 39 | 40 | allayInvoker.invokeDuplicate(); 41 | serverWorld.sendEntityStatus(randomAllay, (byte) 18); 42 | serverWorld.playSoundFromEntity(null, randomAllay, SoundEvents.BLOCK_AMETHYST_BLOCK_CHIME, SoundCategory.NEUTRAL, 2.0F, 1.0F); 43 | 44 | stack.decrement(1); 45 | return stack; 46 | } 47 | 48 | this.setSuccess(false); 49 | return stack; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/behaviors/DyeItemDispenserBehavior.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser.behaviors; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import minitweaks.mixins.mob.shulker.dye.ShulkerEntityInvoker; 5 | import net.minecraft.block.DispenserBlock; 6 | import net.minecraft.block.dispenser.FallibleItemDispenserBehavior; 7 | import net.minecraft.entity.mob.PathAwareEntity; 8 | import net.minecraft.entity.mob.ShulkerEntity; 9 | import net.minecraft.entity.passive.SheepEntity; 10 | import net.minecraft.item.DyeItem; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.predicate.entity.EntityPredicates; 13 | import net.minecraft.sound.SoundCategory; 14 | import net.minecraft.sound.SoundEvents; 15 | import net.minecraft.util.DyeColor; 16 | import net.minecraft.util.Util; 17 | import net.minecraft.util.math.BlockPointer; 18 | import net.minecraft.util.math.BlockPos; 19 | import net.minecraft.util.math.Box; 20 | 21 | import java.util.List; 22 | import java.util.Optional; 23 | 24 | public class DyeItemDispenserBehavior extends FallibleItemDispenserBehavior { 25 | protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { 26 | this.setSuccess(true); 27 | // get color of item 28 | DyeColor itemColor = ((DyeItem) stack.getItem()).getColor(); 29 | 30 | // get block in front of dispenser 31 | BlockPos blockPos = pointer.pos().offset(pointer.state().get(DispenserBlock.FACING)); 32 | // get list of valid entities in front of dispenser 33 | List list = pointer.world().getEntitiesByClass(PathAwareEntity.class, new Box(blockPos), EntityPredicates.VALID_LIVING_ENTITY.and((entity) -> { 34 | // sheep must not be sheared (if dyeableShearedSheep is not enabled) or match item color 35 | if(entity instanceof SheepEntity sheepEntity) { 36 | return (MiniTweaksSettings.dyeableShearedSheep || !sheepEntity.isSheared()) && sheepEntity.getColor() != itemColor; 37 | } 38 | // dyeableShulkers rule must be enabled and shulker must not match item color 39 | else if(MiniTweaksSettings.dyeableShulkers && entity instanceof ShulkerEntity shulkerEntity) { 40 | return shulkerEntity.getColor() != itemColor; 41 | } 42 | return false; 43 | })); 44 | 45 | // check if there are valid entities 46 | if(!list.isEmpty()) { 47 | // choose random mob 48 | PathAwareEntity randomMob = Util.getRandom(list, pointer.world().getRandom()); 49 | // play dye sound 50 | randomMob.getWorld().playSoundFromEntity(null, randomMob, SoundEvents.ITEM_DYE_USE, SoundCategory.PLAYERS, 1.0F, 1.0F); 51 | 52 | // set color of sheep or shulker 53 | if(randomMob instanceof SheepEntity sheepEntity) { 54 | sheepEntity.setColor(itemColor); 55 | } 56 | else if(randomMob instanceof ShulkerEntity shulkerEntity) { 57 | ((ShulkerEntityInvoker) shulkerEntity).invokeSetColor(Optional.of(itemColor)); 58 | } 59 | 60 | stack.decrement(1); 61 | return stack; 62 | } 63 | 64 | // fail to dispense if no entities are available to be dyed 65 | this.setSuccess(false); 66 | return stack; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/behaviors/GoldenAppleDispenserBehavior.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser.behaviors; 2 | 3 | import minitweaks.mixins.mob.zombie.convert.ZombieVillagerEntityInvoker; 4 | import net.minecraft.block.DispenserBlock; 5 | import net.minecraft.block.dispenser.FallibleItemDispenserBehavior; 6 | import net.minecraft.entity.effect.StatusEffects; 7 | import net.minecraft.entity.mob.ZombieVillagerEntity; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.predicate.entity.EntityPredicates; 10 | import net.minecraft.util.Util; 11 | import net.minecraft.util.math.BlockPointer; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.util.math.Box; 14 | 15 | import java.util.List; 16 | 17 | public class GoldenAppleDispenserBehavior extends FallibleItemDispenserBehavior { 18 | protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { 19 | this.setSuccess(true); 20 | 21 | // get block in front of dispenser 22 | BlockPos blockPos = pointer.pos().offset(pointer.state().get(DispenserBlock.FACING)); 23 | // get valid zombie villagers in front of dispenser 24 | List list = pointer.world().getEntitiesByClass(ZombieVillagerEntity.class, new Box(blockPos), EntityPredicates.VALID_LIVING_ENTITY.and((entity) -> { 25 | ZombieVillagerEntity zombieVillagerEntity = (ZombieVillagerEntity) entity; 26 | return !zombieVillagerEntity.isConverting() && zombieVillagerEntity.hasStatusEffect(StatusEffects.WEAKNESS); 27 | })); 28 | 29 | if(!list.isEmpty()) { 30 | // choose random zombie villager 31 | ZombieVillagerEntity zombieVillager = Util.getRandom(list, pointer.world().getRandom()); 32 | // set converting 33 | ((ZombieVillagerEntityInvoker) zombieVillager).invokeSetConverting(null, zombieVillager.getRandom().nextInt(2401) + 3600); 34 | 35 | stack.decrement(1); 36 | return stack; 37 | } 38 | 39 | this.setSuccess(false); 40 | return stack; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/behaviors/IronIngotDispenserBehavior.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser.behaviors; 2 | 3 | import net.minecraft.block.DispenserBlock; 4 | import net.minecraft.block.dispenser.FallibleItemDispenserBehavior; 5 | import net.minecraft.entity.EntityType; 6 | import net.minecraft.entity.passive.IronGolemEntity; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraft.predicate.entity.EntityPredicates; 9 | import net.minecraft.sound.SoundEvents; 10 | import net.minecraft.util.Util; 11 | import net.minecraft.util.math.BlockPointer; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.util.math.Box; 14 | import net.minecraft.util.math.random.Random; 15 | 16 | import java.util.List; 17 | 18 | public class IronIngotDispenserBehavior extends FallibleItemDispenserBehavior { 19 | protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { 20 | this.setSuccess(true); 21 | 22 | // get block in front of dispenser 23 | BlockPos blockPos = pointer.pos().offset(pointer.state().get(DispenserBlock.FACING)); 24 | // get all iron golems in front of dispenser 25 | List list = pointer.world().getEntitiesByType(EntityType.IRON_GOLEM, new Box(blockPos), EntityPredicates.VALID_LIVING_ENTITY.and((entity) -> { 26 | IronGolemEntity ironGolemEntity = (IronGolemEntity) entity; 27 | return ironGolemEntity.getHealth() < ironGolemEntity.getMaxHealth(); 28 | })); 29 | 30 | // if valid iron golems found 31 | if(!list.isEmpty()) { 32 | // get random golem 33 | IronGolemEntity ironGolem = Util.getRandom(list, pointer.world().getRandom()); 34 | // heal golem 35 | ironGolem.heal(25.0F); 36 | 37 | // play repair sound 38 | Random rand = ironGolem.getRandom(); 39 | float pitch = 1.0F + (rand.nextFloat() - rand.nextFloat()) * 0.2F; 40 | ironGolem.playSound(SoundEvents.ENTITY_IRON_GOLEM_REPAIR, 1.0F, pitch); 41 | 42 | // remove one ingot and return 43 | stack.decrement(1); 44 | return stack; 45 | } 46 | 47 | this.setSuccess(false); 48 | return stack; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/behaviors/NameTagDispenserBehavior.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser.behaviors; 2 | 3 | import net.minecraft.block.DispenserBlock; 4 | import net.minecraft.block.dispenser.FallibleItemDispenserBehavior; 5 | import net.minecraft.entity.LivingEntity; 6 | import net.minecraft.entity.mob.MobEntity; 7 | import net.minecraft.entity.player.PlayerEntity; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.predicate.entity.EntityPredicates; 10 | import net.minecraft.util.Util; 11 | import net.minecraft.util.math.BlockPointer; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.util.math.Box; 14 | 15 | import java.util.List; 16 | 17 | public class NameTagDispenserBehavior extends FallibleItemDispenserBehavior { 18 | protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { 19 | this.setSuccess(true); 20 | 21 | // get block in front of dispenser 22 | BlockPos blockPos = pointer.pos().offset(pointer.state().get(DispenserBlock.FACING)); 23 | // get all non-player living entities in front of dispenser 24 | List list = pointer.world().getEntitiesByClass(LivingEntity.class, new Box(blockPos), EntityPredicates.VALID_LIVING_ENTITY.and((livingEntity) -> !(livingEntity instanceof PlayerEntity))); 25 | 26 | // if mobs found 27 | if(!list.isEmpty()) { 28 | // get random entity 29 | LivingEntity entity = Util.getRandom(list, pointer.world().getRandom()); 30 | // set name to nametag's name 31 | entity.setCustomName(stack.getName()); 32 | // if entity is MobEntity, prevent it from despawning 33 | if(entity instanceof MobEntity mobEntity) { 34 | mobEntity.setPersistent(); 35 | } 36 | stack.decrement(1); 37 | return stack; 38 | } 39 | 40 | // name tag is named but no mobs are in front, do nothing 41 | this.setSuccess(false); 42 | return stack; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/behaviors/WaterBottleDispenserBehavior.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser.behaviors; 2 | 3 | import minitweaks.mixins.mob.shulker.dye.ShulkerEntityInvoker; 4 | import net.minecraft.block.DispenserBlock; 5 | import net.minecraft.block.dispenser.FallibleItemDispenserBehavior; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.mob.ShulkerEntity; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.item.Items; 10 | import net.minecraft.predicate.entity.EntityPredicates; 11 | import net.minecraft.server.world.ServerWorld; 12 | import net.minecraft.util.Util; 13 | import net.minecraft.util.math.BlockPointer; 14 | import net.minecraft.util.math.BlockPos; 15 | import net.minecraft.util.math.Box; 16 | 17 | import java.util.List; 18 | import java.util.Optional; 19 | 20 | public class WaterBottleDispenserBehavior extends FallibleItemDispenserBehavior { 21 | @Override 22 | protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { 23 | this.setSuccess(true); 24 | 25 | ServerWorld serverWorld = pointer.world(); 26 | BlockPos blockPos = pointer.pos().offset(pointer.state().get(DispenserBlock.FACING)); 27 | 28 | // get all dyed shulkers in front of dispenser 29 | List list = serverWorld.getEntitiesByType(EntityType.SHULKER, new Box(blockPos), EntityPredicates.VALID_LIVING_ENTITY.and((livingEntity) -> { 30 | return ((ShulkerEntity) livingEntity).getColor() != null; 31 | })); 32 | 33 | // check if there are any shulkers 34 | if(!list.isEmpty()) { 35 | // get random shulker, set its color to undyed 36 | ShulkerEntity randomShulker = Util.getRandom(list, serverWorld.getRandom()); 37 | ((ShulkerEntityInvoker) randomShulker).invokeSetColor(Optional.empty()); 38 | 39 | // try to add new item to inventory, dispense if full 40 | return this.decrementStackWithRemainder(pointer, stack, new ItemStack(Items.GLASS_BOTTLE)); 41 | } 42 | 43 | // no dyed shulkers in front of dispenser 44 | this.setSuccess(false); 45 | return stack; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/dispenser/behaviors/WaterBucketDispenserBehavior.java: -------------------------------------------------------------------------------- 1 | package minitweaks.dispenser.behaviors; 2 | 3 | import net.minecraft.block.DispenserBlock; 4 | import net.minecraft.block.dispenser.FallibleItemDispenserBehavior; 5 | import net.minecraft.entity.Bucketable; 6 | import net.minecraft.entity.LivingEntity; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraft.predicate.entity.EntityPredicates; 9 | import net.minecraft.server.world.ServerWorld; 10 | import net.minecraft.util.Util; 11 | import net.minecraft.util.math.BlockPointer; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.util.math.Box; 14 | 15 | import java.util.List; 16 | 17 | public class WaterBucketDispenserBehavior extends FallibleItemDispenserBehavior { 18 | protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { 19 | this.setSuccess(true); 20 | 21 | ServerWorld serverWorld = pointer.world(); 22 | 23 | // get block in front of dispenser 24 | BlockPos blockPos = pointer.pos().offset(pointer.state().get(DispenserBlock.FACING)); 25 | // get all bucketable mobs in front of dispenser 26 | List list = serverWorld.getEntitiesByClass(LivingEntity.class, new Box(blockPos), EntityPredicates.VALID_LIVING_ENTITY.and((livingEntity) -> { 27 | return livingEntity instanceof Bucketable; 28 | })); 29 | 30 | if(!list.isEmpty()) { 31 | // get random bucketable mob in list 32 | LivingEntity livingEntity = Util.getRandom(list, serverWorld.getRandom()); 33 | Bucketable bucketable = (Bucketable) livingEntity; 34 | 35 | // play bucket sound, get bucket item 36 | livingEntity.playSound(bucketable.getBucketFillSound(), 1.0F, 1.0F); 37 | ItemStack mobBucketItem = bucketable.getBucketItem(); 38 | bucketable.copyDataToStack(mobBucketItem); 39 | 40 | // remove bucketed mob 41 | livingEntity.discard(); 42 | 43 | // return bucket item 44 | return mobBucketItem; 45 | } 46 | 47 | // fail to dispense 48 | this.setSuccess(false); 49 | return stack; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/all/AbstractBlockMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.all; 2 | 3 | import net.minecraft.block.AbstractBlock; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.entity.player.PlayerEntity; 6 | import net.minecraft.item.ItemStack; 7 | import net.minecraft.util.ActionResult; 8 | import net.minecraft.util.Hand; 9 | import net.minecraft.util.hit.BlockHitResult; 10 | import net.minecraft.util.math.BlockPos; 11 | import net.minecraft.world.World; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 | 17 | @Mixin(AbstractBlock.class) 18 | public abstract class AbstractBlockMixin { 19 | @Inject(method = "onUseWithItem", at = @At("HEAD"), cancellable = true) 20 | protected void onUseWithItemInject(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { 21 | // blank 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/anvil/crushing/AnvilBlockMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.anvil.crushing; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import minitweaks.util.AnvilCrushing; 5 | import net.minecraft.block.AnvilBlock; 6 | import net.minecraft.block.BlockState; 7 | import net.minecraft.block.FallingBlock; 8 | import net.minecraft.entity.FallingBlockEntity; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.world.World; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(AnvilBlock.class) 17 | public abstract class AnvilBlockMixin { 18 | @Inject(method = "onLanding", at = @At("HEAD")) 19 | private void convertBlocks(World world, BlockPos pos, BlockState fallingBlockState, BlockState currentStateInPos, FallingBlockEntity fallingBlockEntity, CallbackInfo ci) { 20 | // check if rule enabled 21 | if(MiniTweaksSettings.renewableRawOres) { 22 | AnvilCrushing.tryRawOreCrush(world, pos.down()); 23 | } 24 | } 25 | 26 | @Inject(method = "onDestroyedOnLanding", at = @At("HEAD")) 27 | private void convertLandingDestroyed(World world, BlockPos pos, FallingBlockEntity fallingBlockEntity, CallbackInfo ci) { 28 | // check if rule enabled and anvil can fall through block 29 | if(MiniTweaksSettings.renewableRawOres && FallingBlock.canFallThrough(world.getBlockState(pos))) { 30 | AnvilCrushing.tryRawOreCrush(world, pos.down()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/dispenser/behavior/DispenserBlockMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.dispenser.behavior; 2 | 3 | import minitweaks.dispenser.MiniTweaksDispenserBehaviors; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.DispenserBlock; 6 | import net.minecraft.block.dispenser.DispenserBehavior; 7 | import net.minecraft.block.entity.DispenserBlockEntity; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.server.world.ServerWorld; 10 | import net.minecraft.util.math.BlockPointer; 11 | import net.minecraft.util.math.BlockPos; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 17 | 18 | @Mixin(DispenserBlock.class) 19 | public abstract class DispenserBlockMixin { 20 | @Inject(method = "dispense", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/DispenserBlock;getBehaviorForItem(Lnet/minecraft/world/World;Lnet/minecraft/item/ItemStack;)Lnet/minecraft/block/dispenser/DispenserBehavior;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) 21 | private void getBlockInFront(ServerWorld world, BlockState state, BlockPos pos, CallbackInfo ci, DispenserBlockEntity dispenserBlockEntity, BlockPointer blockPointer, int i, ItemStack itemStack) { 22 | DispenserBehavior customBehavior = MiniTweaksDispenserBehaviors.getCustomDispenserBehavior(world, pos, blockPointer, dispenserBlockEntity, itemStack); 23 | if(customBehavior != null) { 24 | dispenserBlockEntity.setStack(i, customBehavior.dispense(blockPointer, itemStack)); 25 | ci.cancel(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/farmland/trample/FarmlandBlockMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.farmland.trample; 2 | 3 | import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.block.Block; 6 | import net.minecraft.block.BlockState; 7 | import net.minecraft.block.FarmlandBlock; 8 | import net.minecraft.enchantment.EnchantmentHelper; 9 | import net.minecraft.enchantment.Enchantments; 10 | import net.minecraft.entity.Entity; 11 | import net.minecraft.entity.LivingEntity; 12 | import net.minecraft.registry.RegistryKeys; 13 | import net.minecraft.util.math.BlockPos; 14 | import net.minecraft.world.World; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | 18 | @Mixin(FarmlandBlock.class) 19 | public abstract class FarmlandBlockMixin extends Block { 20 | protected FarmlandBlockMixin(Settings settings) { 21 | super(settings); 22 | } 23 | 24 | @WrapWithCondition(method = "onLandedUpon", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/FarmlandBlock;setToDirt(Lnet/minecraft/entity/Entity;Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;)V")) 25 | private boolean featherFallingCheck(Entity entity, BlockState state, World world, BlockPos pos) { 26 | return !(MiniTweaksSettings.noFeatherFallingTrample && entity instanceof LivingEntity livingEntity && EnchantmentHelper.getEquipmentLevel(world.getRegistryManager().getOrThrow(RegistryKeys.ENCHANTMENT).getEntry(Enchantments.FEATHER_FALLING.getValue()).get(), livingEntity) > 0); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/grindstone/curses/GrindstoneScreenHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.grindstone.curses; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.screen.GrindstoneScreenHandler; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(GrindstoneScreenHandler.class) 10 | public abstract class GrindstoneScreenHandlerMixin { 11 | // .filter() lambda in grind 12 | @SuppressWarnings("target") 13 | @ModifyExpressionValue(method = "method_58073", at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/entry/RegistryEntry;isIn(Lnet/minecraft/registry/tag/TagKey;)Z")) 14 | private static boolean grindCursedFilter(boolean original) { 15 | // filter out curses too 16 | return original && !MiniTweaksSettings.removableCurses; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/oxidizable/OxidizableBlockMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.oxidizable; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.FluidBlock; 6 | import net.minecraft.block.Oxidizable; 7 | import net.minecraft.block.OxidizableBlock; 8 | import net.minecraft.registry.tag.FluidTags; 9 | import net.minecraft.server.world.ServerWorld; 10 | import net.minecraft.util.math.BlockPos; 11 | import net.minecraft.util.math.Direction; 12 | import net.minecraft.util.math.random.Random; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | @Mixin(OxidizableBlock.class) 19 | public abstract class OxidizableBlockMixin { 20 | @Inject(method = "randomTick", at = @At("HEAD"), cancellable = true) 21 | private void onRandomTick(BlockState state, ServerWorld world, BlockPos pos, Random random, CallbackInfo ci) { 22 | // if rule is enabled, loop through adjacent blocks 23 | if(MiniTweaksSettings.fasterOxidation) { 24 | for(Direction dir : Direction.values()) { 25 | // check if any touching block has water (except down) 26 | if(dir != Direction.DOWN && world.getFluidState(pos.offset(dir)).isIn(FluidTags.WATER) && state.getBlock() instanceof Oxidizable oxidizable) { 27 | // get oxidation result and place block 28 | oxidizable.getDegradationResult(state).ifPresent(oxidizeState -> world.setBlockState(pos, oxidizeState)); 29 | ci.cancel(); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/piston/waterlog/PistonBlockEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.piston.waterlog; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.block.entity.PistonBlockEntity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(PistonBlockEntity.class) 10 | public abstract class PistonBlockEntityMixin { 11 | @ModifyExpressionValue(method = "tick", at = @At(value = "INVOKE", target = "Ljava/lang/Boolean;booleanValue()Z")) 12 | private static boolean checkWaterloggedState(boolean original) { 13 | return original && !MiniTweaksSettings.moveableWaterloggedBlocks; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/shrieker/activate/SculkShriekerBlockMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.shrieker.activate; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import minitweaks.mixins.block.all.AbstractBlockMixin; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.block.Blocks; 7 | import net.minecraft.block.SculkShriekerBlock; 8 | import net.minecraft.entity.player.PlayerEntity; 9 | import net.minecraft.item.ItemStack; 10 | import net.minecraft.item.Items; 11 | import net.minecraft.sound.SoundCategory; 12 | import net.minecraft.sound.SoundEvents; 13 | import net.minecraft.util.ActionResult; 14 | import net.minecraft.util.Hand; 15 | import net.minecraft.util.hit.BlockHitResult; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.world.World; 18 | import org.spongepowered.asm.mixin.Mixin; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 20 | 21 | @Mixin(SculkShriekerBlock.class) 22 | public abstract class SculkShriekerBlockMixin extends AbstractBlockMixin { 23 | @Override 24 | protected void onUseWithItemInject(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { 25 | // check if rule is enabled, item is echo shard, and is being used on a sculk shrieker with state can_summon=false 26 | if(MiniTweaksSettings.echoShardsEnableShriekers && stack.isOf(Items.ECHO_SHARD) && state.isOf(Blocks.SCULK_SHRIEKER) && !state.get(SculkShriekerBlock.CAN_SUMMON)) { 27 | // remove echo shard if in survival 28 | if(!player.getAbilities().creativeMode) { 29 | stack.decrement(1); 30 | } 31 | // set can_summon state to true and play warden roar 32 | world.setBlockState(pos, state.with(SculkShriekerBlock.CAN_SUMMON, true)); 33 | world.playSound(player, pos, SoundEvents.ENTITY_WARDEN_ROAR, SoundCategory.BLOCKS); 34 | 35 | // swing arm 36 | cir.setReturnValue(ActionResult.SUCCESS); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/block/snow/shave/ShovelItemMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.block.snow.shave; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.block.Blocks; 7 | import net.minecraft.block.SnowBlock; 8 | import net.minecraft.enchantment.EnchantmentHelper; 9 | import net.minecraft.enchantment.Enchantments; 10 | import net.minecraft.entity.LivingEntity; 11 | import net.minecraft.entity.player.PlayerEntity; 12 | import net.minecraft.item.ItemStack; 13 | import net.minecraft.item.ItemUsageContext; 14 | import net.minecraft.item.Items; 15 | import net.minecraft.item.ShovelItem; 16 | import net.minecraft.registry.RegistryKeys; 17 | import net.minecraft.sound.SoundCategory; 18 | import net.minecraft.sound.SoundEvents; 19 | import net.minecraft.util.ActionResult; 20 | import net.minecraft.util.math.BlockPos; 21 | import net.minecraft.world.World; 22 | import net.minecraft.world.event.GameEvent; 23 | import org.spongepowered.asm.mixin.Mixin; 24 | import org.spongepowered.asm.mixin.injection.At; 25 | import org.spongepowered.asm.mixin.injection.Inject; 26 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 27 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 28 | 29 | @Mixin(ShovelItem.class) 30 | public abstract class ShovelItemMixin { 31 | @Inject(method = "useOnBlock", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) 32 | private void shaveSnowLayer(ItemUsageContext context, CallbackInfoReturnable cir, World world, BlockPos blockPos, BlockState blockState, PlayerEntity playerEntity) { 33 | if(MiniTweaksSettings.shaveSnowLayers && !world.isClient && blockState.isOf(Blocks.SNOW)) { 34 | int layers = blockState.get(SnowBlock.LAYERS); 35 | ItemStack tool = context.getStack(); 36 | boolean hasSilkTouch = EnchantmentHelper.getLevel(world.getRegistryManager().getOrThrow(RegistryKeys.ENCHANTMENT).getEntry(Enchantments.SILK_TOUCH.getValue()).get(), tool) > 0; 37 | // set to air if only one snow layer remains, otherwise remove one layer 38 | BlockState shavedBlockState = layers > 1 ? blockState.with(SnowBlock.LAYERS, layers - 1) : Blocks.AIR.getDefaultState(); 39 | 40 | world.setBlockState(blockPos, shavedBlockState, Block.NOTIFY_ALL_AND_REDRAW); 41 | world.emitGameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Emitter.of(playerEntity, shavedBlockState)); 42 | 43 | // drop snow layer if silk touch is used, otherwise drop snowball 44 | Block.dropStack(world, blockPos, new ItemStack(hasSilkTouch ? Items.SNOW : Items.SNOWBALL)); 45 | world.playSound(null, blockPos, SoundEvents.BLOCK_SNOW_BREAK, SoundCategory.BLOCKS, 1.0F, 1.0F); 46 | 47 | // damage tool 48 | if(playerEntity != null) { 49 | tool.damage(1, playerEntity, LivingEntity.getSlotForHand(context.getHand())); 50 | } 51 | 52 | // return success (swing arm) 53 | cir.setReturnValue(ActionResult.SUCCESS_SERVER); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/command/seed/permission/SeedCommandMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.command.seed.permission; 2 | 3 | import carpet.utils.CommandHelper; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.server.command.SeedCommand; 6 | import net.minecraft.server.command.ServerCommandSource; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Redirect; 10 | 11 | @Mixin(SeedCommand.class) 12 | public abstract class SeedCommandMixin { 13 | // .requres() lambda in register method 14 | @SuppressWarnings("target") 15 | @Redirect(method = "method_13618(ZLnet/minecraft/server/command/ServerCommandSource;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/command/ServerCommandSource;hasPermissionLevel(I)Z")) 16 | private static boolean permissionLevelCheck(ServerCommandSource serverCommandSource, int original) { 17 | return CommandHelper.canUseCommand(serverCommandSource, MiniTweaksSettings.commandSeed); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/crafting/curses/RepairItemRecipeMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.crafting.curses; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.item.ItemStack; 6 | import net.minecraft.recipe.RepairItemRecipe; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | @Mixin(RepairItemRecipe.class) 13 | public abstract class RepairItemRecipeMixin { 14 | @Inject(method = "craft", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;setDamage(I)V", shift = At.Shift.AFTER), cancellable = true) 15 | private void craftCursedFilter(CallbackInfoReturnable cir, @Local(ordinal = 2) ItemStack itemStack4) { 16 | // skip checking for curses and adding enchants to crafted item and just return output item 17 | if(MiniTweaksSettings.removableCurses) { 18 | cir.setReturnValue(itemStack4); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/item/hoe/harvest/HoeItemMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.item.hoe.harvest; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.block.Blocks; 7 | import net.minecraft.block.CocoaBlock; 8 | import net.minecraft.block.CropBlock; 9 | import net.minecraft.block.NetherWartBlock; 10 | import net.minecraft.entity.player.PlayerEntity; 11 | import net.minecraft.item.HoeItem; 12 | import net.minecraft.item.ItemStack; 13 | import net.minecraft.item.ItemUsageContext; 14 | import net.minecraft.server.world.ServerWorld; 15 | import net.minecraft.util.ActionResult; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.world.World; 18 | import org.spongepowered.asm.mixin.Mixin; 19 | import org.spongepowered.asm.mixin.injection.At; 20 | import org.spongepowered.asm.mixin.injection.Inject; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 22 | 23 | import java.util.List; 24 | 25 | @Mixin(HoeItem.class) 26 | public abstract class HoeItemMixin { 27 | @Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true) 28 | private void harvestCrop(ItemUsageContext context, CallbackInfoReturnable cir) { 29 | World world = context.getWorld(); 30 | // check if rule is enabled and action is server side 31 | if(MiniTweaksSettings.quickHarvesting && !world.isClient) { 32 | BlockPos pos = context.getBlockPos(); 33 | BlockState state = world.getBlockState(pos); 34 | PlayerEntity player = context.getPlayer(); 35 | 36 | // check if crop is mature 37 | if(isMature(state)) { 38 | // get usage tool (for fortune to apply) 39 | ItemStack tool = player != null ? player.getStackInHand(context.getHand()) : ItemStack.EMPTY; 40 | // get loot drops for crop 41 | List droppedItems = Block.getDroppedStacks(state, (ServerWorld) world, pos, null, player, tool); 42 | boolean removedSeed = false; 43 | for(ItemStack itemStack : droppedItems) { 44 | // if a seed hasn't been removed and item being dropped is the same as the crop being harvested, remove seed 45 | if(!removedSeed && state.isOf(Block.getBlockFromItem(itemStack.getItem()))) { 46 | // remove seed and set removed to true 47 | itemStack.decrement(1); 48 | removedSeed = true; 49 | } 50 | // drop item 51 | Block.dropStack(world, pos, itemStack); 52 | } 53 | 54 | // create block breaking sound and particles 55 | world.breakBlock(pos, false, player); 56 | 57 | // if seed was removed from drops, update seed age to 0, otherwise place air 58 | BlockState newCropState = getNewCrop(state); 59 | BlockState postHarvestState = removedSeed && newCropState != null ? newCropState : Blocks.AIR.getDefaultState(); 60 | world.setBlockState(pos, postHarvestState); 61 | 62 | // return success (swing arm) 63 | cir.setReturnValue(ActionResult.SUCCESS_SERVER); 64 | } 65 | } 66 | } 67 | 68 | // check if crop is mature. Also returns false for invalid blocks being clicked 69 | private static boolean isMature(BlockState state) { 70 | Block block = state.getBlock(); 71 | if(block instanceof CropBlock cropBlock) { 72 | return cropBlock.isMature(state); 73 | } 74 | else if(block instanceof NetherWartBlock) { 75 | return state.get(NetherWartBlock.AGE) == 3; 76 | } 77 | else if(block instanceof CocoaBlock) { 78 | return state.get(CocoaBlock.AGE) == 2; 79 | } 80 | return false; 81 | } 82 | 83 | // get age 0 crop 84 | private static BlockState getNewCrop(BlockState blockState) { 85 | Block block = blockState.getBlock(); 86 | 87 | if(block instanceof CropBlock cropBlock) { 88 | return cropBlock.withAge(0); 89 | } 90 | else if(block instanceof NetherWartBlock) { 91 | return blockState.with(NetherWartBlock.AGE, 0); 92 | } 93 | else if(block instanceof CocoaBlock) { 94 | return blockState.with(CocoaBlock.AGE, 0); 95 | } 96 | return null; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/all/interact/MobEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.all.interact; 2 | 3 | import net.minecraft.entity.EntityType; 4 | import net.minecraft.entity.LivingEntity; 5 | import net.minecraft.entity.mob.MobEntity; 6 | import net.minecraft.entity.player.PlayerEntity; 7 | import net.minecraft.util.ActionResult; 8 | import net.minecraft.util.Hand; 9 | import net.minecraft.world.World; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(MobEntity.class) 16 | public abstract class MobEntityMixin extends LivingEntity { 17 | protected MobEntityMixin(EntityType entityType, World world) { 18 | super(entityType, world); 19 | } 20 | 21 | @Inject(method = "interactMob", at = @At("HEAD"), cancellable = true) 22 | protected void interactMobInject(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { 23 | // blank 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/all/lightning/EntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.all.lightning; 2 | 3 | import net.minecraft.entity.Entity; 4 | import net.minecraft.entity.LightningEntity; 5 | import net.minecraft.server.world.ServerWorld; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(Entity.class) 12 | public abstract class EntityMixin { 13 | @Inject(method = "onStruckByLightning", at = @At("HEAD"), cancellable = true) 14 | protected void lightningStrikeInject(ServerWorld world, LightningEntity lightning, CallbackInfo ci) { 15 | // blank 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/all/nametag/LivingEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.all.nametag; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.component.DataComponentTypes; 5 | import net.minecraft.entity.Entity; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.LivingEntity; 8 | import net.minecraft.entity.damage.DamageSource; 9 | import net.minecraft.item.ItemStack; 10 | import net.minecraft.item.Items; 11 | import net.minecraft.server.world.ServerWorld; 12 | import net.minecraft.world.World; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | @Mixin(LivingEntity.class) 19 | public abstract class LivingEntityMixin extends Entity { 20 | protected LivingEntityMixin(EntityType type, World world) { 21 | super(type, world); 22 | } 23 | 24 | @Inject(method = "drop", at = @At("TAIL")) 25 | private void dropNameTag(ServerWorld world, DamageSource damageSource, CallbackInfo ci) { 26 | // if rule is enabled and mob has custom name 27 | if(MiniTweaksSettings.mobsDropNametag && this.hasCustomName()) { 28 | // create name tag 29 | ItemStack nameTag = new ItemStack(Items.NAME_TAG); 30 | // set name tag to mob's name 31 | nameTag.set(DataComponentTypes.CUSTOM_NAME, this.getCustomName()); 32 | // drop item 33 | this.dropStack(world, nameTag); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/allay/duplicate/AllayEntityInvoker.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.allay.duplicate; 2 | 3 | import net.minecraft.entity.passive.AllayEntity; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(AllayEntity.class) 8 | public interface AllayEntityInvoker { 9 | @Invoker("canDuplicate") 10 | boolean invokeCanDuplicate(); 11 | 12 | @Invoker("duplicate") 13 | void invokeDuplicate(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/blaze/fire/SmallFireballEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.blaze.fire; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.projectile.SmallFireballEntity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(SmallFireballEntity.class) 10 | public abstract class SmallFireballEntityMixin { 11 | @ModifyExpressionValue(method = "onBlockHit", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameRules;getBoolean(Lnet/minecraft/world/GameRules$Key;)Z")) 12 | private boolean preventFire(boolean original) { 13 | return original && !MiniTweaksSettings.disableBlazeFire; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/creeper/block_damage/CreeperEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.creeper.block_damage; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.mob.CreeperEntity; 6 | import net.minecraft.world.World.ExplosionSourceType; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(value = CreeperEntity.class, priority = 1001) 11 | public abstract class CreeperEntityMixin { 12 | @ModifyExpressionValue(method = "explode", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World$ExplosionSourceType;MOB:Lnet/minecraft/world/World$ExplosionSourceType;")) 13 | private ExplosionSourceType modifiedExplode(ExplosionSourceType original) { 14 | return MiniTweaksSettings.noCreeperBlockBreaking ? ExplosionSourceType.NONE : original; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/creeper/head_drops/CreeperEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.creeper.head_drops; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.entity.mob.CreeperEntity; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 | 11 | @Mixin(CreeperEntity.class) 12 | public abstract class CreeperEntityMixin { 13 | 14 | @Shadow 15 | abstract boolean isCharged(); 16 | 17 | @Inject(method = "shouldDropHead", at = @At("HEAD"), cancellable = true) 18 | private void allHeadsDrop(CallbackInfoReturnable cir) { 19 | if(this.isCharged() && MiniTweaksSettings.allChargedCreeperHeadsDrop) { 20 | cir.setReturnValue(true); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/dragon/block_damage/EnderDragonEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.dragon.block_damage; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 5 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 6 | import minitweaks.MiniTweaksSettings; 7 | import minitweaks.MiniTweaksSettings.BlockBreakingType; 8 | import net.minecraft.entity.boss.dragon.EnderDragonEntity; 9 | import net.minecraft.server.world.ServerWorld; 10 | import net.minecraft.util.math.BlockPos; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | 14 | @Mixin(EnderDragonEntity.class) 15 | public abstract class EnderDragonEntityMixin { 16 | @ModifyExpressionValue(method = "destroyBlocks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameRules;getBoolean(Lnet/minecraft/world/GameRules$Key;)Z")) 17 | private boolean gameruleCheck(boolean original) { 18 | return switch(MiniTweaksSettings.dragonBlockDamage) { 19 | case NONE -> false; 20 | case BREAK, DESTROY -> true; 21 | case DEFAULT -> original; 22 | }; 23 | } 24 | 25 | @WrapOperation(method = "destroyBlocks", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;removeBlock(Lnet/minecraft/util/math/BlockPos;Z)Z")) 26 | private boolean destroyType(ServerWorld world, BlockPos pos, boolean move, Operation original) { 27 | if(MiniTweaksSettings.dragonBlockDamage == BlockBreakingType.BREAK) { 28 | // break block and drop as item 29 | return world.breakBlock(pos, true, (EnderDragonEntity) (Object) this); 30 | } 31 | 32 | // default block removal 33 | return original.call(world, pos, move); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/dragon/egg/EnderDragonFightMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.dragon.egg; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.boss.dragon.EnderDragonFight; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(EnderDragonFight.class) 10 | public abstract class EnderDragonFightMixin { 11 | @ModifyExpressionValue(method = "dragonKilled", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/boss/dragon/EnderDragonFight;previouslyKilled:Z", ordinal = 0)) 12 | private boolean shouldGenerateEgg(boolean original) { 13 | return original && !MiniTweaksSettings.renewableDragonEgg; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/ghast/block_damage/FireballEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.ghast.block_damage; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.projectile.FireballEntity; 6 | import net.minecraft.world.World.ExplosionSourceType; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(FireballEntity.class) 11 | public abstract class FireballEntityMixin { 12 | @ModifyExpressionValue(method = "onCollision", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World$ExplosionSourceType;MOB:Lnet/minecraft/world/World$ExplosionSourceType;")) 13 | private ExplosionSourceType modifyDestructionType(ExplosionSourceType original) { 14 | return MiniTweaksSettings.noGhastBlockBreaking ? ExplosionSourceType.NONE : original; 15 | } 16 | 17 | @ModifyExpressionValue(method = "onCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameRules;getBoolean(Lnet/minecraft/world/GameRules$Key;)Z")) 18 | private boolean modifyCreateFire(boolean createFire) { 19 | return createFire && !MiniTweaksSettings.disableGhastFire; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/phantom/spawning/PhantomSpawnerMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.phantom.spawning; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.world.spawner.PhantomSpawner; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.Constant; 7 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 8 | 9 | @Mixin(value = PhantomSpawner.class, priority = 999) 10 | public abstract class PhantomSpawnerMixin { 11 | @ModifyConstant(method = "spawn", constant = @Constant(intValue = 72000), require = 0, expect = 1) 12 | private int phantomSpawnTime(int original) { 13 | return MiniTweaksSettings.phantomSpawningTime; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/pickup/SkeletonZombieEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.pickup; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import minitweaks.MiniTweaksSettings.ItemPickupType; 5 | import net.minecraft.entity.EntityData; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.SpawnReason; 8 | import net.minecraft.entity.mob.AbstractSkeletonEntity; 9 | import net.minecraft.entity.mob.HostileEntity; 10 | import net.minecraft.entity.mob.ZombieEntity; 11 | import net.minecraft.world.LocalDifficulty; 12 | import net.minecraft.world.ServerWorldAccess; 13 | import net.minecraft.world.World; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 18 | 19 | @Mixin({ 20 | AbstractSkeletonEntity.class, 21 | ZombieEntity.class 22 | }) 23 | public abstract class SkeletonZombieEntityMixin extends HostileEntity { 24 | protected SkeletonZombieEntityMixin(EntityType entityType, World world) { 25 | super(entityType, world); 26 | } 27 | 28 | @Inject(method = "initialize", at = @At("TAIL")) 29 | private void modifyLootPickup(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, EntityData entityData, CallbackInfoReturnable cir) { 30 | // checks if rule is enabled (not default) 31 | if(MiniTweaksSettings.mobItemPickup != ItemPickupType.DEFAULT) { 32 | // sets the mob's item pickup ability (true if always, false if never) 33 | this.setCanPickUpLoot(MiniTweaksSettings.mobItemPickup == ItemPickupType.ALWAYS); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/player/drops/ItemEntityAccessor.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.player.drops; 2 | 3 | import net.minecraft.entity.ItemEntity; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(ItemEntity.class) 8 | public interface ItemEntityAccessor { 9 | // allows ItemEntity.age to be changed 10 | @Accessor("itemAge") 11 | void setItemAge(int itemAge); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/player/drops/PlayerInventoryMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.player.drops; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.ItemEntity; 6 | import net.minecraft.entity.player.PlayerInventory; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(PlayerInventory.class) 11 | public abstract class PlayerInventoryMixin { 12 | @ModifyExpressionValue(method = "dropAll", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;")) 13 | private ItemEntity modifyAge(ItemEntity droppedItem) { 14 | // check for null since player.dropItem() is nullable 15 | if(droppedItem != null) { 16 | // modifies age 17 | ((ItemEntityAccessor) droppedItem).setItemAge(minutesToTicks(MiniTweaksSettings.deathItemsDespawnMinutes)); 18 | } 19 | return droppedItem; 20 | } 21 | 22 | private static int minutesToTicks(int despawnMinutes) { 23 | // if minutes is -1, return -32768 (infinite age, see ItemEntity.tick()) 24 | if (despawnMinutes == -1) { 25 | return -32768; 26 | } 27 | // converts minutes to ticks and subtracts from default despawn time 28 | return 6000 - (despawnMinutes * 60 * 20); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/player/xp/PlayerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.player.xp; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.entity.player.PlayerEntity; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.Constant; 7 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 8 | 9 | @Mixin(PlayerEntity.class) 10 | public abstract class PlayerEntityMixin { 11 | @ModifyConstant(method = "getExperienceToDrop", constant = @Constant(intValue = 100), expect = 2) 12 | private int modifyDropCount(int original) { 13 | return MiniTweaksSettings.maxPlayerXpDrop; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/sheep/dye/DyeItemMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.sheep.dye; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.item.DyeItem; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(DyeItem.class) 10 | public class DyeItemMixin { 11 | @ModifyExpressionValue(method = "useOnEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;isSheared()Z")) 12 | private boolean alwaysDyeSheep(boolean original) { 13 | return original && !MiniTweaksSettings.dyeableShearedSheep; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/shulker/dye/DyeItemMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.shulker.dye; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.entity.LivingEntity; 5 | import net.minecraft.entity.mob.ShulkerEntity; 6 | import net.minecraft.entity.player.PlayerEntity; 7 | import net.minecraft.item.DyeItem; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.sound.SoundCategory; 10 | import net.minecraft.sound.SoundEvents; 11 | import net.minecraft.util.ActionResult; 12 | import net.minecraft.util.DyeColor; 13 | import net.minecraft.util.Hand; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 19 | 20 | import java.util.Optional; 21 | 22 | @Mixin(DyeItem.class) 23 | public abstract class DyeItemMixin { 24 | @Shadow 25 | abstract DyeColor getColor(); 26 | 27 | @Inject(method = "useOnEntity", at = @At("HEAD"), cancellable = true) 28 | private void dyeShulkers(ItemStack stack, PlayerEntity user, LivingEntity entity, Hand hand, CallbackInfoReturnable cir) { 29 | if(MiniTweaksSettings.dyeableShulkers && entity instanceof ShulkerEntity shulkerEntity) { 30 | DyeColor dyeItemColor = this.getColor(); 31 | DyeColor currentShulkerColor = shulkerEntity.getColor(); 32 | 33 | // checks if shulker is alive and current color is different than the dye's color 34 | if(shulkerEntity.isAlive() && currentShulkerColor != dyeItemColor) { 35 | shulkerEntity.getWorld().playSoundFromEntity(user, shulkerEntity, SoundEvents.ITEM_DYE_USE, SoundCategory.PLAYERS, 1.0F, 1.0F); 36 | if(!user.getWorld().isClient) { 37 | ((ShulkerEntityInvoker) shulkerEntity).invokeSetColor(Optional.of(dyeItemColor)); 38 | stack.decrement(1); 39 | } 40 | 41 | cir.setReturnValue(ActionResult.SUCCESS); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/shulker/dye/ShulkerEntityInvoker.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.shulker.dye; 2 | 3 | import net.minecraft.entity.mob.ShulkerEntity; 4 | import net.minecraft.util.DyeColor; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Invoker; 7 | 8 | import java.util.Optional; 9 | 10 | @Mixin(ShulkerEntity.class) 11 | public interface ShulkerEntityInvoker { 12 | @Invoker("setColor") 13 | void invokeSetColor(Optional color); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/shulker/dye/ShulkerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.shulker.dye; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import minitweaks.mixins.mob.all.interact.MobEntityMixin; 5 | import net.minecraft.component.DataComponentTypes; 6 | import net.minecraft.component.type.PotionContentsComponent; 7 | import net.minecraft.entity.EntityType; 8 | import net.minecraft.entity.LivingEntity; 9 | import net.minecraft.entity.mob.ShulkerEntity; 10 | import net.minecraft.entity.player.PlayerEntity; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.item.ItemUsage; 13 | import net.minecraft.item.Items; 14 | import net.minecraft.potion.Potions; 15 | import net.minecraft.sound.SoundCategory; 16 | import net.minecraft.sound.SoundEvents; 17 | import net.minecraft.util.ActionResult; 18 | import net.minecraft.util.DyeColor; 19 | import net.minecraft.util.Hand; 20 | import net.minecraft.world.World; 21 | import org.spongepowered.asm.mixin.Mixin; 22 | import org.spongepowered.asm.mixin.Shadow; 23 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 24 | 25 | import java.util.Optional; 26 | 27 | @Mixin(ShulkerEntity.class) 28 | public abstract class ShulkerEntityMixin extends MobEntityMixin { 29 | protected ShulkerEntityMixin(EntityType entityType, World world) { 30 | super(entityType, world); 31 | } 32 | 33 | @Shadow 34 | abstract DyeColor getColor(); 35 | 36 | 37 | @Override 38 | protected void interactMobInject(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { 39 | // check if rule is enabled, server side, and shulker has a color 40 | if(MiniTweaksSettings.dyeableShulkers && !this.getWorld().isClient && this.getColor() != null) { 41 | ItemStack stack = player.getStackInHand(hand); 42 | 43 | // check if item used is a water bottle 44 | if(stack.isOf(Items.POTION) && stack.getOrDefault(DataComponentTypes.POTION_CONTENTS, PotionContentsComponent.DEFAULT).matches(Potions.WATER)) { 45 | // set color to none 46 | ((ShulkerEntityInvoker) this).invokeSetColor(Optional.empty()); 47 | 48 | // play sound, give empty bottle 49 | this.getWorld().playSound(null, this.getBlockPos(), SoundEvents.ITEM_BOTTLE_EMPTY, SoundCategory.BLOCKS, 1.0f, 1.0f); 50 | player.setStackInHand(hand, ItemUsage.exchangeStack(stack, player, new ItemStack(Items.GLASS_BOTTLE))); 51 | 52 | // swing hand 53 | cir.setReturnValue(ActionResult.SUCCESS); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/slime/looting/SlimeEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.slime.looting; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.enchantment.EnchantmentHelper; 6 | import net.minecraft.enchantment.Enchantments; 7 | import net.minecraft.entity.EntityType; 8 | import net.minecraft.entity.mob.MobEntity; 9 | import net.minecraft.entity.mob.SlimeEntity; 10 | import net.minecraft.entity.player.PlayerEntity; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.registry.RegistryKeys; 13 | import net.minecraft.world.World; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | 17 | @Mixin(SlimeEntity.class) 18 | public abstract class SlimeEntityMixin extends MobEntity { 19 | protected SlimeEntityMixin(EntityType type, World world) { 20 | super(type, world); 21 | } 22 | 23 | @ModifyExpressionValue(method = "remove", at = @At(value = "CONSTANT", args = "intValue=3")) 24 | private int addLootingLevel(int original) { 25 | if(MiniTweaksSettings.slimeLooting && this.attackingPlayer != null) { 26 | PlayerEntity playerEntity = this.attackingPlayer.resolve(this.getWorld(), PlayerEntity.class); 27 | ItemStack weapon = playerEntity.getWeaponStack(); 28 | int lootingLevel = EnchantmentHelper.getLevel(this.getWorld().getRegistryManager().getOrThrow(RegistryKeys.ENCHANTMENT).getEntry(Enchantments.LOOTING.getValue()).get(), weapon); 29 | return original + lootingLevel; 30 | } 31 | return original; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/snowgolem/melt/SnowGolemEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.snowgolem.melt; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.passive.SnowGolemEntity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(SnowGolemEntity.class) 10 | public abstract class SnowGolemEntityMixin { 11 | @ModifyExpressionValue(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/entry/RegistryEntry;isIn(Lnet/minecraft/registry/tag/TagKey;)Z")) 12 | private boolean isHotRedirect(boolean original) { 13 | return original && !MiniTweaksSettings.noSnowGolemMelting; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/squid/lightning/SquidEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.squid.lightning; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import minitweaks.mixins.mob.all.lightning.EntityMixin; 5 | import net.minecraft.entity.EntityType; 6 | import net.minecraft.entity.LightningEntity; 7 | import net.minecraft.entity.conversion.EntityConversionContext; 8 | import net.minecraft.entity.passive.SquidEntity; 9 | import net.minecraft.server.world.ServerWorld; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(SquidEntity.class) 14 | public abstract class SquidEntityMixin extends EntityMixin { 15 | 16 | @Override 17 | protected void lightningStrikeInject(ServerWorld world, LightningEntity lightning, CallbackInfo ci) { 18 | if(MiniTweaksSettings.lightningGlowifiesSquids) { 19 | SquidEntity squid = (SquidEntity) (Object) this; 20 | squid.convertTo(EntityType.GLOW_SQUID, EntityConversionContext.create(squid, true, true), (glowSquid) -> {}); 21 | 22 | ci.cancel(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/vex/nerf/VexEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.vex.nerf; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.entity.EntityType; 5 | import net.minecraft.entity.mob.HostileEntity; 6 | import net.minecraft.entity.mob.MobEntity; 7 | import net.minecraft.entity.mob.VexEntity; 8 | import net.minecraft.world.World; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(VexEntity.class) 16 | public abstract class VexEntityMixin extends HostileEntity { 17 | protected VexEntityMixin(EntityType entityType, World world) { 18 | super(entityType, world); 19 | } 20 | 21 | @Shadow 22 | abstract MobEntity getOwner(); 23 | 24 | @Shadow 25 | abstract void setOwner(MobEntity owner); 26 | 27 | @Shadow 28 | abstract void setLifeTicks(int lifeTicks); 29 | 30 | 31 | @Inject(method = "tick", at = @At("HEAD")) 32 | private void checkOwnerDeath(CallbackInfo ci) { 33 | if(MiniTweaksSettings.vexesNerf) { 34 | MobEntity owner = this.getOwner(); 35 | // check if owner is dead 36 | if(owner != null && !owner.isAlive()) { 37 | // set owner to null so this code doesn't keep running 38 | this.setOwner(null); 39 | // set remaining life time to 1 to 5 seconds 40 | this.setLifeTicks(20 + this.random.nextInt(80)); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/villager/bed_explode/VillagerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.villager.bed_explode; 2 | 3 | import minitweaks.MiniTweaksSettings; 4 | import net.minecraft.block.BedBlock; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.passive.MerchantEntity; 8 | import net.minecraft.entity.passive.VillagerEntity; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.util.math.Vec3d; 11 | import net.minecraft.world.World; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | @Mixin(VillagerEntity.class) 18 | public abstract class VillagerEntityMixin extends MerchantEntity { 19 | public VillagerEntityMixin(EntityType entityType, World world) { 20 | super(entityType, world); 21 | } 22 | 23 | @Inject(method = "sleep", at = @At("HEAD"), cancellable = true) 24 | private void explodeBed(BlockPos pos, CallbackInfo ci) { 25 | World world = this.getWorld(); 26 | // if rule enabled and beds explode in dimension 27 | if(MiniTweaksSettings.villagersExplodeBeds && !world.getDimension().bedWorks()) { 28 | // remove bed 29 | BlockState state = world.getBlockState(pos); 30 | world.removeBlock(pos, false); 31 | BlockPos blockPos = pos.offset((state.get(BedBlock.FACING)).getOpposite()); 32 | if(world.getBlockState(blockPos).getBlock() instanceof BedBlock) { 33 | world.removeBlock(blockPos, false); 34 | } 35 | 36 | // create explosion 37 | Vec3d vec3d = pos.toCenterPos(); 38 | world.createExplosion(null, world.getDamageSources().badRespawnPoint(vec3d), null, vec3d, 5.0F, true, World.ExplosionSourceType.BLOCK); 39 | 40 | // cancel sleeping 41 | ci.cancel(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/villager/lightning/VillagerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.villager.lightning; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.EntityType; 6 | import net.minecraft.entity.passive.MerchantEntity; 7 | import net.minecraft.entity.passive.VillagerEntity; 8 | import net.minecraft.world.Difficulty; 9 | import net.minecraft.world.World; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | 13 | @Mixin(VillagerEntity.class) 14 | public abstract class VillagerEntityMixin extends MerchantEntity { 15 | public VillagerEntityMixin(EntityType entityType, World world) { 16 | super(entityType, world); 17 | } 18 | 19 | @ModifyExpressionValue(method = "onStruckByLightning",at = @At(value = "INVOKE",target = "Lnet/minecraft/server/world/ServerWorld;getDifficulty()Lnet/minecraft/world/Difficulty;")) 20 | private Difficulty lightningWitchConversion(Difficulty original) { 21 | return MiniTweaksSettings.noVillagerWitchConversion ? Difficulty.PEACEFUL : original; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/zombie/convert/ZombieEntityMixin.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.zombie.convert; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import minitweaks.MiniTweaksSettings; 5 | import net.minecraft.entity.mob.ZombieEntity; 6 | import net.minecraft.world.Difficulty; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(ZombieEntity.class) 11 | public abstract class ZombieEntityMixin { 12 | @ModifyExpressionValue(method = "onKilledOther", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;getDifficulty()Lnet/minecraft/world/Difficulty;"), expect = 3) 13 | private Difficulty forceZombieVillager(Difficulty original) { 14 | return MiniTweaksSettings.villagersAlwaysConvert ? Difficulty.HARD : original; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/mixins/mob/zombie/convert/ZombieVillagerEntityInvoker.java: -------------------------------------------------------------------------------- 1 | package minitweaks.mixins.mob.zombie.convert; 2 | 3 | import net.minecraft.entity.mob.ZombieVillagerEntity; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | import java.util.UUID; 8 | 9 | @Mixin(ZombieVillagerEntity.class) 10 | public interface ZombieVillagerEntityInvoker { 11 | // allows ZombieVillagerEntity.setConverting() to be used 12 | @Invoker("setConverting") 13 | void invokeSetConverting(UUID uuid, int delay); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/minitweaks/util/AnvilCrushing.java: -------------------------------------------------------------------------------- 1 | package minitweaks.util; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.Blocks; 6 | import net.minecraft.util.math.BlockPos; 7 | import net.minecraft.world.World; 8 | 9 | import java.util.Map; 10 | 11 | public class AnvilCrushing { 12 | private static final Map ANVIL_BLOCK_TO_RAW_ORES = new ImmutableMap.Builder() 13 | // iron, gold 14 | .put(Blocks.IRON_BLOCK, Blocks.RAW_IRON_BLOCK) 15 | .put(Blocks.GOLD_BLOCK, Blocks.RAW_GOLD_BLOCK) 16 | // (non-waxed) copper blocks 17 | .put(Blocks.COPPER_BLOCK, Blocks.RAW_COPPER_BLOCK) 18 | .put(Blocks.EXPOSED_COPPER, Blocks.RAW_COPPER_BLOCK) 19 | .put(Blocks.WEATHERED_COPPER, Blocks.RAW_COPPER_BLOCK) 20 | .put(Blocks.OXIDIZED_COPPER, Blocks.RAW_COPPER_BLOCK) 21 | // create map 22 | .build(); 23 | 24 | public static void tryRawOreCrush(World world, BlockPos pos) { 25 | // get block below landing position 26 | Block convertedBlock = ANVIL_BLOCK_TO_RAW_ORES.get(world.getBlockState(pos).getBlock()); 27 | 28 | // check if converted 29 | if(convertedBlock != null) { 30 | // break block and set to new block 31 | world.breakBlock(pos, false); 32 | world.setBlockState(pos, convertedBlock.getDefaultState()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/assets/minitweaks/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manyrandomthings/minitweaks/15691919818b51915b6a3da4c473cd49e39b39fe/src/main/resources/assets/minitweaks/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/minitweaks/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "minitweaks.rule.allChargedCreeperHeadsDrop.desc": "All mobs killed by a charged creeper drop their head instead of only one", 3 | 4 | "minitweaks.rule.commandSeed.desc": "Permission level required to use /seed", 5 | "minitweaks.rule.commandSeed.extra.0": "Only available server-side", 6 | "minitweaks.rule.commandSeed.extra.1": "ops/2 for only ops, true/0 for anyone", 7 | 8 | "minitweaks.rule.deathItemsDespawnMinutes.desc": "How many minutes it takes for a player's items to despawn after death", 9 | "minitweaks.rule.deathItemsDespawnMinutes.extra.0": "-1 for infinte, 0 for instant despawn, max value 32", 10 | 11 | "minitweaks.rule.disableBlazeFire.desc": "Disables fire made from blaze fireballs", 12 | 13 | "minitweaks.rule.disableGhastFire.desc": "Disables fire made from ghast fireballs", 14 | 15 | "minitweaks.rule.dispensersBucketMobs.desc": "Dispensers can pick up bucketable mobs", 16 | 17 | "minitweaks.rule.dispensersCureVillagers.desc": "Dispensers feed golden apples to zombie villagers with weakness", 18 | "minitweaks.rule.dispensersCureVillagers.extra.0": "Note: dispensers curing a villager does not lower the villager's prices due to gossips being player-specific", 19 | 20 | "minitweaks.rule.dispensersDuplicateAllays.desc": "Dispensers duplicate dancing allays with amethyst shards", 21 | 22 | "minitweaks.rule.dispensersDyeMobs.desc": "Dispensers can dye sheep (and shulkers if dyeableShulkers is enabled)", 23 | 24 | "minitweaks.rule.dispensersNameMobs.desc": "Dispensers use name tags on mobs", 25 | 26 | "minitweaks.rule.dispensersRepairGolems.desc": "Dispensers can repair Iron Golems with iron ingots", 27 | 28 | "minitweaks.rule.dragonBlockDamage.desc": "Set dragon block damage breaking type, regardless of mobGriefing gamerule", 29 | "minitweaks.rule.dragonBlockDamage.extra.0": "default: default block breaking", 30 | "minitweaks.rule.dragonBlockDamage.extra.1": "none: no blocks are broken", 31 | "minitweaks.rule.dragonBlockDamage.extra.2": "break: broken blocks are dropped", 32 | "minitweaks.rule.dragonBlockDamage.extra.3": "destroy: broken blocks are destroyed and not dropped", 33 | 34 | "minitweaks.rule.dyeableShearedSheep.desc": "Sheared sheep can be dyed", 35 | 36 | "minitweaks.rule.dyeableShulkers.desc": "Shulkers can be dyed", 37 | "minitweaks.rule.dyeableShulkers.extra.0": "Color can be reset to default using a water bottle", 38 | 39 | "minitweaks.rule.echoShardsEnableShriekers.desc": "Using an echo shard on a sculk shrieker allows it to summon wardens", 40 | 41 | "minitweaks.rule.fasterOxidation.desc": "Blocks oxidize much faster when touching water", 42 | 43 | "minitweaks.rule.infinityMendingStacking.desc": "Allows infinity and mending to stack on bows, like in 1.9 to 1.11", 44 | 45 | "minitweaks.rule.lightningGlowifiesSquids.desc": "Squids struck by lightning convert to glow squids", 46 | 47 | "minitweaks.rule.maxPlayerXpDrop.desc": "Maximum amount of xp players drop on death", 48 | 49 | "minitweaks.rule.mobItemPickup.desc": "Overwrites random default pickup chance when mob spawns", 50 | "minitweaks.rule.mobItemPickup.extra.0": "Only zombie and skeleton type mobs are affected", 51 | "minitweaks.rule.mobItemPickup.extra.1": "default: uses default pickup", 52 | "minitweaks.rule.mobItemPickup.extra.2": "always: mobs pick up items", 53 | "minitweaks.rule.mobItemPickup.extra.3": "never: mobs don't pick up items", 54 | 55 | "minitweaks.rule.mobsDropNametag.desc": "Named mobs drop their name tag on death", 56 | "minitweaks.rule.mobsDropNametag.extra.0": "Note: mobs will drop a name tag even if they weren't named with one", 57 | "minitweaks.rule.mobsDropNametag.extra.1": "This may also cause name tags to be able to be duped", 58 | 59 | "minitweaks.rule.moveableWaterloggedBlocks.desc": "Waterlogged blocks stay waterlogged when moved with a piston", 60 | 61 | "minitweaks.rule.noCreeperBlockBreaking.desc": "Disables creeper explosion block breaking", 62 | 63 | "minitweaks.rule.noFeatherFallingTrample.desc": "Prevents farmland from being trampled if you have feather falling", 64 | 65 | "minitweaks.rule.noGhastBlockBreaking.desc": "Disables ghast fireball explosion block breaking", 66 | 67 | "minitweaks.rule.noRepairCost.desc": "Removes additional cost for using an item in an anvil multiple times", 68 | 69 | "minitweaks.rule.noSnowGolemMelting.desc": "Prevents Snow Golems from melting in hot biomes", 70 | 71 | "minitweaks.rule.noVillagerWitchConversion.desc": "Villagers don't convert to witches when struck by lightning", 72 | 73 | "minitweaks.rule.phantomSpawningTime.desc": "Amount of ticks before Phantoms start having a chance to spawn", 74 | 75 | "minitweaks.rule.protectionStacking.desc": "Allows all the different protection types to stack on the same piece of armor, like in 1.14 to 1.14.2", 76 | 77 | "minitweaks.rule.quickHarvesting.desc": "Right click crops with a hoe to harvest and replant", 78 | 79 | "minitweaks.rule.removableCurses.desc": "Curses are also removed when using grindstones or repair crafting", 80 | 81 | "minitweaks.rule.renewableDragonEgg.desc": "Dragon eggs will always be placed on the portal after defeating the dragon", 82 | 83 | "minitweaks.rule.renewableRawOres.desc": "An anvil landing on Iron/Gold/Copper blocks turns them into the raw ore block version", 84 | 85 | "minitweaks.rule.shaveSnowLayers.desc": "Snow layers can be shaved, removing one layer, when right clicked with a shovel", 86 | 87 | "minitweaks.rule.slimeLooting.desc": "Bigger slimes spawn more smaller slimes when killed with looting", 88 | "minitweaks.rule.slimeLooting.extra.0": "Additional slimes can be up to as many levels of looting as you have (up to +3 with looting 3, etc)", 89 | 90 | "minitweaks.rule.vexesNerf.desc": "Vexes will start to die after the evoker that summoned them dies", 91 | 92 | "minitweaks.rule.villagersAlwaysConvert.desc": "Villagers will always convert to Zombie Villagers when killed by a zombie, even on easy and normal difficulty", 93 | 94 | "minitweaks.rule.villagersExplodeBeds.desc": "Villagers cause explosions when trying to use beds in the nether or end, like players", 95 | "minitweaks.rule.villagersExplodeBeds.extra.0": "\"But why?\" Idk, it's just a funny idea I had" 96 | } 97 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "minitweaks", 4 | "version": "${version}", 5 | "name": "MiniTweaks", 6 | "description": "Some small game mechanic changes, mainly for survival", 7 | "authors": [ 8 | "manyrandomthings" 9 | ], 10 | "contact": { 11 | "homepage": "https://modrinth.com/mod/minitweaks", 12 | "issues": "https://github.com/manyrandomthings/minitweaks/issues", 13 | "sources": "https://github.com/manyrandomthings/minitweaks" 14 | }, 15 | "license": "LGPL-3.0-or-later", 16 | "icon": "assets/minitweaks/icon.png", 17 | "environment": "*", 18 | "entrypoints": { 19 | "main": [ 20 | "minitweaks.MiniTweaks" 21 | ] 22 | }, 23 | "mixins": [ 24 | "minitweaks.mixins.json" 25 | ], 26 | "depends": { 27 | "java": ">=21", 28 | "minecraft": "~1.21.5", 29 | "fabricloader": ">=0.16.10", 30 | "carpet": ">=1.4.169" 31 | }, 32 | "custom": { 33 | "modmenu": { 34 | "parent": "carpet" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/minitweaks.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "minitweaks.mixins", 4 | "compatibilityLevel": "JAVA_21", 5 | "mixins": [ 6 | "block.all.AbstractBlockMixin", 7 | "block.anvil.crushing.AnvilBlockMixin", 8 | "block.dispenser.behavior.DispenserBlockMixin", 9 | "block.farmland.trample.FarmlandBlockMixin", 10 | "block.grindstone.curses.GrindstoneScreenHandlerMixin", 11 | "block.oxidizable.OxidizableBlockMixin", 12 | "block.piston.waterlog.PistonBlockEntityMixin", 13 | "block.shrieker.activate.SculkShriekerBlockMixin", 14 | "block.snow.shave.ShovelItemMixin", 15 | "command.seed.permission.SeedCommandMixin", 16 | "crafting.curses.RepairItemRecipeMixin", 17 | "item.hoe.harvest.HoeItemMixin", 18 | "mob.all.interact.MobEntityMixin", 19 | "mob.all.lightning.EntityMixin", 20 | "mob.all.nametag.LivingEntityMixin", 21 | "mob.allay.duplicate.AllayEntityInvoker", 22 | "mob.blaze.fire.SmallFireballEntityMixin", 23 | "mob.creeper.block_damage.CreeperEntityMixin", 24 | "mob.creeper.head_drops.CreeperEntityMixin", 25 | "mob.dragon.block_damage.EnderDragonEntityMixin", 26 | "mob.dragon.egg.EnderDragonFightMixin", 27 | "mob.ghast.block_damage.FireballEntityMixin", 28 | "mob.phantom.spawning.PhantomSpawnerMixin", 29 | "mob.pickup.SkeletonZombieEntityMixin", 30 | "mob.player.drops.ItemEntityAccessor", 31 | "mob.player.drops.PlayerInventoryMixin", 32 | "mob.player.xp.PlayerEntityMixin", 33 | "mob.sheep.dye.DyeItemMixin", 34 | "mob.shulker.dye.DyeItemMixin", 35 | "mob.shulker.dye.ShulkerEntityInvoker", 36 | "mob.shulker.dye.ShulkerEntityMixin", 37 | "mob.slime.looting.SlimeEntityMixin", 38 | "mob.snowgolem.melt.SnowGolemEntityMixin", 39 | "mob.squid.lightning.SquidEntityMixin", 40 | "mob.vex.nerf.VexEntityMixin", 41 | "mob.villager.bed_explode.VillagerEntityMixin", 42 | "mob.villager.lightning.VillagerEntityMixin", 43 | "mob.zombie.convert.ZombieEntityMixin", 44 | "mob.zombie.convert.ZombieVillagerEntityInvoker" 45 | ], 46 | "injectors": { 47 | "defaultRequire": 1 48 | } 49 | } 50 | --------------------------------------------------------------------------------