179 Commits
0.1 ... 1.19.4

Author SHA1 Message Date
Christian Knaapen
2bec37ce0a Merge branch 'port/1.19.4' into 1.19.4 2023-12-23 21:21:02 +01:00
Mrbysco
5fce26a796 Initial backport to 1.19.4 2023-12-23 21:09:58 +01:00
Mrbysco
28d47a0cbb Fix textures not being recognized
Mojang no longer automatically loads textures in `textures/items` as they now load textures from `textures/item`
2023-11-21 02:48:06 +01:00
Mrbysco
00cb48b929 Fix loot modifiers
altenative was renamed to any_of
2023-11-21 02:46:50 +01:00
Christian Knaapen
0d6613c3ba Merge branch 'port/1.20' into 1.20-alternative 2023-11-18 20:57:39 +01:00
Christian Knaapen
1607aa35ce Fixed not being able to target tile entities when you want to break them (protection off). 2023-11-18 20:53:28 +01:00
Mrbysco
b75cfc45d8 Fix missing calls 2023-11-18 20:49:10 +01:00
Christian Knaapen
4537dd409d Updated version to 3.6 2023-11-18 12:23:14 +01:00
Christian Knaapen
268d6e7a6b Merge branch 'port/1.20' into 1.20-alternative 2023-11-18 11:59:09 +01:00
Mrbysco
ca44da71e7 Fresh port to 1.20
Note: I changed `BuildModeCategoryEnum` to actually use the Color class instead of using Vector4f. Same counts for `RadialMenu`
2023-11-18 00:38:56 +01:00
Christian Knaapen
8570bde282 Fixed not being able to load when the newest version of Create is installed. 2023-10-22 13:38:47 +02:00
Christian Knaapen
5fb2edb97a Trying to fix flywheel dependency some more. 2023-07-18 22:46:09 +02:00
Christian Knaapen
0a9b764d3f Fixed build.gradle referencing older flywheel.
Updated logo, pack version
2023-07-18 21:16:49 +02:00
Christian Knaapen
82ee3eefaa Fixed not being able to load mod with latest Create mod version: Updated Flyweel dependency to 0.6.9. Updated russian translation. 2023-07-07 21:27:35 +02:00
Christian Knaapen
f033c261d7 Fixed not being able to place blocks if placement reach is 0, falls back to vanilla reach now. 2023-06-28 16:28:01 +02:00
Christian Knaapen
0dad523e13 Updated icons 2023-06-25 20:00:14 +02:00
Christian Knaapen
9c9e4d04b7 Added power level indicator with extensive tooltip to radial menu. 2023-06-24 22:40:12 +02:00
Christian Knaapen
8288e5db97 Added powerlevel command. Updated reach upgrade behaviour. 2023-06-24 21:13:02 +02:00
Christian Knaapen
9e680fc2dc Added power level items and their loot modifiers. 2023-06-24 16:45:36 +02:00
Christian Knaapen
33033666b9 Renamed reach to power level. Separated reach config for more control.
Added draft icons for elastic hand, muscles and building technique book.
Separated BlockUtilities clientside.
2023-06-11 20:40:39 +02:00
Christian Knaapen
594b5e1c0d Decreasing itemstacks in survival. Disabled replacing blocks in survival. Expanded config options for survival.
Added keybinds to switch to previous build mode, and to switch between disabled and previous.
Updated RandomizerBags to use new placement method in vanilla mode. Removed SurvivalHelper placement methods.
2023-06-11 17:23:01 +02:00
Christian Knaapen
410d154276 Enabled for survival. Calculating missing items. Showing itemstacks at cursor. Showing preview blocks in red.
Increased max blocks placed at once.
Filtering coordinates that are not loaded or outside of world border.
Sending items instead of itemstacks to server.
2023-05-18 18:46:23 +02:00
Christian Knaapen
6046b9f5fa Updated russian translation. 2023-04-21 18:12:03 +02:00
Christian Knaapen
a7dd0823e1 Fixed redo and running on server. 2023-02-08 20:34:05 +01:00
Christian Knaapen
df1efef63b WIP UndoRedo overhaul continued.
More validation in ServerBlockPlacer.
Added BlockPlacerHelper.
2023-02-05 13:22:41 +01:00
Christian Knaapen
23b8c0c9fc WIP UndoRedo overhaul.
Fixed concurrent modification issue in ServerBlockPlacer.
2023-02-04 00:30:30 +01:00
Christian Knaapen
bda0908731 Added server validation: allowInSurvival and whitelist.
Instantly placing blocksets of size 1.
Added server config and moved some config values around.
2023-02-03 21:14:29 +01:00
Christian Knaapen
589059fd44 Better text drawing in radial menu using tooltips.
Hold shift for description.
Fixed keybind text.
2023-02-03 20:00:08 +01:00
Christian Knaapen
07667efe98 Icons for modifier settings and replace modes. Rendering mirror lines and areas in world.
Removed shaders.
2023-02-02 00:45:21 +01:00
Christian Knaapen
d4cb0c6858 Radial mirror GUI, and saving modifiers.
Saving modifiers in player persistent data on serverside, and loading when new player joins. This is separate per world.
Modifier GUI panels are a bit larger now, and position fields are wider.
2023-02-01 20:54:48 +01:00
Christian
953a3fe923 Fixed several issues. Mirroring vertically.
Modifier panel up/down buttons updating.
Updating neighbouring blocks when placing blocks.
Removed separate icon files.
2023-01-30 01:37:59 +01:00
Christian
420b06d414 Modifier settings GUI for array and mirror, with header.
New parts in modifiers gui texture. 
Added reordering modifiers.
Replaced all icon rendering with AllIcons class.
Delayed blockset placing in server.
Modifiers now work when buildmode is DISABLED.
Modifiers now work with breaking when buildmode would break a single block.
Modifiers screen renders tooltip on top of everything.
New labeledScrollInput class, extension of Create's ScrollInput.
BlockPreviews ignores config settings to hide itself when placing more than 1 block.
Fixed mirroring wrong horizontal axis.
2023-01-29 23:57:47 +01:00
Christian
c46ae91e99 Created icon sheet. 2023-01-28 20:29:15 +01:00
Christian
2a84c7ca41 WIP Continued modifier settings gui overhaul 2023-01-28 18:19:57 +01:00
Christian
98ebc61bb8 WIP Modifier settings gui overhaul 2023-01-28 02:08:19 +01:00
Christian Knaapen
ed2609f97e Overhauled modifiers to run clientside only. BuilderChain applies rotation and mirror to blockstates.
Removed ModifierSettingsManager, ModifierSettingsMessage and capabilities.
2023-01-26 02:09:50 +01:00
Christian Knaapen
a760565b66 Added gui classes and some textures from Create. 2023-01-24 23:46:45 +01:00
Christian Knaapen
4ea34fc854 Added buttons in radial menu for replace modes. Implemented/fixed replace modes.
Made ModeOptions clientside and UndoRedo serverside.
New config option: only show previews when building, default true.
Overhauled drawOutlinesIfNoBlockInHand, now drawOutlineAtBreakPosition.
Fixed not being able to break blocks.
2023-01-24 02:06:43 +01:00
Christian Knaapen
af4ef73375 Using BlockSets. Added filtering and several replace settings.
Overhauled build.gradle based on Create. Added flywheel as jarInJar, no external dependency anymore. New mappings channel from parchment.
Added replace modes: air, air and solid, solid, offhand filtered. Offhand filtering looks in randomizer bag as well.
Protecting tile entities by default.
Other mods can cancel placement through BlockEvent.OnBlockPlaced, such as FTBChunks.
BlockPreviews.drawOutlinesIfNoBlockInHand overhaul.
2023-01-22 20:36:48 +01:00
Christian Knaapen
ae75e48c01 Added sounds and renewing randomizer bag at right times.
Added text with instruction under actionbar while placing/breaking.
2023-01-22 03:29:37 +01:00
Christian Knaapen
ef687c0658 Overhaul continues continuing.
Saving data and serverside state through persistent player data.
Overhauled block previews.
Removed mode capability.
Added create item and network classes.
Using create BlockHelper to place and break blocks.
2023-01-22 00:11:28 +01:00
Christian Knaapen
617fc82114 Overhaul continues.
Updated build modes, determining blockstates.
Everything compiles and runs.
2023-01-21 01:41:00 +01:00
Christian Knaapen
349c673553 WIP Overhaul
Block calculation only on client, then send to server to place.
BuilderChain system to decouple buildmodes, buildmodifiers and randomizer bag.
2023-01-20 22:26:18 +01:00
Christian Knaapen
91834f9b60 Renamed helper package to utilities. Added systems. Removed direction parameter when placing blocks. 2023-01-18 22:02:19 +01:00
Christian Knaapen
cce5e2e28d Several tweaks 2023-01-18 19:28:42 +01:00
Christian Knaapen
fe69a81622 Merge branch 'create-utilities' into 1.19 2023-01-17 22:42:12 +01:00
Christian Knaapen
5f27eedde2 Keeping outlines until animation is done. Fix itemstack not found error when undoing in creative.
Disabled create test code.
2023-01-17 22:41:38 +01:00
Christian Knaapen
cd3afbbb82 Overhauled block previews to work with ticks. Added place and break animation.
Added DelayedBlockPlacer to play appear animation, then place.
Split BuildConfig into CommonConfig and ClientConfig.
Fixed itemstack not found error in creative.
Added scale and color options to ghost blocks.
2023-01-17 02:30:08 +01:00
Christian Knaapen
fa1aadcd86 Using Create outlines and ghost blocks for block previews.
Fixed item names.
2023-01-16 01:53:09 +01:00
Christian Knaapen
c2d413fad0 Added missing resources.
Modified mods.toml.
Fixed crash and double rendering of modifiers.
2023-01-15 23:16:22 +01:00
Christian Knaapen
064973b07b Added code from Create. Added Flywheel dependency.
Restructured client and mod events to match create.
Copied accesstransformer. Added Create, CreateClient, ClientEvents and CommonEvents proxies.
2023-01-15 22:21:50 +01:00
Christian Knaapen
ea59fef099 Changed reach upgrade recipes. Made radial menu smaller.
Cleaned up radial menu code.
Added russian language.
Tweaked cube icon.
2023-01-14 15:35:43 +01:00
Christian Knaapen
5e6c42d372 Fixed disassembling blocks with the Create Wrench duplicating the item when a build mode other than Normal is used. Disabled break outlines in survival, because breaking multiple blocks in survival is deprecated. 2022-10-25 21:52:14 +02:00
Christian Knaapen
14460c2c01 Fixed keybindings not being registered. 2022-10-24 21:01:19 +02:00
Christian Knaapen
3b31a74780 Merge branch 'master' into 1.19 2022-07-30 10:48:08 +02:00
manapart
911d4b5605 Merged in 1.19 (pull request #4)
1.19

Approved-by: Requios
2022-07-30 08:46:57 +00:00
Christian Knaapen
13ee819439 Merge branch '1.18' 2022-07-30 10:02:13 +02:00
ManApart
319fedc2dd seemingly working with forge .99 2022-07-16 16:14:40 -04:00
ManApart
c7e6230424 seems to be working fine 2022-07-10 08:19:33 -04:00
ManApart
e7ef54bd80 starting game 2022-07-10 08:10:57 -04:00
ManApart
d5af0b097e compiling 2022-07-10 08:05:19 -04:00
ManApart
63b95df0ff upgrade translatable component 2022-07-10 07:59:17 -04:00
ManApart
b3730896e5 upgrade text component 2022-07-10 07:55:10 -04:00
ManApart
675a4930af initial gradle changes 2022-07-10 07:45:49 -04:00
Christian Knaapen
6e22577d61 Updated to work with 1.18.2. 2022-03-19 22:26:59 +01:00
Christian Knaapen
6e7aab673f Fixed crash when previewing lanterns using mirror or radial mirror (blockstate nullpointer). 2022-03-19 21:49:37 +01:00
Christian Knaapen
ad2e81c54e Added build mode categories with colored lines. Hiding pyramid/cone/dome buttons for now. Changed icon of normal mode and renamed to disable. 2022-02-13 16:23:22 +01:00
Christian Knaapen
780d5cc972 Removed reach command and functionality to break multiple blocks in survival. 2022-02-13 01:11:02 +01:00
Christian Knaapen
a1d30fd752 Added descriptions to radial menu. Added pyramid, cone and dome buttons, icons and placeholder classes. Fixed leftclicking to cancel in survival. 2022-02-12 20:55:29 +01:00
Christian Knaapen
96ffc5c1a5 Fixed multiplayer: cache in Mode/ModifierSettingsManager does not work on server, so it's removed. Also changed some packets to use separate classes for clientside handling, as recommended by the Forge Community Wiki. 2022-01-16 15:55:44 +01:00
Christian Knaapen
5ca826362b Update to 1.18 2022-01-03 13:47:18 +01:00
Christian Knaapen
9ae02b16de Tweaks and cleanup 2021-12-05 17:53:47 +01:00
Christian Knaapen
80e355a806 Revamped the radial menu. Removed some old code. 2021-12-05 14:30:57 +01:00
Christian Knaapen
700a3062f7 Hotfix: Fixed crash when rendering overlay: Invalid shaders/core/dissolve.json: Couldn't compile fragment program. 2021-11-02 01:40:17 +01:00
Christian Knaapen
f45d3ffad7 Added golden and diamond randomizer bags (with container, screen, icon and recipe). Updated recipes for reach upgrades. 2021-10-31 16:52:08 +01:00
Christian Knaapen
c8cfa515d0 Fixed icons in radial menu. Cleaned up radial menu code a bit. 2021-10-30 21:34:53 +02:00
Christian Knaapen
6760388bc7 Fixed block preview shader dissolve effect. Fixed reach upgrade item icons. 2021-10-30 20:00:47 +02:00
Christian Knaapen
80c475ff54 Fixed block preview shader being glitchy. Now shows block without effects. 2021-10-30 16:32:23 +02:00
Christian Knaapen
5b8fafd9f7 Fixed randomizer bag inventory not opening. 2021-10-16 13:24:33 +02:00
Christian Knaapen
43d0a02faf Moved dissolve shader to Minecraft's new custom shader rendertype. 2021-09-26 19:38:02 +02:00
Christian Knaapen
4b1997e44f [TASK] Fixed rendering modifier lines and areas. Rendering block previews no longer crashes (renders black). 2021-09-26 12:48:36 +02:00
Christian Knaapen
3930844208 Fixed capabilities and setup events. 2021-09-12 16:58:09 +02:00
Christian Knaapen
d9df8b0d0e Fixed remaining compiler errors. Using deferred registries now, and cleaned up mods.toml file. 2021-09-12 13:54:25 +02:00
Christian Knaapen
178a4ca4e1 Fixed 200+ compiler errors. 23 left. 2021-09-01 17:37:09 +02:00
Christian Knaapen
2297c38574 WIP Update to 1.17 2021-09-01 14:51:44 +02:00
Christian Knaapen
72d1085a54 Updated mapping to 1.16.5 official names. 2021-09-01 14:13:08 +02:00
Christian Knaapen
47f38d0a58 Bumped version number 2021-05-20 21:11:38 +02:00
Alexei Robyn
54d9c3a92f Merged in fakeplayer-fix-1.16 (pull request #3)
Fix FakePlayer-related crashes

Approved-by: Requios
2021-05-20 18:16:16 +00:00
Alexei Robyn
f77a937c6c Fix FakePlayer-related crashes 2021-05-20 14:14:58 +10:00
Christian Knaapen
b41f102f0c Using deferred registry for Randomizer Bag container, which fixes startup crash in 1.16.4 and 1.16.5. 2021-02-03 20:50:09 +01:00
grimm auld
f2fee42c58 Merged in 1.16 (pull request #2)
1.16

Approved-by: Requios
2021-02-02 20:28:16 +00:00
grimmauld
d63786f6e2 merge version bump 2021-02-02 12:52:18 +01:00
Christian Knaapen
a6bb10f06d Version update 2021-01-31 13:38:23 +01:00
grimmauld
5fe5978777 Fix some stuff 2021-01-29 23:43:12 +01:00
grimmauld
7b5f055d22 Get stuff running on 1.16.3 2021-01-29 23:12:11 +01:00
Christian Knaapen
bc6bebe387 Fixed radial menu icons not rendering. Partially fixed shader. 2020-09-03 20:37:50 +02:00
Christian Knaapen
a7961c22a6 Started on GUI for player settings. 2020-08-27 02:24:04 +02:00
Christian Knaapen
96e9e9bb3b Retina display fix 2020-08-26 21:11:51 +02:00
Christian Knaapen
4b22cea9ab Fixes from 1.14 (Fixed freezing when looking up + setting NBT data). 2020-08-25 15:43:15 +02:00
Christian Knaapen
d440325b2a Shader works partially 2020-08-25 15:36:43 +02:00
Christian Knaapen
4bab684369 Working on block previews, updated mappings. 2020-08-24 22:35:13 +02:00
Christian Knaapen
804ed4354b Modifier rendering complete (lines and planes) 2020-08-24 18:18:30 +02:00
Christian Knaapen
31f3cf3233 More work on rendering, mostly test code and a custom rendertype. 2020-08-23 19:27:36 +02:00
Christian Knaapen
e22ad0c311 Initial 1.15 commit 2020-03-15 15:05:57 +01:00
Christian Knaapen
74d74390d2 Fixed undo/redo crashing. 2020-03-15 13:24:13 +01:00
Christian Knaapen
f764fac778 Fixed randomizer bag not opening. Fixed server crash. 2020-03-08 15:10:16 +01:00
Christian Knaapen
42661f884c Removed reach condition for crafting recipes, too much effort to maintain for such a small feature.
Fixed saving modifier settings on closing the gui (don't override onClose() but removed()).
2019-12-23 15:55:32 +01:00
Christian Knaapen
df27697ecb Fixed checkbox graphics (copied GuiCheckBox from previous version). Fixed missing icons in radial menu (moved texture stitch event to ModEventHandler). 2019-12-23 15:13:43 +01:00
Christian Knaapen
fb622bcf99 Removed logging in network messages. 2019-12-23 13:10:25 +01:00
Christian Knaapen
4e3b1ef2ce Fixed crash when placing ladders. Tweaked circle, cylinder and sphere to have the same outer radius whether they are full or hollow. 2019-12-23 13:08:02 +01:00
Christian Knaapen
83b23fe763 Fixed screens, except containerscreen. Fixed clientproxy and BlockRayTraceResult (cast if type = block). 2019-12-06 15:23:05 +01:00
Christian Knaapen
ac429ca64e Initial 1.14 commit. Migrated using xml mapping (in User\.IntelliJIdea2019.3\config\migration). Went through all classes to fix minor issues.
Main tasks left to do: screens, blockBreak animation, ClientProxy/BlockRayTraceResult.
2019-12-04 20:43:40 +01:00
Christian Knaapen
1e51589bde Implemented sphere. Added icons for circle, cylinder, sphere, circle start corner and start center. 2019-12-04 17:36:26 +01:00
Christian Knaapen
6c34b09223 1.13 branch commit, taken from 1.13 repository commit 491113040c350791a403f18752d98c0faa966e6b. For rest of history see repository at https://bitbucket.org/Requios/effortless-building-1.13/src/master/ 2019-12-04 15:40:52 +01:00
Christian Knaapen
3718492495 Branching, version update. 2019-12-04 15:16:21 +01:00
Christian Knaapen
3e5eb4cb30 Fix randomizer bag consuming an extra block in some cases. 2019-11-17 19:48:33 +01:00
Christian Knaapen
2c6b58ddc6 Fixed issue #45: Can't break blocks in survival mode if breakFar=false. 2019-07-09 02:23:35 +02:00
Christian Knaapen
3921a0b70b Fixed issue #43: Duplicating blocks in survival (undo/redo not working in normal mode).
Fixed QuickReplace in normal mode not placing any blocks.
Fixed blockstates sometimes changing on undo/redo.
Now clearing UndoRedo stacks on clientside when changing dimension or logging out.
Breaking mirror/array in survival is now possible again.
Breaking blocks in survival in any buildmode is now possible (previously only in normal mode unless instabreaking was enabled). Still with limited (vanilla) reach.
2019-07-09 02:11:32 +02:00
Christian Knaapen
11cd20c3d3 Fixed QuickReplace in normal mode not placing any blocks. 2019-07-08 22:54:33 +02:00
Christian Knaapen
4ce8b1e927 Fixed issue #23 dank/null compat only uses one block at a time regardless of how many are placed. 2019-07-06 13:17:08 +02:00
Christian Knaapen
0485e6c888 README.md created online with Bitbucket 2019-07-03 10:26:25 +00:00
Christian Knaapen
89057356df Changed MaxBlocksPerAxis and MaxBlocksPlaced formulas. Now allows placing of 60x60 floors and walls with reach upgrade 3 in survival. 2019-06-05 20:02:37 +02:00
Christian Knaapen
a7ac229242 Fixed issue #37 Visual Glitch when destroying blocks 2019-05-17 16:26:19 +02:00
Christian Knaapen
b2ee5dbc04 Fast breaking in Normal+ mode in addition to fast building.
Fixed issue #36: Having issues with some features with a Sponge Forge Server.
Messed with shader to try and fix some issues.
2019-05-12 11:52:11 +02:00
Christian Knaapen
91ddd11b38 NormalPlus option: faster building while holding RMB.
Wall and floor option: filled or hollow.
Slope floor option: raise along long edge or short edge.
Cube option: filled, hollow or skeleton.
Added icons for normalSpeed, fastSpeed, full, hollow, cubeFull, cubeHollow, cubeSkeleton, shortEdge, longEdge, thickness1, thickness3, thickness5.
2019-04-30 00:10:29 +02:00
Christian Knaapen
e3546f9c42 Added quickReplaceMiningLevel to survivalBalancers in config (issue #14).
Fixed crash when trying to preview some modded blocks that have special rendering code. Preview will show outline instead. (issue #9 and #35)
Added build mode options GUI.
2019-04-25 20:34:46 +02:00
Christian Knaapen
00eb42b88a Added Diagonal Line, Diagonal Wall, Slope Floor and Cube buildmodes.
Fixed issue #26: Crash attempting to place wall with a stone block.

Breaking (in creative) can now be undone as well.
Undo/redo now tries to replace the previous blocks, if you have those blocks in your inventory (issue #33).
Fixed undo in survival only undoing blocks that can be broken by hand (issue #29).

Fixed being able to place randomizer bag inside itself (and prevented a black hole from creating) (issue #31).
Randomizer bags can now be placed inside randomizer bags.
Fixed crash when placing with empty randomizer bag (issue #32).

Fixed diagonal wall and slope being 2 blocks thick.
2019-04-10 19:07:41 +02:00
Christian Knaapen
a4e575e733 Added
- slight transparancy to block preview.
Changed
 - Walls dont try to place at extreme angles anymore.
 - Lines will now prefer the axis closest to player if there are multiple good options.
 - undo hotkey is now ctrl-z and redo to ctrl-y.
Fixed
 - undo not going past 2.
 - not being able to cancel placement with leftclicking when out of reach.
 - blockstate getting stuck in preview before first rightclick.
2019-03-20 23:47:42 +01:00
Christian Knaapen
25ae75bfb6 Refactored build modes for easier expansion.
Allowed buildmodes to intersect with existing blocks 1 block deep.
2019-03-19 16:29:48 +01:00
Christian Knaapen
2cac2be29f Added undo, redo functionality.
All placements use blocks from entire inventory now.
Added block count and dimensions in actionbar when placing/breaking blocks.
Added undo, redo, replace and 'open modifier settings' icons.
Fixed not being able to break blocks in creative when starting the selection on a tallgrass (instabreaking) block.
2019-03-15 20:46:47 +01:00
Christian Knaapen
2d45b1e574 Fixed issue #21: "Placement Exceeds Your Reach" message displays when a block is placed in survival with 0 range upgrades.
Fixed issue #22: Sometimes two blocks are placed at once when in regular build mode.
Fixed mouse clicks in Modifier Settings sometimes being in the wrong place.
Fixed typing in multiple text fields at once.
Added diagonal line, wall, slope floor and cube icons.
Added undo, redo, replace and 'open modifier settings' buttons. (Only last 2 work for now).
2019-02-28 17:46:38 +01:00
Christian Knaapen
4cd2973264 Fixed issue #16 and #17: breaking blocks with Tinkers Hammer and Veinminer.
Fixed issue #19: placing lilypads results in a crash.
2019-02-22 21:09:28 +01:00
Christian Knaapen
9a3fef218e Fixed having to click after breaking.
Outlines now adhere to bounding boxes.
Fixed not being able to place when holding sneak.
Repeated placing when holding is now possible in NormalPlus mode.
Repeated breaking when holding now only happens in NormalPlus mode.
Repeated placing and breaking is now possible when not moving the mouse.
Fixed not being able to break when clicking in air.
Sounds no longer depend on distance to player, all are in category BLOCKS, and breaking sound is played when appropriate when previewing.
2019-02-22 16:35:19 +01:00
Christian Knaapen
5328ae342d Fixed crash when right-clicking in mid-air in build mode Normal+. 2019-02-19 00:42:45 +01:00
Christian Knaapen
2986b11983 Disabled shader reload key.
Disabled build modes when reach is 0.
Removed incomplete build mode icons.
Removed sound event (unused).
2019-02-18 20:29:52 +01:00
Christian Knaapen
be865d273c Breaking shows preview (breaking only in creative).
Build mode cancels when opposite mouse button is pressed (left button when placing, right button when breaking).
Simple shader when placing more than 1500 blocks (or when useShaders is false).
Added axis limits in addition to total block limit.
Fixed placing on self (and being pushed).
Fixed issue #15 silk touch, shearing leaves, dropping bed etc by using Block#harvestBlock (items dont drop directly to inventory anymore).
Fixed randomizerbag not randomizing in buildmodes.
2019-02-14 02:02:22 +01:00
Christian Knaapen
e1c23a5bec Fixed dissolve shader sometimes applying to wrong blocks (server and client are synced now, no more message to render).
Fixed build modes on server clicking twice.
Changing config ingame is now possible.
2019-02-12 16:06:54 +01:00
Christian Knaapen
a5e5e7240a Added Chisels and Bits compatibility (no double radial menu).
Added preliminary Architecturecraft compatibility (places right type, not rotation yet).
2019-02-11 17:23:57 +01:00
Christian Knaapen
1aa1401450 Moved radial menu to RenderHandler.
Resolved issue #12 and #13.
(And #10 with pull request.)
2019-02-10 19:27:53 +01:00
FenixFyreX
5944ea4aa1 Merged in FenixFyreX/effortless-building/block-proxy-compat (pull request #1)
Added compatibility for a few block proxies, like /dank/null.
2019-02-10 14:17:08 +00:00
FenixFyreX
0eda9baf8f Fixed sneaking being removed for randomizer bag in ClientProxy. 2019-02-08 11:13:48 -06:00
FenixFyreX
f36c5a4a5a Removed void tear, as it doesn't place items like I thought. 2019-02-08 10:56:02 -06:00
FenixFyreX
8f333249a3 Added compatibility for a few block proxies, like /dank/null. 2019-02-08 10:48:07 -06:00
FenixFyreX
c602f39059 Merged Requios/effortless-building into master 2019-02-08 01:32:43 -06:00
Christian Knaapen
009f385054 Added shader on placement. Added sound on drag (block placement sound).
Added line.
Highlight currently selected in radial menu (with colors).
Fixed scaling of icons.
Refactored RenderHandler into 3 classes and SurvivalHelper.
Added classes and icons for diagonal line, diagonal wall, slope floor and cube.
2019-02-08 03:26:41 +01:00
Christian Knaapen
73c55578e7 More shader stuff. Shader has world position through vertex shader and blockpos.
Dissolve from firstpos to secondpos with some randomness.
Blue pulse with diagonal highlights.
2019-02-07 00:09:12 +01:00
Christian Knaapen
e67849dd0b Messing with shader. 2019-02-06 19:56:37 +01:00
Christian Knaapen
7321d1af01 Updated forge.
Tweaked shader.
Cancel build mode with esc, e, leftclick.
Added breaking blocks with buildmodes.
2019-02-06 03:53:25 +01:00
Christian Knaapen
dfc2b11e94 Shader with dissolve and blue pulse on block previews.
Floor mode.
Block preview sorting based on player distance.
2019-02-06 00:09:11 +01:00
Christian Knaapen
ba82449632 Implemented BuildModes and Wall.
Renamed buildSettings to modifierSettings etc.
Added mode capability, message, settings.
Added icons for normal, normal+, line, wall and floor.
2019-02-05 02:30:23 +01:00
Christian Knaapen
ae4650ba35 Added radial menu with build modes.
Moved extended gui elements to own package.
2019-01-28 21:02:07 +01:00
Christian Knaapen
da0295538e Moved buildmodifier classes to buildmodifier package. 2019-01-28 17:23:13 +01:00
Christian Knaapen
d07751a01f Fixed issue #8 Craft when activating the menu.
Fixed being able to mine bedrock. Whoops.
Fixed grass and flowers instabreaking other blocks.
2019-01-11 03:01:04 +01:00
Christian Knaapen
f3ad36e6a6 Fixed rotation and various small bugs. 2019-01-10 17:21:56 +01:00
Christian Knaapen
aeb4df6156 Added rotation to blocks placed with radial mirror.
Added alternative mode where every other slice of the radial mirror is mirrored.
Fixed issue #6 Crash when creating new world after first launch.
2019-01-08 18:53:12 +01:00
Christian Knaapen
8230f93b0c Added radial mirror with position, slices, and radius. With GUI, capability and network.
Optimized imports.
2019-01-04 17:06:58 +01:00
Christian Knaapen
3d3a90e76f Message when using reach upgrades in creative.
Sound when placing blocks far away.
2019-01-02 17:05:51 +01:00
Christian Knaapen
36a698cd95 Increased range for placing and breaking blocks (breaking at range only in creative).
Added reach upgrade items.
Added reach command (usage: /reach <level>).
2018-12-23 22:15:05 +01:00
Christian Knaapen
13d43a0265 Added breaking animation to all breakable blocks.
Smart preview: only blocks that can be (re)placed show a preview.
Smart outline when breaking: only blocks that can be broken by the current tool get an outline.
Improved occlusion by render further block previews first.
Reversed order of mirror and array to mirror arrays (for roofs etc).
2018-12-15 16:59:13 +01:00
Christian Knaapen
408d96622b Implemented new block preview for mirror and array, quickreplace and randomizer bag.
Refactored buildmodifiers to use findCoordinates and findBlockStates.
2018-12-14 20:12:18 +01:00
Christian Knaapen
2a487f2864 Testing a new block preview 2018-12-14 00:21:04 +01:00
Christian Knaapen
d745a868e5 Settings entries can be collapsed (GuiList with custom height).
Entries are centered vertically if it fits.
2018-12-04 20:00:04 +01:00
Christian Knaapen
ee4ac6f098 Made settings GUI scrollable for future use.
Split mirror and array settings GUI into their own classes (scrollEntries).
2018-12-02 18:31:25 +01:00
Christian Knaapen
1933832c58 Fixed rare crash on ladders.
Added config file with reach, increased mining time and block outline settings.
Mirror size has been renamed to reach.
Mirror and array abide by the new reach settings.
Added reach counter to array UI (reach = count * max offset).
If reach is 0 (as can be set in config), the UI will be disabled.
Added the ability to show the white block outline on the block you place, in addition to blocks placed by mirror/array.
Added the ability to disable increased mining time and to change its percentage in the config.
2018-12-01 18:33:21 +01:00
Christian Knaapen
3891cc349f Mining speed is decreased based on blocks mined (extra blocks count for 50%, which will be configurable).
Tool durability is checked after each block break (no more breaking 5 blocks with 1 durability).
Fortune is taken into account.
Outlines now change to blocks that will be broken when holding tools.
Added indication QuickReplace is on: outline of current block will be white.
Fixed opening Randomizer Bag in offhand.
Fixed placing blocks with Randomizer Bag in offhand.
Fixed mirror block outline showing up outside range.
Fixed outline of QuickReplace not being under replaceables like tall grass.
2018-11-04 18:43:16 +01:00
Christian Knaapen
2ea44f22b5 GUI overhaul. Added +- buttons with shift/ctrl clicking. Rightclick to clear. Tooltips. Scroll to change number.
See empty fields as 0 instead of reset. Images for buttons. Added Toggle 0.5 button.
Can now click "mirror/array enabled" label to toggle.
2018-10-28 19:38:44 +01:00
Christian Knaapen
e2e48a4451 Fixed shiftclicking items into a Randomizer Bag when it is almost full. 2018-10-27 16:04:42 +02:00
Christian Knaapen
c2e29419ca Moved all rendering code to RenderHelper to resolve #3 Crash on stand alone server startup.
Made sure getBuildSettings never returns null to resolve #4 Client Crash W/ OpenBlocks.
Now compatible with block placers and breakers from OpenBlocks (even the Randomizer Bag works in the placer).
Fixed the breakEvent in mirror for array synergy to use the blockstate of the new position. Resolves #5 Crash after destorying blocks with hand.
2018-10-27 14:20:00 +02:00
Christian Knaapen
bb359113f2 Version update and cleanup 2018-10-21 17:05:05 +02:00
Christian Knaapen
bda53e4b08 Check if can break and replace blocks. Breaking only works if your current held tool can break it. Replace only works with blocks that can be harvested with your hand.
Damages used tool appropriately.
Added block outlines to mirror and array.
2018-10-20 16:15:14 +02:00
Christian Knaapen
821b46a253 Fixed shrinking itemstack with mirror/array when not using quickreplace.
Fixed replacing same blocks dropping extra.
2018-10-18 16:50:39 +02:00
Christian Knaapen
0e7d08438d Fixed randomizer bag not working with quickreplace. 2018-10-14 15:46:06 +02:00
Christian Knaapen
483d6cc53e Checks if placeBlock is allowed. Fixed stutter on line rendering. Added toggle for creative/survival mode. 2018-10-06 20:20:10 +02:00
Christian Knaapen
2b4093099f Survival array and mirror. 2018-09-30 18:05:04 +02:00
Christian Knaapen
6f6360ae11 Build works. Added synergy of randomizer bag with mirror/array. Mirror slabs vertically. 2018-09-30 15:32:21 +02:00
Christian Knaapen
cb006c4983 Fixed serverside issues. Hidden randomizer bag. New issue: build does not work with messages. 2018-09-22 00:09:52 +02:00
Christian Knaapen
bf258580d4 Randomizer bag finished (including recipe, placing blocks). Changed default keybinds to ADD and SUBTRACT. QuickReplace now places under replaceable blocks (tall grass). Mirror now happens before array. 2018-09-17 01:17:51 +02:00
Christian Knaapen
efa47851be Working item with inventory 2018-09-16 15:20:01 +02:00
Christian Knaapen
d01fe52aba Some work on randomizerbag 2018-09-16 14:21:05 +02:00
313 changed files with 24953 additions and 2154 deletions

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
# Disable autocrlf on generated files, they always generate with LF
# Add any extra files or paths here to make git stop saying they
# are changed when only line endings change.
src/generated/**/.cache/cache text eol=lf
src/generated/**/*.json text eol=lf

63
.gitignore vendored
View File

@@ -1,43 +1,26 @@
## Based on GitHub's Eclipse .gitignore # eclipse
bin
classes/
run/
.gradle/
build/
gradle-app.setting
## IntelliJ IDEA
.idea/
*.iml
*.iws
*.ipr
## Eclipse
eclipse/
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch *.launch
.settings
.metadata
.classpath
.project
# CDT-specific # idea
.cproject out
*.ipr
*.iws
*.iml
.idea
# PDT-specific # gradle
.buildpath build
.gradle
# other
eclipse
run
logs/*
# Files from Forge MDK
forge*changelog.txt

520
LICENSE.txt Normal file
View File

@@ -0,0 +1,520 @@
Unless noted below, Minecraft Forge, Forge Mod Loader, and all
parts herein are licensed under the terms of the LGPL 2.1 found
here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and
copied below.
Homepage: http://minecraftforge.net/
https://github.com/MinecraftForge/MinecraftForge
A note on authorship:
All source artifacts are property of their original author, with
the exclusion of the contents of the patches directory and others
copied from it from time to time. Authorship of the contents of
the patches directory is retained by the Minecraft Forge project.
This is because the patches are partially machine generated
artifacts, and are changed heavily due to the way forge works.
Individual attribution within them is impossible.
Consent:
All contributions to Forge must consent to the release of any
patch content to the Forge project.
A note on infectivity:
The LGPL is chosen specifically so that projects may depend on Forge
features without being infected with its license. That is the
purpose of the LGPL. Mods and others using this code via ordinary
Java mechanics for referencing libraries are specifically not bound
by Forge's license for the Mod code.
=== MCP Data ===
This software includes data from the Minecraft Coder Pack (MCP), with kind permission
from them. The license to MCP data is not transitive - distribution of this data by
third parties requires independent licensing from the MCP team. This data is not
redistributable without permission from the MCP team.
=== Sharing ===
I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of
the minecraft modding community. All contributions to these parts should be licensed under the same additional grant.
-- Runtime patcher --
License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher
and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
-- ASM transformers --
License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories)
under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
=========================================================================
This software includes portions from the Apache Maven project at
http://maven.apache.org/ specifically the ComparableVersion.java code. It is
included based on guidelines at
http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
with notices intact. The only change is a non-functional change of package name.
This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating
binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/
authored by genman, heikok, pivot.
The only changes are to replace some Trove collection types with standard Java collections, and repackaged.
=========================================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS

BIN
Logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
Logo.xcf

Binary file not shown.

53
README.txt Normal file
View File

@@ -0,0 +1,53 @@
-------------------------------------------
Source installation information for modders
-------------------------------------------
This code follows the Minecraft Forge installation methodology. It will apply
some small patches to the vanilla MCP source code, giving you and it access
to some of the data and functions you need to build a successful mod.
Note also that the patches are built against "unrenamed" MCP source code (aka
srgnames) - this means that you will not be able to read them directly against
normal code.
Source pack installation information:
Standalone source installation
==============================
See the Forge Documentation online for more detailed instructions:
http://mcforge.readthedocs.io/en/latest/gettingstarted/
Step 1: Open your command-line and browse to the folder where you extracted the zip file.
Step 2: You're left with a choice.
If you prefer to use Eclipse:
1. Run the following command: "gradlew genEclipseRuns" (./gradlew genEclipseRuns if you are on Mac/Linux)
2. Open Eclipse, Import > Existing Gradle Project > Select Folder
or run "gradlew eclipse" to generate the project.
(Current Issue)
4. Open Project > Run/Debug Settings > Edit runClient and runServer > Environment
5. Edit MOD_CLASSES to show [modid]%%[Path]; 2 times rather then the generated 4.
If you prefer to use IntelliJ:
1. Open IDEA, and import project.
2. Select your build.gradle file and have it import.
3. Run the following command: "gradlew genIntellijRuns" (./gradlew genIntellijRuns if you are on Mac/Linux)
4. Refresh the Gradle Project in IDEA if required.
If at any point you are missing libraries in your IDE, or you've run into problems you can run "gradlew --refresh-dependencies" to refresh the local cache. "gradlew clean" to reset everything {this does not affect your code} and then start the processs again.
Should it still not work,
Refer to #ForgeGradle on EsperNet for more information about the gradle environment.
or the Forge Project Discord discord.gg/UvedJ9m
Forge source installation
=========================
MinecraftForge ships with this code and installs it as part of the forge
installation process, no further action is required on your part.
LexManos' Install Video
=======================
https://www.youtube.com/watch?v=8VEdtQLuLO0&feature=youtu.be
For more details update more often refer to the Forge Forums:
http://www.minecraftforge.net/forum/index.php/topic,14048.0.html

View File

@@ -1,77 +1,202 @@
buildscript { buildscript {
repositories { repositories {
jcenter() maven { url = 'https://maven.minecraftforge.net' }
maven { url = "http://files.minecraftforge.net/maven" } mavenCentral()
maven { url = 'https://repo.spongepowered.org/repository/maven-public' }
maven { url = 'https://maven.parchmentmc.org' }
} }
dependencies { dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' classpath "net.minecraftforge.gradle:ForgeGradle:${forgegradle_version}"
classpath "org.spongepowered:mixingradle:${mixingradle_version}"
classpath "org.parchmentmc:librarian:${librarian_version}"
} }
} }
apply plugin: 'net.minecraftforge.gradle.forge'
//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
plugins {
id 'com.matthewprenger.cursegradle' version "${cursegradle_version}"
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'org.parchmentmc.librarian.forgegradle'
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
apply plugin: 'org.spongepowered.mixin'
version = "0.1.1" jarJar.enable()
group = "nl.requios.effortlessbuilding" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "effortlessbuilding"
sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. boolean flywheelInWorkspace = findProject(':Flywheel') != null
compileJava {
sourceCompatibility = targetCompatibility = '1.8' ext.buildNumber = System.getenv('BUILD_NUMBER')
version = mod_version
group = 'nl.requios.effortlessbuilding'
base {
archivesName = "effortlessbuilding-${artifact_minecraft_version}"
} }
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + ' (' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
minecraft { minecraft {
version = "1.12.2-14.23.4.2705" mappings channel: 'parchment', version: "${parchment_version}-${minecraft_version}"
runDir = "run" accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// the mappings can be changed at any time, and must be in the following format. runs {
// snapshot_YYYYMMDD snapshot are built nightly. client {
// stable_# stables are built at the discretion of the MCP team. workingDirectory project.file('run')
// Use non-default mappings at your own risk. they may not always work. arg '-mixin.config=flywheel.mixins.json'
// simply re-run your setup task after changing the mappings to update your workspace. //jvmArgs '-XX:+UnlockCommercialFeatures' // uncomment for profiling
mappings = "snapshot_20171003" property 'forge.logging.console.level', 'info'
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
mods {
effortlessbuilding {
source sourceSets.main
}
if (flywheelInWorkspace) {
flywheel {
source project(":Flywheel").sourceSets.main
}
}
}
}
server {
workingDirectory project.file('run/server')
property 'forge.logging.console.level', 'info'
mods {
effortlessbuilding {
source sourceSets.main
}
}
}
data {
workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES,REGISTRYDUMP'
property 'forge.logging.console.level', 'debug'
args '--mod', 'effortlessbuilding', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources')
mods {
effortlessbuilding {
source sourceSets.main
}
if (flywheelInWorkspace) {
flywheel {
source project(":Flywheel").sourceSets.main
}
}
}
}
}
}
repositories {
maven {
// location of the maven for Registrate and Flywheel
name = 'tterrag maven'
url = 'https://maven.tterrag.com'
}
maven {
url = 'https://www.cursemaven.com'
content {
includeGroup "curse.maven"
}
}
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
mavenLocal()
flatDir {
dirs 'libs'
}
} }
dependencies { dependencies {
// you may put jars on which you depend on in ./libs minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
// or you may define them like so..
//compile "some.group:artifact:version:classifier"
//compile "some.group:artifact:version"
// real examples
//compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
//compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
// the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. jarJar("com.jozufozu.flywheel:flywheel-forge-${flywheel_minecraft_version}:${flywheel_version}") {
//provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' jarJar.ranged(it, '[0.6.9,0.7)')
// the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided,
// except that these dependencies get remapped to your current MCP mappings
//deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev'
//deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// for more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
}
processResources {
// this will ensure that this task is redone when the versions change.
inputs.property "version", project.version
inputs.property "mcversion", project.minecraft.version
// replace stuff in mcmod.info, nothing else
from(sourceSets.main.resources.srcDirs) {
include 'mcmod.info'
// replace version and mcversion
expand 'version':project.version, 'mcversion':project.minecraft.version
} }
// copy everything else except the mcmod.info if (flywheelInWorkspace) {
from(sourceSets.main.resources.srcDirs) { implementation project(':Flywheel')
exclude 'mcmod.info' } else {
implementation fg.deobf("com.jozufozu.flywheel:flywheel-forge-${flywheel_minecraft_version}:${flywheel_version}")
}
// Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings
// This allows 'Settings > Build, Execution, and Deployment > Build Tools > Gradle > Build and run using' set to IntelliJ to work correctly
if (!Boolean.getBoolean('idea.sync.active')) {
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
} }
} }
sourceSets.main.resources {
srcDir 'src/generated/resources'
exclude '.cache/'
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}
compileJava {
options.compilerArgs = ['-Xdiags:verbose']
}
jar {
manifest {
attributes([
"Specification-Title": "effortlessbuilding",
"Specification-Vendor": "requios",
"Specification-Version": "1",
"Implementation-Title": project.name,
"Implementation-Version": project.jar.archiveVersion,
"Implementation-Vendor" :"requios",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
}
}
task jarJarRelease {
group = 'jarjar'
doLast {
tasks.jarJar {
classifier = ''
}
}
finalizedBy tasks.jarJar
}
java {
withSourcesJar()
withJavadocJar()
}
jar.finalizedBy('reobfJar')
tasks.jarJar.finalizedBy('reobfJarJar')
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = base.archivesName.get()
from components.java
fg.component(it)
jarJar.component(it)
}
}
repositories {
if (project.hasProperty('mavendir')) {
maven { url mavendir }
}
}
}

20
gradle.properties Normal file
View File

@@ -0,0 +1,20 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
mod_version = 3.7
artifact_minecraft_version = 1.19.4
minecraft_version = 1.19.4
forge_version = 45.2.6
forgegradle_version = 6.0.7
mixingradle_version = 0.7-SNAPSHOT
mixin_version = 0.8.5
librarian_version = 1.+
cursegradle_version = 1.4.0
parchment_version = 2023.06.26
flywheel_minecraft_version = 1.19.3
flywheel_version = 0.6.9-2

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Mon Sep 14 12:28:28 PDT 2015
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip

314
gradlew vendored
View File

@@ -1,79 +1,129 @@
#!/usr/bin/env bash #!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="" DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn ( ) { warn () {
echo "$*" echo "$*"
} } >&2
die ( ) { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
case "`uname`" in nonstop=false
CYGWIN* ) case "$( uname )" in #(
cygwin=true CYGWIN* ) cygwin=true ;; #(
;; Darwin* ) darwin=true ;; #(
Darwin* ) MSYS* | MINGW* ) msys=true ;; #(
darwin=true NONSTOP* ) nonstop=true ;;
;;
MINGW* )
msys=true
;;
esac esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -82,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@@ -90,75 +140,105 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
MAX_FD="$MAX_FD_LIMIT" # shellcheck disable=SC3045
fi MAX_FD=$( ulimit -H -n ) ||
ulimit -n $MAX_FD warn "Could not query maximum file descriptor limit"
if [ $? -ne 0 ] ; then esac
warn "Could not set maximum file descriptor limit: $MAX_FD" case $MAX_FD in #(
fi '' | soft) :;; #(
else *)
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
fi # shellcheck disable=SC3045
fi ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules # Collect all arguments for the java command, stacking in reverse order:
function splitJvmOpts() { # * args from the command line
JVM_OPTS=("$@") # * the main class name
} # * -classpath
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS # * -D...appname settings
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

66
gradlew.bat vendored
View File

@@ -1,4 +1,20 @@
@if "%DEBUG%" == "" @echo off @rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -8,20 +24,24 @@
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if %ERRORLEVEL% equ 0 goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +55,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,44 +65,26 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

10
settings.gradle Normal file
View File

@@ -0,0 +1,10 @@
pluginManagement {
repositories {
gradlePluginPortal()
maven { url = 'https://maven.minecraftforge.net/' }
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'
}

View File

@@ -0,0 +1,69 @@
package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.gui.UIRenderHelper;
import nl.requios.effortlessbuilding.create.foundation.gui.element.ScreenElement;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
public enum AllGuiTextures implements ScreenElement {
ARRAY_ENTRY("modifiers", 226, 64),
MIRROR_ENTRY("modifiers", 0, 64, 226, 64),
RADIAL_MIRROR_ENTRY("modifiers", 0, 128, 226, 64),
ENABLE_BUTTON_BACKGROUND("modifiers", 234, 0, 9, 9),
CHECKMARK("modifiers", 243, 0, 10, 9),
ARROW_UP("modifiers", 234, 9, 9, 9),
ARROW_DOWN("modifiers", 243, 9, 9, 9),
TRASH("modifiers", 234, 18, 9, 9),
;
public final ResourceLocation location;
public int width, height;
public int startX, startY;
private AllGuiTextures(String location, int width, int height) {
this(location, 0, 0, width, height);
}
private AllGuiTextures(int startX, int startY) {
this("icons", startX * 16, startY * 16, 16, 16);
}
private AllGuiTextures(String location, int startX, int startY, int width, int height) {
this(EffortlessBuilding.MODID, location, startX, startY, width, height);
}
private AllGuiTextures(String namespace, String location, int startX, int startY, int width, int height) {
this.location = new ResourceLocation(namespace, "textures/gui/" + location + ".png");
this.width = width;
this.height = height;
this.startX = startX;
this.startY = startY;
}
@OnlyIn(Dist.CLIENT)
public void bind() {
RenderSystem.setShaderTexture(0, location);
}
@OnlyIn(Dist.CLIENT)
@Override
public void render(PoseStack poseStack, int x, int y) {
bind();
GuiComponent.blit(poseStack, x, y, 0, startX, startY, width, height, 256, 256);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack poseStack, int x, int y, GuiComponent component) {
bind();
component.blit(poseStack, x, y, 0, startX, startY, width, height, 256, 256);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack poseStack, int x, int y, Color c, GuiComponent component) {
bind();
UIRenderHelper.drawColoredTexture(poseStack, c, x, y, startX, startY, width, height);
}
}

View File

@@ -0,0 +1,151 @@
package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.Create;
import nl.requios.effortlessbuilding.create.foundation.gui.element.DelegatedStencilElement;
import nl.requios.effortlessbuilding.create.foundation.gui.element.ScreenElement;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import org.joml.Matrix4f;
public class AllIcons implements ScreenElement {
public static final ResourceLocation ICON_ATLAS = Create.asResource("textures/gui/icons.png");
public static final int ICON_ATLAS_SIZE = 256;
private static int x = 0, y = -1;
private int iconX;
private int iconY;
public static final AllIcons
I_SETTINGS = newRow(),
I_UNDO = next(),
I_REDO = next(),
I_REPLACE = next(),
I_REPLACE_AIR = next(),
I_REPLACE_BLOCKS_AND_AIR = next(),
I_REPLACE_BLOCKS = next(),
I_REPLACE_OFFHAND_FILTERED = next(),
I_PROTECT_TILE_ENTITIES = next();
public static final AllIcons
I_DISABLE = newRow(),
I_SINGLE = next(),
I_LINE = next(),
I_WALL = next(),
I_FLOOR = next(),
I_CUBE = next(),
I_DIAGONAL_LINE = next(),
I_DIAGONAL_WALL = next(),
I_SLOPED_FLOOR = next(),
I_CIRCLE = next(),
I_CYLINDER = next(),
I_SPHERE = next(),
I_PYRAMID = next(),
I_CONE = next(),
I_DOME = next();
public static final AllIcons
I_NORMAL_SPEED = newRow(),
I_FAST_SPEED = next(),
I_FILLED = next(),
I_HOLLOW = next(),
I_CUBE_FILLED = next(),
I_CUBE_HOLLOW = next(),
I_CUBE_SKELETON = next(),
I_SHORT_EDGE = next(),
I_LONG_EDGE = next(),
I_CIRCLE_START_CORNER = next(),
I_CIRCLE_START_CENTER = next(),
I_THICKNESS_1 = next(),
I_THICKNESS_3 = next(),
I_THICKNESS_5 = next();
public static final AllIcons
I_PLAYER = newRow(),
I_BLOCK_CENTER = next(),
I_BLOCK_CORNER = next(),
I_HIDE_LINES = next(),
I_SHOW_LINES = next(),
I_HIDE_AREAS = next(),
I_SHOW_AREAS = next(),
I_X_OFF = next(),
I_X_ON = next(),
I_Y_OFF = next(),
I_Y_ON = next(),
I_Z_OFF = next(),
I_Z_ON = next(),
I_ALTERNATE_OFF = next(),
I_ALTERNATE_ON = next();
public AllIcons(int x, int y) {
iconX = x * 16;
iconY = y * 16;
}
private static AllIcons next() {
return new AllIcons(++x, y);
}
private static AllIcons newRow() {
return new AllIcons(x = 0, ++y);
}
public void bind() {
RenderSystem.setShaderTexture(0, ICON_ATLAS);
}
@OnlyIn(Dist.CLIENT)
@Override
public void render(PoseStack ms, int x, int y) {
bind();
GuiComponent.blit(ms, x, y, 0, iconX, iconY, 16, 16, 256, 256);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack ms, MultiBufferSource buffer, int color) {
VertexConsumer builder = buffer.getBuffer(RenderType.textSeeThrough(ICON_ATLAS));
Matrix4f matrix = ms.last().pose();
Color rgb = new Color(color);
int light = LightTexture.FULL_BRIGHT;
Vec3 vec1 = new Vec3(0, 0, 0);
Vec3 vec2 = new Vec3(0, 1, 0);
Vec3 vec3 = new Vec3(1, 1, 0);
Vec3 vec4 = new Vec3(1, 0, 0);
float u1 = iconX * 1f / ICON_ATLAS_SIZE;
float u2 = (iconX + 16) * 1f / ICON_ATLAS_SIZE;
float v1 = iconY * 1f / ICON_ATLAS_SIZE;
float v2 = (iconY + 16) * 1f / ICON_ATLAS_SIZE;
vertex(builder, matrix, vec1, rgb, u1, v1, light);
vertex(builder, matrix, vec2, rgb, u1, v2, light);
vertex(builder, matrix, vec3, rgb, u2, v2, light);
vertex(builder, matrix, vec4, rgb, u2, v1, light);
}
@OnlyIn(Dist.CLIENT)
private void vertex(VertexConsumer builder, Matrix4f matrix, Vec3 vec, Color rgb, float u, float v, int light) {
builder.vertex(matrix, (float) vec.x, (float) vec.y, (float) vec.z)
.color(rgb.getRed(), rgb.getGreen(), rgb.getBlue(), 255)
.uv(u, v)
.uv2(light)
.endVertex();
}
@OnlyIn(Dist.CLIENT)
public DelegatedStencilElement asStencil() {
return new DelegatedStencilElement().withStencilRenderer((ms, w, h, alpha) -> this.render(ms, 0, 0)).withBounds(16, 16);
}
}

View File

@@ -1,82 +0,0 @@
package nl.requios.effortlessbuilding;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.world.BlockEvent;
public class Array {
//TODO config file
public static final int MAX_COUNT = 100;
public static class ArraySettings{
public boolean enabled = false;
public BlockPos offset = BlockPos.ORIGIN;
public int count = 5;
public ArraySettings(){
}
public ArraySettings(boolean enabled, BlockPos offset, int count) {
this.enabled = enabled;
this.offset = offset;
this.count = count;
}
}
//Called from EventHandler
public static void onBlockPlaced(BlockEvent.PlaceEvent event) {
if (event.getWorld().isRemote) return;
//find arraysettings for the player that placed the block
ArraySettings a = BuildSettingsManager.getBuildSettings(event.getPlayer()).getArraySettings();
if (a == null || !a.enabled) return;
if (a.offset.getX() == 0 && a.offset.getY() == 0 && a.offset.getZ() == 0) return;
BlockPos pos = event.getPos();
Vec3i offset = new Vec3i(a.offset.getX(), a.offset.getY(), a.offset.getZ());
for (int i = 0; i < a.count; i++) {
pos = pos.add(offset);
if (event.getWorld().isBlockLoaded(pos, true)) {
event.getWorld().setBlockState(pos, event.getPlacedBlock());
//Mirror synergy
BlockSnapshot blockSnapshot = new BlockSnapshot(event.getWorld(), pos, event.getPlacedBlock());
BlockEvent.PlaceEvent placeEvent = new BlockEvent.PlaceEvent(blockSnapshot, event.getPlacedBlock(), event.getPlayer(), EnumHand.MAIN_HAND);
Mirror.onBlockPlaced(placeEvent);
}
}
}
//Called from EventHandler
public static void onBlockBroken(BlockEvent.BreakEvent event) {
if (event.getWorld().isRemote) return;
//find arraysettings for the player that placed the block
ArraySettings a = BuildSettingsManager.getBuildSettings(event.getPlayer()).getArraySettings();
if (a == null || !a.enabled) return;
if (a.offset.getX() == 0 && a.offset.getY() == 0 && a.offset.getZ() == 0) return;
BlockPos pos = event.getPos();
Vec3i offset = new Vec3i(a.offset.getX(), a.offset.getY(), a.offset.getZ());
for (int i = 0; i < a.count; i++) {
pos = pos.add(offset);
if (event.getWorld().isBlockLoaded(pos, false)) {
event.getWorld().setBlockToAir(pos);
//Mirror synergy
BlockEvent.BreakEvent breakEvent = new BlockEvent.BreakEvent(event.getWorld(), pos, event.getState(), event.getPlayer());
Mirror.onBlockBroken(breakEvent);
}
}
}
// @SubscribeEvent
// @SideOnly(Side.CLIENT)
// public static void onRender(RenderWorldLastEvent event) {
//
// }
}

View File

@@ -1,102 +0,0 @@
package nl.requios.effortlessbuilding;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
import nl.requios.effortlessbuilding.capability.BuildModifierCapability;
import nl.requios.effortlessbuilding.network.BuildSettingsMessage;
@Mod.EventBusSubscriber
public class BuildSettingsManager {
public static BuildSettings getBuildSettings(EntityPlayer player){
if (player.hasCapability(BuildModifierCapability.buildModifier, null)) {
BuildModifierCapability.IBuildModifier capability = player.getCapability(BuildModifierCapability.buildModifier, null);
return capability.getBuildModifierData();
}
EffortlessBuilding.log("buildsettings is null");
return null;
}
public static void setBuildSettings(EntityPlayer player, BuildSettings buildSettings) {
if (player == null) {
EffortlessBuilding.log("cannot set buildsettings, player is null");
return;
}
if (player.hasCapability(BuildModifierCapability.buildModifier, null)) {
BuildModifierCapability.IBuildModifier capability = player.getCapability(BuildModifierCapability.buildModifier, null);
capability.setBuildModifierData(buildSettings);
} else {
EffortlessBuilding.log(player, "Saving buildsettings failed.");
}
}
public static class BuildSettings {
private Mirror.MirrorSettings mirrorSettings;
private Array.ArraySettings arraySettings;
private boolean quickReplace = false;
public BuildSettings() {
mirrorSettings = new Mirror.MirrorSettings();
arraySettings = new Array.ArraySettings();
}
public BuildSettings(Mirror.MirrorSettings mirrorSettings, Array.ArraySettings arraySettings, boolean quickReplace) {
this.mirrorSettings = mirrorSettings;
this.arraySettings = arraySettings;
this.quickReplace = quickReplace;
}
public Mirror.MirrorSettings getMirrorSettings() {
return mirrorSettings;
}
public void setMirrorSettings(Mirror.MirrorSettings mirrorSettings) {
this.mirrorSettings = mirrorSettings;
}
public Array.ArraySettings getArraySettings() {
return arraySettings;
}
public void setArraySettings(Array.ArraySettings arraySettings) {
this.arraySettings = arraySettings;
}
public boolean doQuickReplace() {
return quickReplace;
}
public void setQuickReplace(boolean quickReplace) {
this.quickReplace = quickReplace;
}
}
@SubscribeEvent
public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
EntityPlayer player = event.player;
handleNewPlayer(player);
}
@SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
EntityPlayer player = event.player;
handleNewPlayer(player);
}
private static void handleNewPlayer(EntityPlayer player){
if (getBuildSettings(player) == null) {
setBuildSettings(player, new BuildSettings());
}
//Only on server
if (!player.world.isRemote) {
//Send to client
BuildSettingsMessage msg = new BuildSettingsMessage(getBuildSettings(player));
EffortlessBuilding.packetHandler.sendTo(msg, (EntityPlayerMP) player);
}
}
}

View File

@@ -0,0 +1,49 @@
package nl.requios.effortlessbuilding;
import net.minecraftforge.common.ForgeConfigSpec;
import static net.minecraftforge.common.ForgeConfigSpec.*;
public class ClientConfig {
private static final Builder builder = new Builder();
public static final Visuals visuals = new Visuals(builder);
public static final ForgeConfigSpec spec = builder.build();
public static class Visuals {
public final BooleanValue showBlockPreviews;
public final BooleanValue onlyShowBlockPreviewsWhenBuilding;
public final IntValue maxBlockPreviews;
public final IntValue appearAnimationLength;
public final IntValue breakAnimationLength;
public Visuals(Builder builder) {
builder.push("Visuals");
showBlockPreviews = builder
.comment("Show previews of the blocks while placing them")
.define("showBlockPreviews", true);
onlyShowBlockPreviewsWhenBuilding = builder
.comment("Show block previews only when actively using a build mode")
.define("onlyShowBlockPreviewsWhenBuilding", true);
maxBlockPreviews = builder
.comment("Don't show block previews when placing more than this many blocks. " +
"The outline will always be rendered.")
.defineInRange("maxBlockPreviews", 400, 0, 5000);
appearAnimationLength = builder
.comment("How long it takes for a block to appear when placed in ticks.",
"Set to 0 to disable animation.")
.defineInRange("appearAnimationLength", 5, 0, 100);
breakAnimationLength = builder
.comment("How long the break animation is in ticks.",
"Set to 0 to disable animation.")
.defineInRange("breakAnimationLength", 10, 0, 100);
builder.pop();
}
}
}

View File

@@ -0,0 +1,207 @@
package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.client.settings.KeyConflictContext;
import net.minecraftforge.client.settings.KeyModifier;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.gui.buildmode.PlayerSettingsGui;
import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu;
import nl.requios.effortlessbuilding.gui.buildmodifier.ModifiersScreen;
import org.lwjgl.glfw.GLFW;
@EventBusSubscriber(Dist.CLIENT)
public class ClientEvents {
public static KeyMapping[] keyBindings;
public static int ticksInGame = 0;
private static int placeCooldown = 0;
private static int breakCooldown = 0;
//Mod Bus Events
@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD)
public static class ModBusEvents {
@SubscribeEvent
public static void registerKeyMappings(RegisterKeyMappingsEvent event) {
keyBindings = new KeyMapping[6];
keyBindings[0] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category");
keyBindings[1] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category");
keyBindings[2] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category");
keyBindings[3] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category");
keyBindings[4] = new KeyMapping("key.effortlessbuilding.previous_build_mode.desc", KeyConflictContext.IN_GAME, InputConstants.UNKNOWN, "key.effortlessbuilding.category");
keyBindings[5] = new KeyMapping("key.effortlessbuilding.disable_build_mode_toggle.desc", KeyConflictContext.IN_GAME, InputConstants.UNKNOWN, "key.effortlessbuilding.category");
for (KeyMapping keyBinding : keyBindings) {
event.register(keyBinding);
}
}
// @SubscribeEvent
// public static void registerShaders(RegisterShadersEvent event) throws IOException {
// event.registerShader(new ShaderInstance(event.getResourceManager(),
// new ResourceLocation(EffortlessBuilding.MODID, "dissolve"),
// DefaultVertexFormat.BLOCK),
// shaderInstance -> BuildRenderTypes.dissolveShaderInstance = shaderInstance);
// }
}
@SubscribeEvent
public static void onClientTick(TickEvent.ClientTickEvent event) {
if (!isGameActive()) return;
if (event.phase == TickEvent.Phase.START) {
EffortlessBuildingClient.BUILDER_CHAIN.onTick();
onMouseInput();
EffortlessBuildingClient.BLOCK_PREVIEWS.onTick();
} else if (event.phase == TickEvent.Phase.END) {
Screen gui = Minecraft.getInstance().screen;
if (gui == null || !gui.isPauseScreen()) {
ticksInGame++;
}
}
}
private static void onMouseInput() {
Minecraft mc = Minecraft.getInstance();
LocalPlayer player = mc.player;
if (player == null) return;
BuildModeEnum buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
if (mc.screen != null ||
RadialMenu.instance.isVisible()) {
return;
}
if (mc.options.keyUse.isDown()) {
if (placeCooldown <= 0) {
placeCooldown = 4;
EffortlessBuildingClient.BUILDER_CHAIN.onRightClick();
} else if (buildMode == BuildModeEnum.SINGLE) {
placeCooldown--;
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0;
}
} else {
placeCooldown = 0;
}
if (mc.options.keyAttack.isDown()) {
//Break block in distance in creative (or survival if enabled in config)
if (breakCooldown <= 0) {
breakCooldown = 4;
EffortlessBuildingClient.BUILDER_CHAIN.onLeftClick();
} else if (buildMode == BuildModeEnum.SINGLE) {
breakCooldown--;
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0;
}
} else {
breakCooldown = 0;
}
}
@SubscribeEvent(receiveCanceled = true)
public static void onKeyPress(InputEvent.Key event) {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null)
return;
//Radial menu
if (keyBindings[0].isDown()) {
if (!EffortlessBuildingClient.POWER_LEVEL.isDisabled(player)) {
if (!RadialMenu.instance.isVisible()) {
Minecraft.getInstance().setScreen(RadialMenu.instance);
}
} else {
EffortlessBuilding.log(player, "Build modes are disabled until your reach has increased. Increase your reach with craftable reach upgrades.");
}
}
//Show Modifier Settings GUI
if (keyBindings[1].consumeClick()) {
openModifierSettings();
}
//Undo (Ctrl+Z)
if (keyBindings[2].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.UNDO);
}
//Redo (Ctrl+Y)
if (keyBindings[3].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.REDO);
}
//Previous build mode
if (keyBindings[4].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.PREVIOUS_BUILD_MODE);
}
//Disable build mode toggle
if (keyBindings[5].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.DISABLE_BUILD_MODE_TOGGLE);
}
}
public static void openModifierSettings() {
Minecraft mc = Minecraft.getInstance();
LocalPlayer player = mc.player;
if (player == null) return;
//Disabled if max reach is 0, might be set in the config that way.
if (EffortlessBuildingClient.POWER_LEVEL.isDisabled(player)) {
EffortlessBuilding.log(player, "Build modifiers are disabled until your power level has increased. Increase your power level by consuming certain items.");
} else {
mc.setScreen(new ModifiersScreen());
}
}
public static void openPlayerSettings() {
Minecraft mc = Minecraft.getInstance();
mc.setScreen(new PlayerSettingsGui());
}
@SubscribeEvent
public static void onGuiOpen(ScreenEvent event) {
Player player = Minecraft.getInstance().player;
if (player != null) {
EffortlessBuildingClient.BUILDER_CHAIN.cancel();
}
}
public static boolean isKeybindDown(int keybindIndex) {
return InputConstants.isKeyDown(
Minecraft.getInstance().getWindow().getWindow(),
keyBindings[keybindIndex].getKey().getValue());
}
public static boolean isGameActive() {
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
}
}

View File

@@ -0,0 +1,145 @@
package nl.requios.effortlessbuilding;
import net.minecraftforge.common.ForgeConfigSpec;
import static net.minecraftforge.common.ForgeConfigSpec.*;
public class CommonConfig {
private static final Builder builder = new Builder();
public static final Reach reach = new Reach(builder);
public static final MaxBlocksPlacedAtOnce maxBlocksPlacedAtOnce = new MaxBlocksPlacedAtOnce(builder);
public static final MaxBlocksPerAxis maxBlocksPerAxis = new MaxBlocksPerAxis(builder);
public static final MaxMirrorRadius maxMirrorRadius = new MaxMirrorRadius(builder);
public static final ForgeConfigSpec spec = builder.build();
public static class Reach {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public Reach(Builder builder) {
builder.push("Reach");
creative = builder
.comment("How far away the player can place and break blocks.")
.defineInRange("reachCreative", 200, 0, 1000);
level0 = builder
.comment("Maximum reach in survival without upgrades",
"Consume Power Level upgrades to permanently increase this.")
.defineInRange("reachLevel0", 0, 0, 1000);
level1 = builder
.defineInRange("reachLevel1", 8, 0, 1000);
level2 = builder
.defineInRange("reachLevel2", 16, 0, 1000);
level3 = builder
.defineInRange("reachLevel3", 32, 0, 1000);
builder.pop();
}
}
public static class MaxBlocksPlacedAtOnce {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public MaxBlocksPlacedAtOnce(Builder builder) {
builder.push("MaxBlocksPlacedAtOnce");
creative = builder
.comment("How many blocks can be placed in one click.")
.defineInRange("maxBlocksPlacedAtOnceCreative", 10000, 0, 100000);
level0 = builder
.comment("In survival without upgrades",
"Consume Power Level upgrades to permanently increase this.",
"Set to 0 to disable Effortless Building until the player has increased their Building Power Level.")
.defineInRange("maxBlocksPlacedAtOnceLevel0", 128, 0, 100000);
level1 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel1", 192, 0, 100000);
level2 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel2", 320, 0, 100000);
level3 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel3", 640, 0, 100000);
builder.pop();
}
}
public static class MaxBlocksPerAxis {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public MaxBlocksPerAxis(Builder builder) {
builder.push("MaxBlocksPerAxis");
creative = builder
.comment("How many blocks can be placed at once per axis when using build modes (e.g. walls).",
"Also affects the array modifier.")
.defineInRange("maxBlocksPerAxisCreative", 1000, 0, 1000);
level0 = builder
.comment("In survival without upgrades",
"Consume Power Level upgrades to permanently increase this.")
.defineInRange("maxBlocksPerAxisLevel0", 8, 0, 1000);
level1 = builder
.defineInRange("maxBlocksPerAxisLevel1", 12, 0, 1000);
level2 = builder
.defineInRange("maxBlocksPerAxisLevel2", 16, 0, 1000);
level3 = builder
.defineInRange("maxBlocksPerAxisLevel3", 20, 0, 1000);
builder.pop();
}
}
public static class MaxMirrorRadius {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public MaxMirrorRadius(Builder builder) {
builder.push("MaxMirrorRadius");
creative = builder
.comment("The maximum (radial) mirror radius.")
.defineInRange("maxMirrorRadiusCreative", 200, 0, 1000);
level0 = builder
.comment("Maximum reach in survival without upgrades",
"Consume Power Level upgrades upgrades to permanently increase this.")
.defineInRange("maxMirrorRadiusLevel0", 16, 0, 1000);
level1 = builder
.defineInRange("maxMirrorRadiusLevel1", 32, 0, 1000);
level2 = builder
.defineInRange("maxMirrorRadiusLevel2", 48, 0, 1000);
level3 = builder
.defineInRange("maxMirrorRadiusLevel3", 64, 0, 1000);
builder.pop();
}
}
}

View File

@@ -0,0 +1,139 @@
package nl.requios.effortlessbuilding;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.network.ModifierSettingsPacket;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.systems.ServerBuildState;
import nl.requios.effortlessbuilding.utilities.PowerLevelCommand;
@EventBusSubscriber
public class CommonEvents {
//Mod Bus Events
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)
public static class ModBusEvents {
}
@SubscribeEvent
public static void registerCommands(RegisterCommandsEvent event) {
PowerLevelCommand.register(event.getDispatcher());
}
@SubscribeEvent
public static void onTick(TickEvent.LevelTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (event.side == LogicalSide.CLIENT) return;
EffortlessBuilding.SERVER_BLOCK_PLACER.tick();
}
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
@SubscribeEvent
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
if (event.getLevel().isClientSide()) return; //Never called clientside anyway, but just to be sure
if (!(event.getEntity() instanceof Player player)) return;
if (event.getEntity() instanceof FakePlayer) return;
//Don't cancel event if our custom logic is breaking blocks
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
if (!ServerBuildState.isLikeVanilla(player)) {
//Only cancel if itemblock in hand
//Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled.
if (isPlayerHoldingBlock(player)) {
event.setCanceled(true);
//TODO Notify client to not decrease itemstack
}
}
}
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
@SubscribeEvent
public static void onBlockBroken(BlockEvent.BreakEvent event) {
if (event.getLevel().isClientSide()) return;
Player player = event.getPlayer();
if (player instanceof FakePlayer) return;
//Don't cancel event if our custom logic is breaking blocks
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
if (!ServerBuildState.isLikeVanilla(player) && EffortlessBuilding.SERVER_POWER_LEVEL.canBreakFar(player)) {
event.setCanceled(true);
}
}
private static boolean isPlayerHoldingBlock(Player player) {
ItemStack currentItemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
return currentItemStack.getItem() instanceof BlockItem ||
(CompatHelper.isItemBlockProxy(currentItemStack) && !player.isShiftKeyDown());
}
@SubscribeEvent
public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) return;
ServerBuildState.handleNewPlayer(player);
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new ModifierSettingsPacket(player));
EffortlessBuilding.SERVER_POWER_LEVEL.sendToClient(player);
}
@SubscribeEvent
public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) {
EffortlessBuilding.log("PlayerLoggedOutEvent triggers on client side");
return;
}
EffortlessBuilding.UNDO_REDO.clear(player);
}
@SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) {
EffortlessBuilding.log("PlayerRespawnEvent triggers on client side");
return;
}
//TODO check if this is needed
ServerBuildState.handleNewPlayer(player);
}
@SubscribeEvent
public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) {
EffortlessBuilding.log("PlayerChangedDimensionEvent triggers on client side");
return;
}
//Undo redo has no dimension data, so clear it
EffortlessBuilding.UNDO_REDO.clear(player);
//TODO disable build mode and modifiers?
}
}

View File

@@ -1,106 +1,142 @@
package nl.requios.effortlessbuilding; package nl.requios.effortlessbuilding;
import net.minecraft.block.Block; import com.mojang.serialization.Codec;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.network.chat.Component;
import net.minecraft.item.Item; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.text.TextComponentString; import net.minecraft.world.flag.FeatureFlags;
import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.loot.IGlobalLootModifier;
import net.minecraftforge.event.CreativeModeTabEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.common.SidedProxy; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.network.IContainerFactory;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent; import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.registries.RegistryObject;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import net.minecraftforge.fml.relauncher.Side; import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagContainer;
import nl.requios.effortlessbuilding.capability.*; import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagContainer;
import nl.requios.effortlessbuilding.inventory.RandomizerGuiHandler; import nl.requios.effortlessbuilding.gui.RandomizerBagContainer;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag; import nl.requios.effortlessbuilding.item.*;
import nl.requios.effortlessbuilding.network.BuildSettingsMessage; import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.network.QuickReplaceMessage; import nl.requios.effortlessbuilding.proxy.ClientProxy;
import nl.requios.effortlessbuilding.proxy.IProxy; import nl.requios.effortlessbuilding.proxy.IProxy;
import nl.requios.effortlessbuilding.proxy.ServerProxy;
import nl.requios.effortlessbuilding.systems.ItemUsageTracker;
import nl.requios.effortlessbuilding.systems.ServerBlockPlacer;
import nl.requios.effortlessbuilding.systems.ServerPowerLevel;
import nl.requios.effortlessbuilding.systems.UndoRedo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@Mod(modid = EffortlessBuilding.MODID, name = EffortlessBuilding.NAME, version = EffortlessBuilding.VERSION) import java.util.List;
@Mod.EventBusSubscriber
public class EffortlessBuilding
{
public static final String MODID = "effortlessbuilding";
public static final String NAME = "Effortless Building";
public static final String VERSION = "0.1.2";
@Mod.Instance(EffortlessBuilding.MODID) @Mod(EffortlessBuilding.MODID)
public static EffortlessBuilding instance; public class EffortlessBuilding {
public static Logger logger; public static final String MODID = "effortlessbuilding";
public static final Logger logger = LogManager.getLogger();
@SidedProxy( public static EffortlessBuilding instance;
clientSide="nl.requios.effortlessbuilding.proxy.ClientProxy", public static IProxy proxy = DistExecutor.unsafeRunForDist(() -> ClientProxy::new, () -> ServerProxy::new);
serverSide="nl.requios.effortlessbuilding.proxy.ServerProxy"
)
public static IProxy proxy;
public static final SimpleNetworkWrapper packetHandler = NetworkRegistry.INSTANCE.newSimpleChannel(EffortlessBuilding.MODID); public static final ServerBlockPlacer SERVER_BLOCK_PLACER = new ServerBlockPlacer();
public static final UndoRedo UNDO_REDO = new UndoRedo();
public static final ItemUsageTracker ITEM_USAGE_TRACKER = new ItemUsageTracker();
public static final ServerPowerLevel SERVER_POWER_LEVEL = new ServerPowerLevel();
public static final Block[] BLOCKS = { //Registration
}; private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
private static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, EffortlessBuilding.MODID);
public static final DeferredRegister<Codec<? extends IGlobalLootModifier>> LOOT_MODIFIERS = DeferredRegister.create(ForgeRegistries.Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, EffortlessBuilding.MODID);
public static final Item[] ITEMS = { public static final RegistryObject<Item> RANDOMIZER_BAG_ITEM = ITEMS.register("randomizer_bag", RandomizerBagItem::new);
new ItemRandomizerBag() public static final RegistryObject<Item> GOLDEN_RANDOMIZER_BAG_ITEM = ITEMS.register("golden_randomizer_bag", GoldenRandomizerBagItem::new);
}; public static final RegistryObject<Item> DIAMOND_RANDOMIZER_BAG_ITEM = ITEMS.register("diamond_randomizer_bag", DiamondRandomizerBagItem::new);
public static final RegistryObject<Item> REACH_UPGRADE_1_ITEM = ITEMS.register("reach_upgrade1", ReachUpgrade1Item::new);
public static final RegistryObject<Item> REACH_UPGRADE_2_ITEM = ITEMS.register("reach_upgrade2", ReachUpgrade2Item::new);
public static final RegistryObject<Item> REACH_UPGRADE_3_ITEM = ITEMS.register("reach_upgrade3", ReachUpgrade3Item::new);
public static final RegistryObject<Item> MUSCLES_ITEM = ITEMS.register("muscles", PowerLevelItem::new);
public static final RegistryObject<Item> ELASTIC_HAND_ITEM = ITEMS.register("elastic_hand", PowerLevelItem::new);
public static final RegistryObject<Item> BUILDING_TECHNIQUES_BOOK_ITEM = ITEMS.register("building_techniques_book", PowerLevelItem::new);
public static final int RANDOMIZER_BAG_GUI = 0; public static final RegistryObject<MenuType<RandomizerBagContainer>> RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("randomizer_bag", () -> registerContainer(RandomizerBagContainer::new));
public static final RegistryObject<MenuType<GoldenRandomizerBagContainer>> GOLDEN_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("golden_randomizer_bag", () -> registerContainer(GoldenRandomizerBagContainer::new));
public static final RegistryObject<MenuType<DiamondRandomizerBagContainer>> DIAMOND_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("diamond_randomizer_bag", () -> registerContainer(DiamondRandomizerBagContainer::new));
@EventHandler
// preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the GameRegistry."
public void preInit(FMLPreInitializationEvent event)
{
logger = event.getModLog();
CapabilityManager.INSTANCE.register(BuildModifierCapability.IBuildModifier.class, new BuildModifierCapability.Storage(), BuildModifierCapability.BuildModifier.class); public EffortlessBuilding() {
instance = this;
EffortlessBuilding.packetHandler.registerMessage(BuildSettingsMessage.MessageHandler.class, BuildSettingsMessage.class, 0, Side.SERVER); ModLoadingContext modLoadingContext = ModLoadingContext.get();
EffortlessBuilding.packetHandler.registerMessage(BuildSettingsMessage.MessageHandler.class, BuildSettingsMessage.class, 0, Side.CLIENT); IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
IEventBus forgeEventBus = MinecraftForge.EVENT_BUS;
EffortlessBuilding.packetHandler.registerMessage(QuickReplaceMessage.MessageHandler.class, QuickReplaceMessage.class, 1, Side.SERVER); modEventBus.addListener(EffortlessBuilding::setup);
EffortlessBuilding.packetHandler.registerMessage(QuickReplaceMessage.MessageHandler.class, QuickReplaceMessage.class, 1, Side.CLIENT); modEventBus.addListener(EffortlessBuilding::addTabContents);
proxy.preInit(event); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> EffortlessBuildingClient.onConstructorClient(modEventBus, forgeEventBus));
}
@EventHandler ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
// Do your mod setup. Build whatever data structures you care about. CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus());
// Register network handlers
public void init(FMLInitializationEvent event)
{
NetworkRegistry.INSTANCE.registerGuiHandler(EffortlessBuilding.instance, new RandomizerGuiHandler());
proxy.init(event);
}
@EventHandler var singleItemLootModifier = SingleItemLootModifier.CODEC; //load this class to register the loot modifier
// postInit "Handle interaction with other mods, complete your setup based on this." LOOT_MODIFIERS.register(FMLJavaModLoadingContext.get().getModEventBus());
public void postInit(FMLPostInitializationEvent event)
{
proxy.postInit(event);
}
@EventHandler //Register config
public void serverStarting(FMLServerStartingEvent event) modLoadingContext.registerConfig(ModConfig.Type.COMMON, CommonConfig.spec);
{ modLoadingContext.registerConfig(ModConfig.Type.CLIENT, ClientConfig.spec);
proxy.serverStarting(event); modLoadingContext.registerConfig(ModConfig.Type.SERVER, ServerConfig.spec);
} }
public static void log(String msg){ public static void setup(final FMLCommonSetupEvent event) {
logger.info(msg); PacketHandler.register();
}
public static void log(EntityPlayer player, String msg){ CompatHelper.setup();
log(player, msg, false); }
}
public static void addTabContents(final CreativeModeTabEvent.BuildContents event) {
if (event.getTab() == CreativeModeTabs.TOOLS_AND_UTILITIES) {
List<ItemStack> stacks = ITEMS.getEntries().stream().map(reg -> new ItemStack(reg.get())).toList();
event.acceptAll(stacks);
}
}
public static <T extends AbstractContainerMenu> MenuType<T> registerContainer(IContainerFactory<T> fact){
MenuType<T> type = new MenuType<T>(fact, FeatureFlags.REGISTRY.allFlags());
return type;
}
public static void log(String msg) {
logger.info(msg);
}
public static void log(Player player, String msg) {
log(player, msg, false);
}
public static void log(Player player, String msg, boolean actionBar) {
player.displayClientMessage(Component.literal(msg), actionBar);
}
//Log with translation supported, call either on client or server (which then sends a message)
public static void logTranslate(Player player, String prefix, String translationKey, String suffix, boolean actionBar) {
proxy.logTranslate(player, prefix, translationKey, suffix, actionBar);
}
public static void logError(String msg) {
logger.error(msg);
}
public static void log(EntityPlayer player, String msg, boolean actionBar){
player.sendStatusMessage(new TextComponentString(msg), actionBar);
}
} }

View File

@@ -0,0 +1,34 @@
package nl.requios.effortlessbuilding;
import net.minecraft.client.gui.screens.MenuScreens;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagScreen;
import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagScreen;
import nl.requios.effortlessbuilding.gui.RandomizerBagScreen;
import nl.requios.effortlessbuilding.render.BlockPreviews;
import nl.requios.effortlessbuilding.systems.*;
public class EffortlessBuildingClient {
public static final BuilderChain BUILDER_CHAIN = new BuilderChain();
public static final BuildModes BUILD_MODES = new BuildModes();
public static final BuildModifiers BUILD_MODIFIERS = new BuildModifiers();
public static final BuildSettings BUILD_SETTINGS = new BuildSettings();
public static final BlockPreviews BLOCK_PREVIEWS = new BlockPreviews();
public static final BuilderFilter BUILDER_FILTER = new BuilderFilter();
public static final ItemUsageTracker ITEM_USAGE_TRACKER = new ItemUsageTracker();
public static final PowerLevel POWER_LEVEL = new PowerLevel();
public static void onConstructorClient(IEventBus modEventBus, IEventBus forgeEventBus) {
modEventBus.addListener(EffortlessBuildingClient::clientSetup);
}
public static void clientSetup(final FMLClientSetupEvent event) {
MenuScreens.register(EffortlessBuilding.RANDOMIZER_BAG_CONTAINER.get(), RandomizerBagScreen::new);
MenuScreens.register(EffortlessBuilding.GOLDEN_RANDOMIZER_BAG_CONTAINER.get(), GoldenRandomizerBagScreen::new);
MenuScreens.register(EffortlessBuilding.DIAMOND_RANDOMIZER_BAG_CONTAINER.get(), DiamondRandomizerBagScreen::new);
}
}

View File

@@ -1,59 +0,0 @@
package nl.requios.effortlessbuilding;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import nl.requios.effortlessbuilding.capability.BuildModifierCapability;
@Mod.EventBusSubscriber
public class EventHandler
{
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event)
{
event.getRegistry().registerAll(EffortlessBuilding.BLOCKS);
}
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event)
{
event.getRegistry().registerAll(EffortlessBuilding.ITEMS);
for (Block block : EffortlessBuilding.BLOCKS)
{
event.getRegistry().register(new ItemBlock(block).setRegistryName(block.getRegistryName()));
}
}
@SubscribeEvent
public static void attachCapabilities(AttachCapabilitiesEvent<Entity> event) {
if (event.getObject() instanceof EntityPlayer) {
event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "BuildModifier"), new BuildModifierCapability.Provider());
}
}
@SubscribeEvent
public static void onBlockPlaced(BlockEvent.PlaceEvent event) {
QuickReplace.onBlockPlaced(event);
if (event.isCanceled()) return;
Array.onBlockPlaced(event);
Mirror.onBlockPlaced(event);
}
@SubscribeEvent
public static void onBlockBroken(BlockEvent.BreakEvent event) {
Array.onBlockBroken(event);
Mirror.onBlockBroken(event);
}
}

View File

@@ -1,401 +0,0 @@
package nl.requios.effortlessbuilding;
import net.minecraft.block.BlockDirectional;
import net.minecraft.block.BlockDispenser;
import net.minecraft.block.BlockStairs;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Color;
@Mod.EventBusSubscriber
public class Mirror {
//TODO config file
public static final int MAX_RADIUS = 200;
private static final Color colorX = new Color(255, 72, 52);
private static final Color colorY = new Color(67, 204, 51);
private static final Color colorZ = new Color(52, 247, 255);
private static final int lineAlpha = 200;
private static final int planeAlpha = 75;
private static final Vec3d epsilon = new Vec3d(0.001, 0.001, 0.001); //prevents z-fighting
public static class MirrorSettings {
public boolean enabled = false;
public Vec3d position = new Vec3d(0.5, 64.5, 0.5);
public boolean mirrorX = true, mirrorY = false, mirrorZ = false;
public int radius = 20;
public boolean drawLines = true, drawPlanes = false;
public MirrorSettings() {
}
public MirrorSettings(boolean mirrorEnabled, Vec3d position, boolean mirrorX, boolean mirrorY, boolean mirrorZ, int radius, boolean drawLines, boolean drawPlanes) {
this.enabled = mirrorEnabled;
this.position = position;
this.mirrorX = mirrorX;
this.mirrorY = mirrorY;
this.mirrorZ = mirrorZ;
this.radius = radius;
this.drawLines = drawLines;
this.drawPlanes = drawPlanes;
}
}
//Called from EventHandler
public static void onBlockPlaced(BlockEvent.PlaceEvent event) {
if (event.getWorld().isRemote) return;
//find mirrorsettings for the player that placed the block
MirrorSettings m = BuildSettingsManager.getBuildSettings(event.getPlayer()).getMirrorSettings();
if (m == null) return;
if (!m.enabled || (!m.mirrorX && !m.mirrorY && !m.mirrorZ)) return;
//if within mirror distance, mirror
BlockPos oldBlockPos = event.getPos();
if (oldBlockPos.getX() + 0.5 < m.position.x - m.radius || oldBlockPos.getX() + 0.5 > m.position.x + m.radius ||
oldBlockPos.getY() + 0.5 < m.position.y - m.radius || oldBlockPos.getY() + 0.5 > m.position.y + m.radius ||
oldBlockPos.getZ() + 0.5 < m.position.z - m.radius || oldBlockPos.getZ() + 0.5 > m.position.z+ m.radius)
return;
if (m.mirrorX) {
placeMirrorX(event.getWorld(), m, oldBlockPos, event.getPlacedBlock());
}
if (m.mirrorY) {
placeMirrorY(event.getWorld(), m, oldBlockPos, event.getPlacedBlock());
}
if (m.mirrorZ) {
placeMirrorZ(event.getWorld(), m, oldBlockPos, event.getPlacedBlock());
}
}
private static void placeMirrorX(World world, MirrorSettings m, BlockPos oldBlockPos, IBlockState oldBlockState) {
//find mirror position
double x = m.position.x + (m.position.x - oldBlockPos.getX() - 0.5);
BlockPos newBlockPos = new BlockPos(x, oldBlockPos.getY(), oldBlockPos.getZ());
IBlockState newBlockState = oldBlockState;
//place block
if (world.isBlockLoaded(newBlockPos, true)) {
newBlockState = oldBlockState.withMirror(net.minecraft.util.Mirror.FRONT_BACK);
world.setBlockState(newBlockPos, newBlockState);
}
if (m.mirrorY) placeMirrorY(world, m, newBlockPos, newBlockState);
if (m.mirrorZ) placeMirrorZ(world, m, newBlockPos, newBlockState);
}
private static void placeMirrorY(World world, MirrorSettings m, BlockPos oldBlockPos, IBlockState oldBlockState) {
//find mirror position
double y = m.position.y + (m.position.y - oldBlockPos.getY() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), y, oldBlockPos.getZ());
IBlockState newBlockState = oldBlockState;
//place block
if (world.isBlockLoaded(newBlockPos, true)) {
newBlockState = getVerticalMirror(oldBlockState);
world.setBlockState(newBlockPos, newBlockState);
}
if (m.mirrorZ) placeMirrorZ(world, m, newBlockPos, newBlockState);
}
private static void placeMirrorZ(World world, MirrorSettings m, BlockPos oldBlockPos, IBlockState oldBlockState) {
//find mirror position
double z = m.position.z + (m.position.z - oldBlockPos.getZ() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), oldBlockPos.getY(), z);
IBlockState newBlockState = oldBlockState;
//place block
if (world.isBlockLoaded(newBlockPos, true)) {
newBlockState = oldBlockState.withMirror(net.minecraft.util.Mirror.LEFT_RIGHT);
world.setBlockState(newBlockPos, newBlockState);
}
}
private static IBlockState getVerticalMirror(IBlockState blockState) {
//Stairs
if (blockState.getBlock() instanceof BlockStairs) {
if (blockState.getValue(BlockStairs.HALF) == BlockStairs.EnumHalf.BOTTOM) {
return blockState.withProperty(BlockStairs.HALF, BlockStairs.EnumHalf.TOP);
} else {
return blockState.withProperty(BlockStairs.HALF, BlockStairs.EnumHalf.BOTTOM);
}
}
//Buttons, endrod, observer, piston
if (blockState.getBlock() instanceof BlockDirectional) {
if (blockState.getValue(BlockDirectional.FACING) == EnumFacing.DOWN) {
return blockState.withProperty(BlockDirectional.FACING, EnumFacing.UP);
} else if (blockState.getValue(BlockDirectional.FACING) == EnumFacing.UP) {
return blockState.withProperty(BlockDirectional.FACING, EnumFacing.DOWN);
}
}
//Dispenser, dropper
if (blockState.getBlock() instanceof BlockDispenser) {
if (blockState.getValue(BlockDispenser.FACING) == EnumFacing.DOWN) {
return blockState.withProperty(BlockDispenser.FACING, EnumFacing.UP);
} else if (blockState.getValue(BlockDispenser.FACING) == EnumFacing.UP) {
return blockState.withProperty(BlockDispenser.FACING, EnumFacing.DOWN);
}
}
return blockState;
}
//Called from EventHandler
public static void onBlockBroken(BlockEvent.BreakEvent event) {
if (event.getWorld().isRemote) return;
//find mirrorsettings for the player that broke the block
MirrorSettings m = BuildSettingsManager.getBuildSettings(event.getPlayer()).getMirrorSettings();
if (m == null) return;
if (!m.enabled || (!m.mirrorX && !m.mirrorY && !m.mirrorZ)) return;
//if within mirror distance, break mirror block
BlockPos oldBlockPos = event.getPos();
if (oldBlockPos.getX() + 0.5 < m.position.x - m.radius || oldBlockPos.getX() + 0.5 > m.position.x + m.radius ||
oldBlockPos.getY() + 0.5 < m.position.y - m.radius || oldBlockPos.getY() + 0.5 > m.position.y + m.radius ||
oldBlockPos.getZ() + 0.5 < m.position.z - m.radius || oldBlockPos.getZ() + 0.5 > m.position.z+ m.radius)
return;
if (m.mirrorX) {
breakMirrorX(event, m, oldBlockPos);
}
if (m.mirrorY) {
breakMirrorY(event, m, oldBlockPos);
}
if (m.mirrorZ) {
breakMirrorZ(event, m, oldBlockPos);
}
}
private static void breakMirrorX(BlockEvent.BreakEvent event, MirrorSettings m, BlockPos oldBlockPos) {
//find mirror position
double x = m.position.x + (m.position.x - oldBlockPos.getX() - 0.5);
BlockPos newBlockPos = new BlockPos(x, oldBlockPos.getY(), oldBlockPos.getZ());
//break block
if (event.getWorld().isBlockLoaded(newBlockPos, true)) {
event.getWorld().setBlockToAir(newBlockPos);
}
if (m.mirrorY) breakMirrorY(event, m, newBlockPos);
if (m.mirrorZ) breakMirrorZ(event, m, newBlockPos);
}
private static void breakMirrorY(BlockEvent.BreakEvent event, MirrorSettings m, BlockPos oldBlockPos) {
//find mirror position
double y = m.position.y + (m.position.y - oldBlockPos.getY() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), y, oldBlockPos.getZ());
//place block
if (event.getWorld().isBlockLoaded(newBlockPos, true)) {
event.getWorld().setBlockToAir(newBlockPos);
}
if (m.mirrorZ) breakMirrorZ(event, m, newBlockPos);
}
private static void breakMirrorZ(BlockEvent.BreakEvent event, MirrorSettings m, BlockPos oldBlockPos) {
//find mirror position
double z = m.position.z + (m.position.z - oldBlockPos.getZ() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), oldBlockPos.getY(), z);
//place block
if (event.getWorld().isBlockLoaded(newBlockPos, true)) {
event.getWorld().setBlockToAir(newBlockPos);
}
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
public static void onRender(RenderWorldLastEvent event) {
EntityPlayerSP player = Minecraft.getMinecraft().player;
BuildSettingsManager.BuildSettings buildSettings = BuildSettingsManager.getBuildSettings(player);
if (buildSettings == null) return;
MirrorSettings m = buildSettings.getMirrorSettings();
if (m == null || !m.enabled || (!m.mirrorX && !m.mirrorY && !m.mirrorZ)) return;
Vec3d playerPos = new Vec3d(player.posX, player.posY, player.posZ);
Vec3d pos = m.position.add(epsilon);
int radius = m.radius;
if (m.mirrorX) {
Vec3d posA = new Vec3d(pos.x, pos.y - radius, pos.z - radius);
Vec3d posB = new Vec3d(pos.x, pos.y + radius, pos.z + radius);
drawMirrorPlane(playerPos, posA, posB, colorX, m.drawLines, m.drawPlanes);
}
if (m.mirrorY) {
Vec3d posA = new Vec3d(pos.x - radius, pos.y, pos.z - radius);
Vec3d posB = new Vec3d(pos.x + radius, pos.y, pos.z + radius);
drawMirrorPlaneY(playerPos, posA, posB, colorY, m.drawLines, m.drawPlanes);
}
if (m.mirrorZ) {
Vec3d posA = new Vec3d(pos.x - radius, pos.y - radius, pos.z);
Vec3d posB = new Vec3d(pos.x + radius, pos.y + radius, pos.z);
drawMirrorPlane(playerPos, posA, posB, colorZ, m.drawLines, m.drawPlanes);
}
//Draw axis coordinated colors if two or more axes are enabled
//(If only one is enabled the lines are that planes color)
if (m.drawLines && ((m.mirrorX && m.mirrorY) || (m.mirrorX && m.mirrorZ) || (m.mirrorY && m.mirrorZ))) {
drawMirrorLines(playerPos, m);
}
}
@SideOnly(Side.CLIENT)
public static void drawMirrorPlane(Vec3d playerPos, Vec3d posA, Vec3d posB, Color c, boolean drawLines, boolean drawPlanes) {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glTranslated(-playerPos.x, -playerPos.y, -playerPos.z);
GL11.glColor4d(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha);
GL11.glLineWidth(2);
GL11.glDepthMask(false);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
if (drawPlanes) {
bufferBuilder.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_COLOR);
bufferBuilder.pos(posA.x, posA.y, posA.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
bufferBuilder.pos(posA.x, posB.y, posA.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
bufferBuilder.pos(posB.x, posA.y, posB.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
bufferBuilder.pos(posB.x, posB.y, posB.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
tessellator.draw();
}
if (drawLines) {
Vec3d middle = posA.add(posB).scale(0.5);
bufferBuilder.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);
bufferBuilder.pos(posA.x, middle.y, posA.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(posB.x, middle.y, posB.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(middle.x, posA.y, middle.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(middle.x, posB.y, middle.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
tessellator.draw();
}
GL11.glDepthMask(true);
GL11.glPopMatrix();
GL11.glPopAttrib();
}
@SideOnly(Side.CLIENT)
public static void drawMirrorPlaneY(Vec3d playerPos, Vec3d posA, Vec3d posB, Color c, boolean drawLines, boolean drawPlanes) {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glTranslated(-playerPos.x, -playerPos.y, -playerPos.z);
GL11.glColor4d(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
GL11.glLineWidth(2);
GL11.glDepthMask(false);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
if (drawPlanes) {
bufferBuilder.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_COLOR);
bufferBuilder.pos(posA.x, posA.y, posA.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
bufferBuilder.pos(posA.x, posA.y, posB.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
bufferBuilder.pos(posB.x, posA.y, posA.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
bufferBuilder.pos(posB.x, posA.y, posB.z).color(c.getRed(), c.getGreen(), c.getBlue(), planeAlpha).endVertex();
tessellator.draw();
}
if (drawLines) {
Vec3d middle = posA.add(posB).scale(0.5);
bufferBuilder.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);
bufferBuilder.pos(middle.x, middle.y, posA.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(middle.x, middle.y, posB.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(posA.x, middle.y, middle.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(posB.x, middle.y, middle.z).color(c.getRed(), c.getGreen(), c.getBlue(), lineAlpha).endVertex();
tessellator.draw();
}
GL11.glDepthMask(true);
GL11.glPopMatrix();
GL11.glPopAttrib();
}
@SideOnly(Side.CLIENT)
public static void drawMirrorLines(Vec3d playerPos, MirrorSettings m) {
Vec3d pos = m.position.add(epsilon);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glTranslated(-playerPos.x, -playerPos.y, -playerPos.z);
GL11.glColor4d(100, 100, 100, 255);
GL11.glLineWidth(2);
GL11.glDepthMask(false);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
bufferBuilder.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);
bufferBuilder.pos(pos.x - m.radius, pos.y, pos.z).color(colorZ.getRed(), colorZ.getGreen(), colorZ.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(pos.x + m.radius, pos.y, pos.z).color(colorZ.getRed(), colorZ.getGreen(), colorZ.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(pos.x, pos.y - m.radius, pos.z).color(colorY.getRed(), colorY.getGreen(), colorY.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(pos.x, pos.y + m.radius, pos.z).color(colorY.getRed(), colorY.getGreen(), colorY.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(pos.x, pos.y, pos.z - m.radius).color(colorX.getRed(), colorX.getGreen(), colorX.getBlue(), lineAlpha).endVertex();
bufferBuilder.pos(pos.x, pos.y, pos.z + m.radius).color(colorX.getRed(), colorX.getGreen(), colorX.getBlue(), lineAlpha).endVertex();
tessellator.draw();
GL11.glDepthMask(true);
GL11.glPopMatrix();
GL11.glPopAttrib();
}
}

View File

@@ -1,61 +0,0 @@
package nl.requios.effortlessbuilding;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.world.BlockEvent;
import nl.requios.effortlessbuilding.network.QuickReplaceMessage;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.UUID;
public class QuickReplace {
//Dilemma in getting blockstate from event to when message is received:
// 1) send via client. Then hacking makes it possible to place any block.
// 2) save serverside. Messages may not be received chronologically so data could get switched.
//Solution for now: save blockstate per player. Messages from 1 player will rarely come unchronologically
//and players will rarely switch between blocks that quickly.
private static Dictionary<UUID, IBlockState> blockStates = new Hashtable<>();
public static void onBlockPlaced(BlockEvent.PlaceEvent event) {
if (event.getWorld().isRemote) return;
//Only serverside
BuildSettingsManager.BuildSettings buildSettings = BuildSettingsManager.getBuildSettings(event.getPlayer());
if (!buildSettings.doQuickReplace()) return;
//TODO base on player facing instead, no more messages (or break block clientside)
blockStates.put(event.getPlayer().getUniqueID(), event.getPlacedBlock());
//RayTraceResult result = event.getWorld().rayTraceBlocks(event.getPlayer().getPositionEyes(1f), event.getPlayer().getLookVec());
EffortlessBuilding.packetHandler.sendTo(new QuickReplaceMessage(), (EntityPlayerMP) event.getPlayer());
event.setCanceled(true);
}
public static void onMessageReceived(EntityPlayer player, QuickReplaceMessage message) {
BuildSettingsManager.BuildSettings buildSettings = BuildSettingsManager.getBuildSettings(player);
if (!buildSettings.doQuickReplace()) return;
//TODO check for bedrock, end portal etc in survival
if (!message.isBlockHit() || message.getBlockPos() == null) return;
BlockPos placedAgainstBlockPos = message.getBlockPos();
//placedAgainstBlockPos = placedAgainstBlockPos.down();
IBlockState blockState = blockStates.get(player.getUniqueID());
player.world.setBlockState(placedAgainstBlockPos, blockState);
//Mirror and Array synergy
BlockSnapshot blockSnapshot = new BlockSnapshot(player.world, placedAgainstBlockPos, blockState);
BlockEvent.PlaceEvent placeEvent = new BlockEvent.PlaceEvent(blockSnapshot, blockState, player, EnumHand.MAIN_HAND);
Array.onBlockPlaced(placeEvent);
Mirror.onBlockPlaced(placeEvent);
}
}

View File

@@ -0,0 +1,59 @@
package nl.requios.effortlessbuilding;
import net.minecraftforge.common.ForgeConfigSpec;
import java.util.Arrays;
import java.util.List;
import static net.minecraftforge.common.ForgeConfigSpec.*;
public class ServerConfig {
private static final Builder builder = new Builder();
public static final ServerConfig.Validation validation = new ServerConfig.Validation(builder);
public static final ServerConfig.Memory memory = new ServerConfig.Memory(builder);
public static final ForgeConfigSpec spec = builder.build();
public static class Validation {
public final BooleanValue allowInSurvival;
public final BooleanValue useWhitelist;
public final ConfigValue<List<? extends String>> whitelist;
public final IntValue maxBlocksPlacedAtOnce;
public Validation(Builder builder) {
builder.push("Validation");
allowInSurvival = builder
.comment("Allow use of the mod for players that are in survival mode. Otherwise, only creative mode players can use the mod.")
.define("allowInSurvival", true);
useWhitelist = builder
.comment("Use a whitelist to determine which players can use the mod. If false, all players can use the mod.")
.define("useWhitelist", false);
whitelist = builder
.comment("List of player names that can use the mod.")
.defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true);
maxBlocksPlacedAtOnce = builder
.comment("Maximum number of blocks that can be placed at once. This is a last check. If you want the player to receive visual feedback instead of an error message, change values in the common config.")
.defineInRange("maxBlocksPlacedAtOnce", 10000, 1, 100000);
builder.pop();
}
}
public static class Memory {
public final IntValue undoStackSize;
public Memory(Builder builder) {
builder.push("Memory");
undoStackSize = builder
.comment("How many sets of blocks are remembered for the undo functionality, per player.")
.worldRestart()
.defineInRange("undoStackSize", 50, 10, 200);
builder.pop();
}
}
}

View File

@@ -0,0 +1,19 @@
package nl.requios.effortlessbuilding.buildmode;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public abstract class BaseBuildMode implements IBuildMode {
protected int clicks;
@Override
public void initialize() {
clicks = 0;
}
@Override
public boolean onClick(BlockSet blocks) {
clicks++;
return false;
}
}

View File

@@ -0,0 +1,16 @@
package nl.requios.effortlessbuilding.buildmode;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
public enum BuildModeCategoryEnum {
BASIC(new Color(0f, .5f, 1f, .8f)),
DIAGONAL(new Color(0.56f, 0.28f, 0.87f, .8f)),
CIRCULAR(new Color(0.29f, 0.76f, 0.3f, 1f)),
ROOF(new Color(0.83f, 0.87f, 0.23f, .8f));
public final Color color;
BuildModeCategoryEnum(Color color) {
this.color = color;
}
}

View File

@@ -0,0 +1,45 @@
package nl.requios.effortlessbuilding.buildmode;
import nl.requios.effortlessbuilding.AllIcons;
import nl.requios.effortlessbuilding.buildmode.buildmodes.*;
public enum BuildModeEnum {
DISABLED("normal", new Disabled(), BuildModeCategoryEnum.BASIC, AllIcons.I_DISABLE),
SINGLE("normal_plus", new Single(), BuildModeCategoryEnum.BASIC, AllIcons.I_SINGLE, ModeOptions.OptionEnum.BUILD_SPEED),
LINE("line", new Line(), BuildModeCategoryEnum.BASIC, AllIcons.I_LINE /*, OptionEnum.THICKNESS*/),
WALL("wall", new Wall(), BuildModeCategoryEnum.BASIC, AllIcons.I_WALL, ModeOptions.OptionEnum.FILL),
FLOOR("floor", new Floor(), BuildModeCategoryEnum.BASIC, AllIcons.I_FLOOR, ModeOptions.OptionEnum.FILL),
CUBE("cube", new Cube(), BuildModeCategoryEnum.BASIC, AllIcons.I_CUBE, ModeOptions.OptionEnum.CUBE_FILL),
DIAGONAL_LINE("diagonal_line", new DiagonalLine(), BuildModeCategoryEnum.DIAGONAL, AllIcons.I_DIAGONAL_LINE /*, OptionEnum.THICKNESS*/),
DIAGONAL_WALL("diagonal_wall", new DiagonalWall(), BuildModeCategoryEnum.DIAGONAL, AllIcons.I_DIAGONAL_WALL /*, OptionEnum.FILL*/),
SLOPE_FLOOR("slope_floor", new SlopeFloor(), BuildModeCategoryEnum.DIAGONAL, AllIcons.I_SLOPED_FLOOR, ModeOptions.OptionEnum.RAISED_EDGE),
CIRCLE("circle", new Circle(), BuildModeCategoryEnum.CIRCULAR, AllIcons.I_CIRCLE, ModeOptions.OptionEnum.CIRCLE_START, ModeOptions.OptionEnum.FILL),
CYLINDER("cylinder", new Cylinder(), BuildModeCategoryEnum.CIRCULAR, AllIcons.I_CYLINDER, ModeOptions.OptionEnum.CIRCLE_START, ModeOptions.OptionEnum.FILL),
SPHERE("sphere", new Sphere(), BuildModeCategoryEnum.CIRCULAR, AllIcons.I_SPHERE, ModeOptions.OptionEnum.CIRCLE_START, ModeOptions.OptionEnum.FILL);
// PYRAMID("pyramid", new Pyramid(), BuildModeCategoryEnum.ROOF),
// CONE("cone", new Cone(), BuildModeCategoryEnum.ROOF),
// DOME("dome", new Dome(), BuildModeCategoryEnum.ROOF);
private final String name;
public final IBuildMode instance;
public final BuildModeCategoryEnum category;
public final AllIcons icon;
public final ModeOptions.OptionEnum[] options;
BuildModeEnum(String name, IBuildMode instance, BuildModeCategoryEnum category, AllIcons icon, ModeOptions.OptionEnum... options) {
this.name = name;
this.instance = instance;
this.category = category;
this.icon = icon;
this.options = options;
}
public String getNameKey() {
return "effortlessbuilding.mode." + name;
}
public String getDescriptionKey() {
return "effortlessbuilding.modedescription." + name;
}
}

View File

@@ -0,0 +1,122 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.network.IsUsingBuildModePacket;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.utilities.BlockSet;
@OnlyIn(Dist.CLIENT)
public class BuildModes {
private BuildModeEnum buildMode = BuildModeEnum.DISABLED;
private BuildModeEnum previousBuildMode = BuildModeEnum.DISABLED;
private BuildModeEnum beforeDisabledBuildMode = BuildModeEnum.SINGLE;
public void findCoordinates(BlockSet blocks, Player player) {
buildMode.instance.findCoordinates(blocks);
}
public BuildModeEnum getBuildMode() {
return buildMode;
}
public void setBuildMode(BuildModeEnum buildMode) {
this.buildMode = buildMode;
PacketHandler.INSTANCE.sendToServer(new IsUsingBuildModePacket(this.buildMode != BuildModeEnum.DISABLED));
EffortlessBuilding.log(Minecraft.getInstance().player, I18n.get(buildMode.getNameKey()), true);
}
public void activatePreviousBuildMode() {
var temp = buildMode;
setBuildMode(previousBuildMode);
previousBuildMode = temp;
}
public void activateDisableBuildModeToggle(){
if (buildMode == BuildModeEnum.DISABLED) {
setBuildMode(beforeDisabledBuildMode);
} else {
beforeDisabledBuildMode = buildMode;
setBuildMode(BuildModeEnum.DISABLED);
}
}
public void onCancel() {
getBuildMode().instance.initialize();
}
//Find coordinates on a line bound by a plane
public static Vec3 findXBound(double x, Vec3 start, Vec3 look) {
//then y and z are
double y = (x - start.x) / look.x * look.y + start.y;
double z = (x - start.x) / look.x * look.z + start.z;
return new Vec3(x, y, z);
}
public static Vec3 findYBound(double y, Vec3 start, Vec3 look) {
//then x and z are
double x = (y - start.y) / look.y * look.x + start.x;
double z = (y - start.y) / look.y * look.z + start.z;
return new Vec3(x, y, z);
}
public static Vec3 findZBound(double z, Vec3 start, Vec3 look) {
//then x and y are
double x = (z - start.z) / look.z * look.x + start.x;
double y = (z - start.z) / look.z * look.y + start.y;
return new Vec3(x, y, z);
}
//Use this instead of player.getLookVec() in any buildmodes code
public static Vec3 getPlayerLookVec(Player player) {
Vec3 lookVec = player.getLookAngle();
double x = lookVec.x;
double y = lookVec.y;
double z = lookVec.z;
//Further calculations (findXBound etc) don't like any component being 0 or 1 (e.g. dividing by 0)
//isCriteriaValid below will take up to 2 minutes to raytrace blocks towards infinity if that is the case
//So make sure they are close to but never exactly 0 or 1
if (Math.abs(x) < 0.0001) x = 0.0001;
if (Math.abs(x - 1.0) < 0.0001) x = 0.9999;
if (Math.abs(x + 1.0) < 0.0001) x = -0.9999;
if (Math.abs(y) < 0.0001) y = 0.0001;
if (Math.abs(y - 1.0) < 0.0001) y = 0.9999;
if (Math.abs(y + 1.0) < 0.0001) y = -0.9999;
if (Math.abs(z) < 0.0001) z = 0.0001;
if (Math.abs(z - 1.0) < 0.0001) z = 0.9999;
if (Math.abs(z + 1.0) < 0.0001) z = -0.9999;
return new Vec3(x, y, z);
}
public static boolean isCriteriaValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace, Vec3 lineBound, Vec3 planeBound, double distToPlayerSq) {
boolean intersects = false;
if (!skipRaytrace) {
//collision within a 1 block radius to selected is fine
ClipContext rayTraceContext = new ClipContext(start, lineBound, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player);
HitResult rayTraceResult = player.level.clip(rayTraceContext);
intersects = rayTraceResult != null && rayTraceResult.getType() == HitResult.Type.BLOCK &&
planeBound.subtract(rayTraceResult.getLocation()).lengthSqr() > 4;
}
return planeBound.subtract(start).dot(look) > 0 &&
distToPlayerSq > 2 && distToPlayerSq < reach * reach &&
!intersects;
}
}

View File

@@ -0,0 +1,14 @@
package nl.requios.effortlessbuilding.buildmode;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public interface IBuildMode {
//Reset values here, start over
void initialize();
//Returns if we should place blocks now
boolean onClick(BlockSet blocks);
void findCoordinates(BlockSet blocks);
}

View File

@@ -0,0 +1,187 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.AllIcons;
import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.network.PerformRedoPacket;
import nl.requios.effortlessbuilding.network.PerformUndoPacket;
import nl.requios.effortlessbuilding.systems.BuildSettings;
@OnlyIn(Dist.CLIENT)
public class ModeOptions {
private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED;
private static ActionEnum fill = ActionEnum.FULL;
private static ActionEnum cubeFill = ActionEnum.CUBE_FULL;
private static ActionEnum raisedEdge = ActionEnum.SHORT_EDGE;
private static ActionEnum lineThickness = ActionEnum.THICKNESS_1;
private static ActionEnum circleStart = ActionEnum.CIRCLE_START_CORNER;
public static ActionEnum getOptionSetting(OptionEnum option) {
switch (option) {
case BUILD_SPEED:
return getBuildSpeed();
case FILL:
return getFill();
case CUBE_FILL:
return getCubeFill();
case RAISED_EDGE:
return getRaisedEdge();
case LINE_THICKNESS:
return getLineThickness();
case CIRCLE_START:
return getCircleStart();
default:
return null;
}
}
public static ActionEnum getBuildSpeed() {
return buildSpeed;
}
public static ActionEnum getFill() {
return fill;
}
public static ActionEnum getCubeFill() {
return cubeFill;
}
public static ActionEnum getRaisedEdge() {
return raisedEdge;
}
public static ActionEnum getLineThickness() {
return lineThickness;
}
public static ActionEnum getCircleStart() {
return circleStart;
}
public static void performAction(Player player, ActionEnum action) {
if (action == null) return;
switch (action) {
case UNDO -> PacketHandler.INSTANCE.sendToServer(new PerformUndoPacket());
case REDO -> PacketHandler.INSTANCE.sendToServer(new PerformRedoPacket());
case OPEN_MODIFIER_SETTINGS -> ClientEvents.openModifierSettings();
case OPEN_PLAYER_SETTINGS -> ClientEvents.openPlayerSettings();
case PREVIOUS_BUILD_MODE -> EffortlessBuildingClient.BUILD_MODES.activatePreviousBuildMode();
case DISABLE_BUILD_MODE_TOGGLE -> EffortlessBuildingClient.BUILD_MODES.activateDisableBuildModeToggle();
case REPLACE_ONLY_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_AIR);
case REPLACE_BLOCKS_AND_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.BLOCKS_AND_AIR);
case REPLACE_ONLY_BLOCKS -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_BLOCKS);
case REPLACE_FILTERED_BY_OFFHAND -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.FILTERED_BY_OFFHAND);
case TOGGLE_PROTECT_TILE_ENTITIES -> EffortlessBuildingClient.BUILD_SETTINGS.toggleProtectTileEntities();
case NORMAL_SPEED -> buildSpeed = ActionEnum.NORMAL_SPEED;
case FAST_SPEED -> buildSpeed = ActionEnum.FAST_SPEED;
case FULL -> fill = ActionEnum.FULL;
case HOLLOW -> fill = ActionEnum.HOLLOW;
case CUBE_FULL -> cubeFill = ActionEnum.CUBE_FULL;
case CUBE_HOLLOW -> cubeFill = ActionEnum.CUBE_HOLLOW;
case CUBE_SKELETON -> cubeFill = ActionEnum.CUBE_SKELETON;
case SHORT_EDGE -> raisedEdge = ActionEnum.SHORT_EDGE;
case LONG_EDGE -> raisedEdge = ActionEnum.LONG_EDGE;
case THICKNESS_1 -> lineThickness = ActionEnum.THICKNESS_1;
case THICKNESS_3 -> lineThickness = ActionEnum.THICKNESS_3;
case THICKNESS_5 -> lineThickness = ActionEnum.THICKNESS_5;
case CIRCLE_START_CENTER -> circleStart = ActionEnum.CIRCLE_START_CENTER;
case CIRCLE_START_CORNER -> circleStart = ActionEnum.CIRCLE_START_CORNER;
}
if (player.level.isClientSide &&
action != ActionEnum.OPEN_MODIFIER_SETTINGS &&
action != ActionEnum.OPEN_PLAYER_SETTINGS &&
action != ActionEnum.PREVIOUS_BUILD_MODE &&
action != ActionEnum.DISABLE_BUILD_MODE_TOGGLE) {
EffortlessBuilding.logTranslate(player, "", action.getNameKey(), "", true);
}
}
public enum ActionEnum {
UNDO("undo", AllIcons.I_UNDO),
REDO("redo", AllIcons.I_REDO),
OPEN_MODIFIER_SETTINGS("open_modifier_settings", AllIcons.I_SETTINGS),
OPEN_PLAYER_SETTINGS("open_player_settings", AllIcons.I_SETTINGS),
PREVIOUS_BUILD_MODE("previous_build_mode", AllIcons.I_SINGLE),
DISABLE_BUILD_MODE_TOGGLE("disable_build_mode_toggle", AllIcons.I_DISABLE),
REPLACE_ONLY_AIR("replace_only_air", AllIcons.I_REPLACE_AIR),
REPLACE_BLOCKS_AND_AIR("replace_blocks_and_air", AllIcons.I_REPLACE_BLOCKS_AND_AIR),
REPLACE_ONLY_BLOCKS("replace_only_blocks", AllIcons.I_REPLACE_BLOCKS),
REPLACE_FILTERED_BY_OFFHAND("replace_filtered_by_offhand", AllIcons.I_REPLACE_OFFHAND_FILTERED),
TOGGLE_PROTECT_TILE_ENTITIES("toggle_protect_tile_entities", AllIcons.I_PROTECT_TILE_ENTITIES),
NORMAL_SPEED("normal_speed", AllIcons.I_NORMAL_SPEED),
FAST_SPEED("fast_speed", AllIcons.I_FAST_SPEED),
FULL("full", AllIcons.I_FILLED),
HOLLOW("hollow", AllIcons.I_HOLLOW),
CUBE_FULL("full", AllIcons.I_CUBE_FILLED),
CUBE_HOLLOW("hollow", AllIcons.I_CUBE_HOLLOW),
CUBE_SKELETON("skeleton", AllIcons.I_CUBE_SKELETON),
SHORT_EDGE("short_edge", AllIcons.I_SHORT_EDGE),
LONG_EDGE("long_edge", AllIcons.I_LONG_EDGE),
THICKNESS_1("thickness_1", AllIcons.I_THICKNESS_1),
THICKNESS_3("thickness_3", AllIcons.I_THICKNESS_3),
THICKNESS_5("thickness_5", AllIcons.I_THICKNESS_5),
CIRCLE_START_CORNER("start_corner", AllIcons.I_CIRCLE_START_CORNER),
CIRCLE_START_CENTER("start_center", AllIcons.I_CIRCLE_START_CENTER);
public String name;
public AllIcons icon;
ActionEnum(String name, AllIcons icon) {
this.name = name;
this.icon = icon;
}
public String getName() {
return name;
}
public String getNameKey() {
return "effortlessbuilding.action." + name;
}
public String getDescriptionKey() {
return "effortlessbuilding.action." + name + ".description";
}
}
public enum OptionEnum {
BUILD_SPEED("effortlessbuilding.action.build_speed", ActionEnum.NORMAL_SPEED, ActionEnum.FAST_SPEED),
FILL("effortlessbuilding.action.filling", ActionEnum.FULL, ActionEnum.HOLLOW),
CUBE_FILL("effortlessbuilding.action.filling", ActionEnum.CUBE_FULL, ActionEnum.CUBE_HOLLOW, ActionEnum.CUBE_SKELETON),
RAISED_EDGE("effortlessbuilding.action.raised_edge", ActionEnum.SHORT_EDGE, ActionEnum.LONG_EDGE),
LINE_THICKNESS("effortlessbuilding.action.thickness", ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5),
CIRCLE_START("effortlessbuilding.action.circle_start", ActionEnum.CIRCLE_START_CORNER, ActionEnum.CIRCLE_START_CENTER);
public String name;
public ActionEnum[] actions;
OptionEnum(String name, ActionEnum... actions) {
this.name = name;
this.actions = actions;
}
}
}

View File

@@ -0,0 +1,211 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.List;
public abstract class ThreeClicksBuildMode extends BaseBuildMode {
protected BlockEntry firstBlockEntry;
protected BlockEntry secondBlockEntry;
//Finds height after floor has been chosen in buildmodes with 3 clicks
@Override
public void initialize() {
super.initialize();
firstBlockEntry = null;
secondBlockEntry = null;
}
@Override
public boolean onClick(BlockSet blocks) {
super.onClick(blocks);
if (clicks == 1) {
//First click, remember starting position
firstBlockEntry = EffortlessBuildingClient.BUILDER_CHAIN.getStartPos();
//If clicking in air, reset and try again
if (firstBlockEntry == null) clicks = 0;
} else if (clicks == 2) {
//Second click, find second position
if (blocks.size() == 0) {
clicks = 0;
return false;
}
var player = Minecraft.getInstance().player;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
secondBlockEntry = new BlockEntry(secondPos);
} else {
//Third click, place blocks
clicks = 0;
return true;
}
return false;
}
@Override
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
if (clicks == 1) {
var player = Minecraft.getInstance().player;
var firstPos = firstBlockEntry.blockPos;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
if (secondPos == null) return;
//Limit amount of blocks we can place per row
int axisLimit = EffortlessBuildingClient.POWER_LEVEL.getMaxBlocksPerAxis(player);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
blocks.clear();
for (BlockPos pos : getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2)) {
if (blocks.containsKey(pos)) continue;
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = secondPos;
} else {
var player = Minecraft.getInstance().player;
BlockPos firstPos = firstBlockEntry.blockPos;
BlockPos secondPos = secondBlockEntry.blockPos;
BlockPos thirdPos = findThirdPos(player, firstPos, secondPos, true);
if (thirdPos == null) return;
//Limit amount of blocks you can place per row
int axisLimit = EffortlessBuildingClient.POWER_LEVEL.getMaxBlocksPerAxis(player);
int x1 = firstPos.getX(), x2 = secondPos.getX(), x3 = thirdPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY(), y3 = thirdPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ(), z3 = thirdPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
if (x3 - x1 >= axisLimit) x3 = x1 + axisLimit - 1;
if (x1 - x3 >= axisLimit) x3 = x1 - axisLimit + 1;
if (y3 - y1 >= axisLimit) y3 = y1 + axisLimit - 1;
if (y1 - y3 >= axisLimit) y3 = y1 - axisLimit + 1;
if (z3 - z1 >= axisLimit) z3 = z1 + axisLimit - 1;
if (z1 - z3 >= axisLimit) z3 = z1 - axisLimit + 1;
blocks.clear();
for (BlockPos pos : getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3)) {
if (blocks.containsKey(pos)) continue;
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = thirdPos;
}
}
public static BlockPos findHeight(Player player, BlockPos secondPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
List<HeightCriteria> criteriaList = new ArrayList<>(3);
//X
Vec3 xBound = BuildModes.findXBound(secondPos.getX(), start, look);
criteriaList.add(new HeightCriteria(xBound, secondPos, start));
//Z
Vec3 zBound = BuildModes.findZBound(secondPos.getZ(), start, look);
criteriaList.add(new HeightCriteria(zBound, secondPos, start));
//Remove invalid criteria
int reach = EffortlessBuildingClient.POWER_LEVEL.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
if (criteriaList.isEmpty()) return null;
//If only 1 is valid, choose that one
HeightCriteria selected = criteriaList.get(0);
//If multiple are valid, choose based on criteria
if (criteriaList.size() > 1) {
//Select the one that is closest (from wall position to its line counterpart)
for (int i = 1; i < criteriaList.size(); i++) {
HeightCriteria criteria = criteriaList.get(i);
if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) {
//Both very close to line, choose closest to player
if (criteria.distToPlayerSq < selected.distToPlayerSq)
selected = criteria;
} else {
//Pick closest to line
if (criteria.distToLineSq < selected.distToLineSq)
selected = criteria;
}
}
}
return BlockPos.containing(selected.lineBound);
}
// protected abstract BlockEntry findSecondPos(List<BlockEntry> blocks);
//Finds the place of the second block pos
// @Deprecated
protected abstract BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace);
//Finds the place of the third block pos
protected abstract BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace);
//After first and second pos are known, we want to visualize the blocks in a way (like floor for cube)
protected abstract List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2);
//After first, second and third pos are known, we want all the blocks
protected abstract List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3);
static class HeightCriteria {
Vec3 planeBound;
Vec3 lineBound;
double distToLineSq;
double distToPlayerSq;
HeightCriteria(Vec3 planeBound, BlockPos secondPos, Vec3 start) {
this.planeBound = planeBound;
this.lineBound = toLongestLine(this.planeBound, secondPos);
this.distToLineSq = this.lineBound.subtract(this.planeBound).lengthSqr();
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
}
//Make it from a plane into a line, on y axis only
private Vec3 toLongestLine(Vec3 boundVec, BlockPos secondPos) {
BlockPos bound = BlockPos.containing(boundVec);
return new Vec3(secondPos.getX(), bound.getY(), secondPos.getZ());
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq);
}
}
}

View File

@@ -0,0 +1,79 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.List;
public abstract class TwoClicksBuildMode extends BaseBuildMode {
protected BlockEntry firstBlockEntry;
@Override
public void initialize() {
super.initialize();
firstBlockEntry = null;
}
@Override
public boolean onClick(BlockSet blocks) {
super.onClick(blocks);
if (clicks == 1) {
//First click, remember starting position
firstBlockEntry = EffortlessBuildingClient.BUILDER_CHAIN.getStartPos();
//If clicking in air, reset and try again
if (firstBlockEntry == null) clicks = 0;
} else {
//Second click, place blocks
clicks = 0;
return true;
}
return false;
}
@Override
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
var player = Minecraft.getInstance().player;
var firstPos = firstBlockEntry.blockPos;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
if (secondPos == null) return;
//Limit amount of blocks we can place per row
int axisLimit = EffortlessBuildingClient.POWER_LEVEL.getMaxBlocksPerAxis(player);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
blocks.clear();
for (BlockPos pos : getAllBlocks(player, x1, y1, z1, x2, y2, z2)) {
if (blocks.containsKey(pos)) continue;
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = secondPos;
}
//Finds the place of the second block pos based on criteria (floor must be on same height as first click, wall on same plane etc)
protected abstract BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace);
//After first and second pos are known, we want all the blocks
protected abstract List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2);
}

View File

@@ -0,0 +1,89 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Circle extends TwoClicksBuildMode {
public static List<BlockPos> getCircleBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
float centerX = x1;
float centerZ = z1;
//Adjust for CIRCLE_START
if (ModeOptions.getCircleStart() == ModeOptions.ActionEnum.CIRCLE_START_CORNER) {
centerX = x1 + (x2 - x1) / 2f;
centerZ = z1 + (z2 - z1) / 2f;
} else {
x1 = (int) (centerX - (x2 - centerX));
z1 = (int) (centerZ - (z2 - centerZ));
}
float radiusX = Mth.abs(x2 - centerX);
float radiusZ = Mth.abs(z2 - centerZ);
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
addCircleBlocks(list, x1, y1, z1, x2, y2, z2, centerX, centerZ, radiusX, radiusZ);
else
addHollowCircleBlocks(list, x1, y1, z1, x2, y2, z2, centerX, centerZ, radiusX, radiusZ);
return list;
}
public static void addCircleBlocks(List<BlockPos> list, int x1, int y1, int z1, int x2, int y2, int z2, float centerX, float centerZ, float radiusX, float radiusZ) {
for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) {
for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) {
float distance = distance(l, n, centerX, centerZ);
float radius = calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, l, n);
if (distance < radius + 0.4f)
list.add(new BlockPos(l, y1, n));
}
}
}
public static void addHollowCircleBlocks(List<BlockPos> list, int x1, int y1, int z1, int x2, int y2, int z2, float centerX, float centerZ, float radiusX, float radiusZ) {
for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) {
for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) {
float distance = distance(l, n, centerX, centerZ);
float radius = calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, l, n);
if (distance < radius + 0.4f && distance > radius - 0.6f)
list.add(new BlockPos(l, y1, n));
}
}
}
private static float distance(float x1, float z1, float x2, float z2) {
return Mth.sqrt((x2 - x1) * (x2 - x1) + (z2 - z1) * (z2 - z1));
}
public static float calculateEllipseRadius(float centerX, float centerZ, float radiusX, float radiusZ, int x, int z) {
//https://math.stackexchange.com/questions/432902/how-to-get-the-radius-of-an-ellipse-at-a-specific-angle-by-knowing-its-semi-majo
float theta = (float) Mth.atan2(z - centerZ, x - centerX);
float part1 = radiusX * radiusX * Mth.sin(theta) * Mth.sin(theta);
float part2 = radiusZ * radiusZ * Mth.cos(theta) * Mth.cos(theta);
return radiusX * radiusZ / Mth.sqrt(part1 + part2);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
}

View File

@@ -0,0 +1,31 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.List;
public class Cone extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//TODO
return SlopeFloor.getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -0,0 +1,101 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Cube extends ThreeClicksBuildMode {
public static List<BlockPos> getFloorBlocksUsingCubeFill(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (ModeOptions.getCubeFill() == ModeOptions.ActionEnum.CUBE_SKELETON)
Floor.addHollowFloorBlocks(list, x1, x2, y1, z1, z2);
else
Floor.addFloorBlocks(list, x1, x2, y1, z1, z2);
return list;
}
public static List<BlockPos> getCubeBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
switch (ModeOptions.getCubeFill()) {
case CUBE_FULL:
addCubeBlocks(list, x1, x2, y1, y2, z1, z2);
break;
case CUBE_HOLLOW:
addHollowCubeBlocks(list, x1, x2, y1, y2, z1, z2);
break;
case CUBE_SKELETON:
addSkeletonCubeBlocks(list, x1, x2, y1, y2, z1, z2);
break;
}
return list;
}
public static void addCubeBlocks(List<BlockPos> list, int x1, int x2, int y1, int y2, int z1, int z2) {
for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) {
for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) {
for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) {
list.add(new BlockPos(l, m, n));
}
}
}
}
public static void addHollowCubeBlocks(List<BlockPos> list, int x1, int x2, int y1, int y2, int z1, int z2) {
Wall.addXWallBlocks(list, x1, y1, y2, z1, z2);
Wall.addXWallBlocks(list, x2, y1, y2, z1, z2);
Wall.addZWallBlocks(list, x1, x2, y1, y2, z1);
Wall.addZWallBlocks(list, x1, x2, y1, y2, z2);
Floor.addFloorBlocks(list, x1, x2, y1, z1, z2);
Floor.addFloorBlocks(list, x1, x2, y2, z1, z2);
}
public static void addSkeletonCubeBlocks(List<BlockPos> list, int x1, int x2, int y1, int y2, int z1, int z2) {
Line.addXLineBlocks(list, x1, x2, y1, z1);
Line.addXLineBlocks(list, x1, x2, y1, z2);
Line.addXLineBlocks(list, x1, x2, y2, z1);
Line.addXLineBlocks(list, x1, x2, y2, z2);
Line.addYLineBlocks(list, y1, y2, x1, z1);
Line.addYLineBlocks(list, y1, y2, x1, z2);
Line.addYLineBlocks(list, y1, y2, x2, z1);
Line.addYLineBlocks(list, y1, y2, x2, z2);
Line.addZLineBlocks(list, z1, z2, x1, y1);
Line.addZLineBlocks(list, z1, z2, x1, y2);
Line.addZLineBlocks(list, z1, z2, x2, y1);
Line.addZLineBlocks(list, z1, z2, x2, y2);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getFloorBlocksUsingCubeFill(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getCubeBlocks(player, x1, y1, z1, x3, y3, z3);
}
}

View File

@@ -0,0 +1,50 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Cylinder extends ThreeClicksBuildMode {
public static List<BlockPos> getCylinderBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
//Get circle blocks (using CIRCLE_START and FILL options built-in)
List<BlockPos> circleBlocks = Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
int lowest = Math.min(y1, y3);
int highest = Math.max(y1, y3);
//Copy circle on y axis
for (int y = lowest; y <= highest; y++) {
for (BlockPos blockPos : circleBlocks) {
list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ()));
}
}
return list;
}
@Override
public BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
public BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
public List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
public List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getCylinderBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -0,0 +1,53 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class DiagonalLine extends ThreeClicksBuildMode {
//Add diagonal line from first to second
public static List<BlockPos> getDiagonalLineBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, float sampleMultiplier) {
List<BlockPos> list = new ArrayList<>();
Vec3 first = new Vec3(x1, y1, z1).add(0.5, 0.5, 0.5);
Vec3 second = new Vec3(x2, y2, z2).add(0.5, 0.5, 0.5);
int iterations = (int) Math.ceil(first.distanceTo(second) * sampleMultiplier);
for (double t = 0; t <= 1.0; t += 1.0 / iterations) {
Vec3 lerp = first.add(second.subtract(first).scale(t));
BlockPos candidate = BlockPos.containing(lerp);
//Only add if not equal to the last in the list
if (list.isEmpty() || !list.get(list.size() - 1).equals(candidate))
list.add(candidate);
}
return list;
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
//Add diagonal line from first to second
return getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 10);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//Add diagonal line from first to third
return getDiagonalLineBlocks(player, x1, y1, z1, x3, y3, z3, 10);
}
}

View File

@@ -0,0 +1,51 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class DiagonalWall extends ThreeClicksBuildMode {
//Add diagonal wall from first to second
public static List<BlockPos> getDiagonalWallBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
//Get diagonal line blocks
List<BlockPos> diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 1);
int lowest = Math.min(y1, y3);
int highest = Math.max(y1, y3);
//Copy diagonal line on y axis
for (int y = lowest; y <= highest; y++) {
for (BlockPos blockPos : diagonalLineBlocks) {
list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ()));
}
}
return list;
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 1);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getDiagonalWallBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -0,0 +1,22 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Disabled implements IBuildMode {
@Override
public void initialize() {
}
@Override
public boolean onClick(BlockSet blocks) {
return true;
}
@Override
public void findCoordinates(BlockSet blocks) {
//Do nothing
}
}

View File

@@ -0,0 +1,31 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.List;
public class Dome extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//TODO
return SlopeFloor.getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -0,0 +1,94 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Floor extends TwoClicksBuildMode {
public static BlockPos findFloor(Player player, BlockPos firstPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
List<Criteria> criteriaList = new ArrayList<>(3);
//Y
Vec3 yBound = BuildModes.findYBound(firstPos.getY(), start, look);
criteriaList.add(new Criteria(yBound, start));
//Remove invalid criteria
int reach = EffortlessBuildingClient.POWER_LEVEL.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
if (criteriaList.isEmpty()) return null;
//Then only 1 can be valid, return that one
Criteria selected = criteriaList.get(0);
return BlockPos.containing(selected.planeBound);
}
public static List<BlockPos> getFloorBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
addFloorBlocks(list, x1, x2, y1, z1, z2);
else
addHollowFloorBlocks(list, x1, x2, y1, z1, z2);
return list;
}
public static void addFloorBlocks(List<BlockPos> list, int x1, int x2, int y, int z1, int z2) {
for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) {
for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) {
list.add(new BlockPos(l, y, n));
}
}
}
public static void addHollowFloorBlocks(List<BlockPos> list, int x1, int x2, int y, int z1, int z2) {
Line.addXLineBlocks(list, x1, x2, y, z1);
Line.addXLineBlocks(list, x1, x2, y, z2);
Line.addZLineBlocks(list, z1, z2, x1, y);
Line.addZLineBlocks(list, z1, z2, x2, y);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return findFloor(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
static class Criteria {
Vec3 planeBound;
double distToPlayerSq;
Criteria(Vec3 planeBound, Vec3 start) {
this.planeBound = planeBound;
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq);
}
}
}

View File

@@ -0,0 +1,147 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Line extends TwoClicksBuildMode {
public static BlockPos findLine(Player player, BlockPos firstPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
List<Criteria> criteriaList = new ArrayList<>(3);
//X
Vec3 xBound = BuildModes.findXBound(firstPos.getX(), start, look);
criteriaList.add(new Criteria(xBound, firstPos, start));
//Y
Vec3 yBound = BuildModes.findYBound(firstPos.getY(), start, look);
criteriaList.add(new Criteria(yBound, firstPos, start));
//Z
Vec3 zBound = BuildModes.findZBound(firstPos.getZ(), start, look);
criteriaList.add(new Criteria(zBound, firstPos, start));
//Remove invalid criteria
int reach = EffortlessBuildingClient.POWER_LEVEL.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
if (criteriaList.isEmpty()) return null;
//If only 1 is valid, choose that one
Criteria selected = criteriaList.get(0);
//If multiple are valid, choose based on criteria
if (criteriaList.size() > 1) {
//Select the one that is closest (from wall position to its line counterpart)
for (int i = 1; i < criteriaList.size(); i++) {
Criteria criteria = criteriaList.get(i);
if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) {
//Both very close to line, choose closest to player
if (criteria.distToPlayerSq < selected.distToPlayerSq)
selected = criteria;
} else {
//Pick closest to line
if (criteria.distToLineSq < selected.distToLineSq)
selected = criteria;
}
}
}
return BlockPos.containing(selected.lineBound);
}
public static List<BlockPos> getLineBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (x1 != x2) {
addXLineBlocks(list, x1, x2, y1, z1);
} else if (y1 != y2) {
addYLineBlocks(list, y1, y2, x1, z1);
} else {
addZLineBlocks(list, z1, z2, x1, y1);
}
return list;
}
public static void addXLineBlocks(List<BlockPos> list, int x1, int x2, int y, int z) {
for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) {
list.add(new BlockPos(x, y, z));
}
}
public static void addYLineBlocks(List<BlockPos> list, int y1, int y2, int x, int z) {
for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) {
list.add(new BlockPos(x, y, z));
}
}
public static void addZLineBlocks(List<BlockPos> list, int z1, int z2, int x, int y) {
for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) {
list.add(BlockPos.containing(x, y, z));
}
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return findLine(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getLineBlocks(player, x1, y1, z1, x2, y2, z2);
}
static class Criteria {
Vec3 planeBound;
Vec3 lineBound;
double distToLineSq;
double distToPlayerSq;
Criteria(Vec3 planeBound, BlockPos firstPos, Vec3 start) {
this.planeBound = planeBound;
this.lineBound = toLongestLine(this.planeBound, firstPos);
this.distToLineSq = this.lineBound.subtract(this.planeBound).lengthSqr();
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
}
//Make it from a plane into a line
//Select the axis that is longest
private Vec3 toLongestLine(Vec3 boundVec, BlockPos firstPos) {
BlockPos bound = BlockPos.containing(boundVec);
BlockPos firstToSecond = bound.subtract(firstPos);
firstToSecond = new BlockPos(Math.abs(firstToSecond.getX()), Math.abs(firstToSecond.getY()), Math.abs(firstToSecond.getZ()));
int longest = Math.max(firstToSecond.getX(), Math.max(firstToSecond.getY(), firstToSecond.getZ()));
if (longest == firstToSecond.getX()) {
return new Vec3(bound.getX(), firstPos.getY(), firstPos.getZ());
}
if (longest == firstToSecond.getY()) {
return new Vec3(firstPos.getX(), bound.getY(), firstPos.getZ());
}
if (longest == firstToSecond.getZ()) {
return new Vec3(firstPos.getX(), firstPos.getY(), bound.getZ());
}
return null;
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq);
}
}
}

View File

@@ -0,0 +1,31 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.List;
public class Pyramid extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//TODO
return SlopeFloor.getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -0,0 +1,22 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Single implements IBuildMode {
@Override
public void initialize() {
}
@Override
public boolean onClick(BlockSet blocks) {
return true;
}
@Override
public void findCoordinates(BlockSet blocks) {
//Do nothing
}
}

View File

@@ -0,0 +1,95 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class SlopeFloor extends ThreeClicksBuildMode {
//Add slope floor from first to second
public static List<BlockPos> getSlopeFloorBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
int axisLimit = EffortlessBuildingClient.POWER_LEVEL.getMaxBlocksPerAxis(player);
//Determine whether to use x or z axis to slope up
boolean onXAxis = true;
int xLength = Math.abs(x2 - x1);
int zLength = Math.abs(z2 - z1);
if (ModeOptions.getRaisedEdge() == ModeOptions.ActionEnum.SHORT_EDGE) {
//Slope along short edge
if (zLength > xLength) onXAxis = false;
} else {
//Slope along long edge
if (zLength <= xLength) onXAxis = false;
}
if (onXAxis) {
//Along X goes up
//Get diagonal line blocks
List<BlockPos> diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y3, z1, 1f);
//Limit amount of blocks we can place
int lowest = Math.min(z1, z2);
int highest = Math.max(z1, z2);
if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1;
//Copy diagonal line on x axis
for (int z = lowest; z <= highest; z++) {
for (BlockPos blockPos : diagonalLineBlocks) {
list.add(new BlockPos(blockPos.getX(), blockPos.getY(), z));
}
}
} else {
//Along Z goes up
//Get diagonal line blocks
List<BlockPos> diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x1, y3, z2, 1f);
//Limit amount of blocks we can place
int lowest = Math.min(x1, x2);
int highest = Math.max(x1, x2);
if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1;
//Copy diagonal line on x axis
for (int x = lowest; x <= highest; x++) {
for (BlockPos blockPos : diagonalLineBlocks) {
list.add(new BlockPos(x, blockPos.getY(), blockPos.getZ()));
}
}
}
return list;
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -0,0 +1,110 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Sphere extends ThreeClicksBuildMode {
public static List<BlockPos> getSphereBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
float centerX = x1;
float centerY = y1;
float centerZ = z1;
//Adjust for CIRCLE_START
if (ModeOptions.getCircleStart() == ModeOptions.ActionEnum.CIRCLE_START_CORNER) {
centerX = x1 + (x2 - x1) / 2f;
centerY = y1 + (y3 - y1) / 2f;
centerZ = z1 + (z2 - z1) / 2f;
} else {
x1 = (int) (centerX - (x2 - centerX));
y1 = (int) (centerY - (y3 - centerY));
z1 = (int) (centerZ - (z2 - centerZ));
}
float radiusX = Mth.abs(x2 - centerX);
float radiusY = Mth.abs(y3 - centerY);
float radiusZ = Mth.abs(z2 - centerZ);
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
addSphereBlocks(list, x1, y1, z1, x3, y3, z3, centerX, centerY, centerZ, radiusX, radiusY, radiusZ);
else
addHollowSphereBlocks(list, x1, y1, z1, x3, y3, z3, centerX, centerY, centerZ, radiusX, radiusY, radiusZ);
return list;
}
public static void addSphereBlocks(List<BlockPos> list, int x1, int y1, int z1, int x2, int y2, int z2,
float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ) {
for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) {
for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) {
for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) {
float distance = distance(l, m, n, centerX, centerY, centerZ);
float radius = calculateSpheroidRadius(centerX, centerY, centerZ, radiusX, radiusY, radiusZ, l, m, n);
if (distance < radius + 0.4f)
list.add(new BlockPos(l, m, n));
}
}
}
}
public static void addHollowSphereBlocks(List<BlockPos> list, int x1, int y1, int z1, int x2, int y2, int z2,
float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ) {
for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) {
for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) {
for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) {
float distance = distance(l, m, n, centerX, centerY, centerZ);
float radius = calculateSpheroidRadius(centerX, centerY, centerZ, radiusX, radiusY, radiusZ, l, m, n);
if (distance < radius + 0.4f && distance > radius - 0.6f)
list.add(new BlockPos(l, m, n));
}
}
}
}
private static float distance(float x1, float y1, float z1, float x2, float y2, float z2) {
return Mth.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1));
}
public static float calculateSpheroidRadius(float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ, int x, int y, int z) {
//Twice ellipse radius
float radiusXZ = Circle.calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, x, z);
//TODO project x to plane
return Circle.calculateEllipseRadius(centerX, centerY, radiusXZ, radiusY, x, y);
}
@Override
public BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
public BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
public List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
public List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getSphereBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -0,0 +1,135 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Wall extends TwoClicksBuildMode {
public static BlockPos findWall(Player player, BlockPos firstPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
List<Criteria> criteriaList = new ArrayList<>(3);
//X
Vec3 xBound = BuildModes.findXBound(firstPos.getX(), start, look);
criteriaList.add(new Criteria(xBound, firstPos, start, look));
//Z
Vec3 zBound = BuildModes.findZBound(firstPos.getZ(), start, look);
criteriaList.add(new Criteria(zBound, firstPos, start, look));
//Remove invalid criteria
int reach = EffortlessBuildingClient.POWER_LEVEL.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
if (criteriaList.isEmpty()) return null;
//If only 1 is valid, choose that one
Criteria selected = criteriaList.get(0);
//If multiple are valid, choose based on criteria
if (criteriaList.size() > 1) {
//Select the one that is closest
//Limit the angle to not be too extreme
for (int i = 1; i < criteriaList.size(); i++) {
Criteria criteria = criteriaList.get(i);
if (criteria.distToPlayerSq < selected.distToPlayerSq && Math.abs(criteria.angle) - Math.abs(selected.angle) < 3)
selected = criteria;
}
}
return BlockPos.containing(selected.planeBound);
}
public static List<BlockPos> getWallBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (x1 == x2) {
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
addXWallBlocks(list, x1, y1, y2, z1, z2);
else
addXHollowWallBlocks(list, x1, y1, y2, z1, z2);
} else {
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
addZWallBlocks(list, x1, x2, y1, y2, z1);
else
addZHollowWallBlocks(list, x1, x2, y1, y2, z1);
}
return list;
}
public static void addXWallBlocks(List<BlockPos> list, int x, int y1, int y2, int z1, int z2) {
for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) {
for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) {
list.add(new BlockPos(x, y, z));
}
}
}
public static void addZWallBlocks(List<BlockPos> list, int x1, int x2, int y1, int y2, int z) {
for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) {
for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) {
list.add(new BlockPos(x, y, z));
}
}
}
public static void addXHollowWallBlocks(List<BlockPos> list, int x, int y1, int y2, int z1, int z2) {
Line.addZLineBlocks(list, z1, z2, x, y1);
Line.addZLineBlocks(list, z1, z2, x, y2);
Line.addYLineBlocks(list, y1, y2, x, z1);
Line.addYLineBlocks(list, y1, y2, x, z2);
}
public static void addZHollowWallBlocks(List<BlockPos> list, int x1, int x2, int y1, int y2, int z) {
Line.addXLineBlocks(list, x1, x2, y1, z);
Line.addXLineBlocks(list, x1, x2, y2, z);
Line.addYLineBlocks(list, y1, y2, x1, z);
Line.addYLineBlocks(list, y1, y2, x2, z);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return findWall(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getWallBlocks(player, x1, y1, z1, x2, y2, z2);
}
static class Criteria {
Vec3 planeBound;
double distToPlayerSq;
double angle;
Criteria(Vec3 planeBound, BlockPos firstPos, Vec3 start, Vec3 look) {
this.planeBound = planeBound;
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
Vec3 wall = this.planeBound.subtract(Vec3.atLowerCornerOf(firstPos));
this.angle = wall.x * look.x + wall.z * look.z; //dot product ignoring y (looking up/down should not affect this angle)
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq);
}
}
}

View File

@@ -0,0 +1,63 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Array extends BaseModifier {
public Vec3i offset = BlockPos.ZERO;
public int count = 5;
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!enabled || offset.getX() == 0 && offset.getY() == 0 && offset.getZ() == 0) return;
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
var pos = blockEntry.blockPos;
for (int i = 0; i < count; i++) {
pos = pos.offset(offset);
if (blocks.containsKey(pos)) continue;
var newBlockEntry = new BlockEntry(pos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
blocks.add(newBlockEntry);
}
}
}
@Override
public void onPowerLevelChanged(int powerLevel) {
}
public int getReach() {
//find largest offset
int x = Math.abs(offset.getX());
int y = Math.abs(offset.getY());
int z = Math.abs(offset.getZ());
int largestOffset = Math.max(Math.max(x, y), z);
return largestOffset * count;
}
@Override
public CompoundTag serializeNBT() {
var compound = super.serializeNBT();
compound.putIntArray("offset", new int[]{offset.getX(), offset.getY(), offset.getZ()});
compound.putInt("count", count);
return compound;
}
@Override
public void deserializeNBT(CompoundTag compound) {
super.deserializeNBT(compound);
int[] offsetArray = compound.getIntArray("offset");
offset = new Vec3i(offsetArray[0], offsetArray[1], offsetArray[2]);
count = compound.getInt("count");
}
}

View File

@@ -0,0 +1,24 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public abstract class BaseModifier {
public boolean enabled = true;
public abstract void findCoordinates(BlockSet blocks, Player player);
public abstract void onPowerLevelChanged(int powerLevel);
public CompoundTag serializeNBT() {
CompoundTag compound = new CompoundTag();
compound.putString("type", this.getClass().getSimpleName());
compound.putBoolean("enabled", enabled);
return compound;
}
public void deserializeNBT(CompoundTag compound) {
enabled = compound.getBoolean("enabled");
}
}

View File

@@ -0,0 +1,160 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.utility.NBTHelper;
import nl.requios.effortlessbuilding.network.ModifierSettingsPacket;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@OnlyIn(Dist.CLIENT)
public class BuildModifiers {
private List<BaseModifier> modifierSettingsList = new ArrayList<>();
public List<BaseModifier> getModifierSettingsList() {
return Collections.unmodifiableList(modifierSettingsList);
}
public void addModifierSettings(BaseModifier modifierSettings) {
modifierSettingsList.add(modifierSettings);
}
public void removeModifierSettings(BaseModifier modifierSettings) {
modifierSettingsList.remove(modifierSettings);
}
public void removeModifierSettings(int index) {
modifierSettingsList.remove(index);
}
public void moveUp(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == 0) return;
Collections.swap(modifierSettingsList, index, index - 1);
}
public void moveDown(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == modifierSettingsList.size() - 1) return;
Collections.swap(modifierSettingsList, index, index + 1);
}
public void setFirst(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == 0) return;
modifierSettingsList.remove(index);
modifierSettingsList.add(0, modifierSettings);
}
public void setLast(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == modifierSettingsList.size() - 1) return;
modifierSettingsList.remove(index);
modifierSettingsList.add(modifierSettings);
}
public void clearAllModifierSettings() {
modifierSettingsList.clear();
}
public void findCoordinates(BlockSet blocks, Player player) {
for (BaseModifier modifierSettings : modifierSettingsList) {
modifierSettings.findCoordinates(blocks, player);
}
}
public void onPowerLevelChanged(int powerLevel) {
for (BaseModifier modifierSettings : modifierSettingsList) {
modifierSettings.onPowerLevelChanged(powerLevel);
}
}
public CompoundTag serializeNBT() {
var compoundTag = new CompoundTag();
compoundTag.put("modifierSettingsList", NBTHelper.writeCompoundList(modifierSettingsList, BaseModifier::serializeNBT));
return compoundTag;
}
public void deserializeNBT(CompoundTag compoundTag) {
var listTag = compoundTag.getList("modifierSettingsList", Tag.TAG_COMPOUND);
modifierSettingsList = NBTHelper.readCompoundList(listTag, tag -> {
var modifier = createModifier(tag.getString("type"));
modifier.deserializeNBT(tag);
return modifier;
});
}
public void save() {
PacketHandler.INSTANCE.sendToServer(new ModifierSettingsPacket(serializeNBT()));
//Save locally as well?
// var listTag = NBTHelper.writeCompoundList(modifierSettingsList, BaseModifier::serializeNBT);
// player.getPersistentData().put(DATA_KEY, listTag);
}
private BaseModifier createModifier(String type) {
switch (type) {
case "Mirror": return new Mirror();
case "Array": return new Array();
case "RadialMirror": return new RadialMirror();
default: throw new IllegalArgumentException("Unknown modifier type: " + type);
}
}
// public static String sanitize(ModifierSettingsManager.ModifierSettings modifierSettings, Player player) {
// int maxReach = ReachHelper.getMaxReach(player);
// String error = "";
//
// //Mirror settings
// Mirror.MirrorSettings m = modifierSettings.getMirrorSettings();
// if (m.radius < 1) {
// m.radius = 1;
// error += "Mirror size has to be at least 1. This has been corrected. ";
// }
// if (m.getReach() > maxReach) {
// m.radius = maxReach / 2;
// error += "Mirror exceeds your maximum reach of " + (maxReach / 2) + ". Radius has been set to " + (maxReach / 2) + ". ";
// }
//
// //Array settings
// Array.ArraySettings a = modifierSettings.getArraySettings();
// if (a.count < 0) {
// a.count = 0;
// error += "Array count may not be negative. It has been reset to 0.";
// }
//
// if (a.getReach() > maxReach) {
// a.count = 0;
// error += "Array exceeds your maximum reach of " + maxReach + ". Array count has been reset to 0. ";
// }
//
// //Radial mirror settings
// RadialMirror.RadialMirrorSettings r = modifierSettings.getRadialMirrorSettings();
// if (r.slices < 2) {
// r.slices = 2;
// error += "Radial mirror needs to have at least 2 slices. Slices has been set to 2.";
// }
//
// if (r.radius < 1) {
// r.radius = 1;
// error += "Radial mirror radius has to be at least 1. This has been corrected. ";
// }
// if (r.getReach() > maxReach) {
// r.radius = maxReach / 2;
// error += "Radial mirror exceeds your maximum reach of " + (maxReach / 2) + ". Radius has been set to " + (maxReach / 2) + ". ";
// }
//
// return error;
// }
}

View File

@@ -0,0 +1,151 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Mirror extends BaseModifier {
public Vec3 position = new Vec3(0.5, 64.5, 0.5);
public boolean mirrorX = true;
public boolean mirrorY = false;
public boolean mirrorZ = false;
public int radius = 10;
public boolean drawLines = true;
public boolean drawPlanes = true;
public Mirror() {
super();
var player = Minecraft.getInstance().player;
if (player != null)
position = Vec3.atLowerCornerOf(Minecraft.getInstance().player.blockPosition());
}
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!(enabled && (mirrorX || mirrorY || mirrorZ))) return;
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
if (!isWithinRange(blockEntry.blockPos)) continue;
if (mirrorX) performMirrorX(blocks, blockEntry);
if (mirrorY) performMirrorY(blocks, blockEntry);
if (mirrorZ) performMirrorZ(blocks, blockEntry);
}
}
@Override
public void onPowerLevelChanged(int powerLevel) {
radius = EffortlessBuildingClient.POWER_LEVEL.getMaxMirrorRadius(Minecraft.getInstance().player);
}
private void performMirrorX(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double x = position.x + (position.x - blockEntry.blockPos.getX() - 0.5);
BlockPos newBlockPos = BlockPos.containing(x, blockEntry.blockPos.getY(), blockEntry.blockPos.getZ());
if (blocks.containsKey(newBlockPos)) return;
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorX = !newBlockEntry.mirrorX;
blocks.add(newBlockEntry);
if (mirrorY) performMirrorY(blocks, newBlockEntry);
if (mirrorZ) performMirrorZ(blocks, newBlockEntry);
}
private void performMirrorY(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double y = position.y + (position.y - blockEntry.blockPos.getY() - 0.5);
BlockPos newBlockPos = BlockPos.containing(blockEntry.blockPos.getX(), y, blockEntry.blockPos.getZ());
if (blocks.containsKey(newBlockPos)) return;
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorY = !newBlockEntry.mirrorY;
blocks.add(newBlockEntry);
if (mirrorZ) performMirrorZ(blocks, newBlockEntry);
}
private void performMirrorZ(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double z = position.z + (position.z - blockEntry.blockPos.getZ() - 0.5);
BlockPos newBlockPos = BlockPos.containing(blockEntry.blockPos.getX(), blockEntry.blockPos.getY(), z);
if (blocks.containsKey(newBlockPos)) return;
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorZ = !newBlockEntry.mirrorZ;
blocks.add(newBlockEntry);
}
public boolean isWithinRange(BlockPos blockPos) {
return !(blockPos.getX() + 0.5 < position.x - radius) && !(blockPos.getX() + 0.5 > position.x + radius) &&
!(blockPos.getY() + 0.5 < position.y - radius) && !(blockPos.getY() + 0.5 > position.y + radius) &&
!(blockPos.getZ() + 0.5 < position.z - radius) && !(blockPos.getZ() + 0.5 > position.z + radius);
}
public int getReach() {
return radius * 2; //Change ModifierSettings#setReachUpgrade too
}
public void toggleMirrorAxis(int index) {
switch (index) {
case 0 -> mirrorX = !mirrorX;
case 1 -> mirrorY = !mirrorY;
case 2 -> mirrorZ = !mirrorZ;
}
}
public boolean getMirrorAxis(int index) {
switch (index) {
case 0 -> {
return mirrorX;
}
case 1 -> {
return mirrorY;
}
case 2 -> {
return mirrorZ;
}
}
return false;
}
@Override
public CompoundTag serializeNBT() {
var compound = super.serializeNBT();
compound.putDouble("positionX", position.x);
compound.putDouble("positionY", position.y);
compound.putDouble("positionZ", position.z);
compound.putBoolean("mirrorX", mirrorX);
compound.putBoolean("mirrorY", mirrorY);
compound.putBoolean("mirrorZ", mirrorZ);
compound.putInt("radius", radius);
compound.putBoolean("drawLines", drawLines);
compound.putBoolean("drawPlanes", drawPlanes);
return compound;
}
@Override
public void deserializeNBT(CompoundTag compound) {
super.deserializeNBT(compound);
position = new Vec3(compound.getDouble("positionX"), compound.getDouble("positionY"), compound.getDouble("positionZ"));
mirrorX = compound.getBoolean("mirrorX");
mirrorY = compound.getBoolean("mirrorY");
mirrorZ = compound.getBoolean("mirrorZ");
radius = compound.getInt("radius");
drawLines = compound.getBoolean("drawLines");
drawPlanes = compound.getBoolean("drawPlanes");
}
}

View File

@@ -0,0 +1,186 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class RadialMirror extends BaseModifier {
public Vec3 position = new Vec3(0.5, 64.5, 0.5);
public int slices = 4;
public boolean alternate = false;
public int radius = 20;
public boolean drawLines = true;
public boolean drawPlanes = false;
public RadialMirror() {
super();
var player = Minecraft.getInstance().player;
if (player != null)
position = Vec3.atLowerCornerOf(Minecraft.getInstance().player.blockPosition());
}
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!enabled) return;
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
if (!isWithinRange(blockEntry.blockPos)) continue;
performRadialMirror(blocks, blockEntry);
}
}
@Override
public void onPowerLevelChanged(int powerLevel) {
radius = EffortlessBuildingClient.POWER_LEVEL.getMaxMirrorRadius(Minecraft.getInstance().player);
}
public void performRadialMirror(BlockSet blocks, BlockEntry blockEntry) {
//get angle between slices
double sliceAngle = 2 * Math.PI / slices;
//Get start vector relative to mirror center
Vec3 relStartVec = Vec3.atCenterOf(blockEntry.blockPos).subtract(position);
double startAngleToCenter = Mth.atan2(relStartVec.x, relStartVec.z); //between -PI and PI
//TODO change to Abs if alternative?
if (startAngleToCenter < 0) startAngleToCenter += Math.PI;
double startAngleInSlice = startAngleToCenter % sliceAngle;
for (int i = 1; i < slices; i++) {
double curAngle = sliceAngle * i;
//alternate mirroring of slices
boolean doAlternate = alternate && i % 2 == 1;
if (doAlternate) {
curAngle = curAngle - startAngleInSlice + (sliceAngle - startAngleInSlice);
}
Vec3 relNewVec = relStartVec.yRot((float) curAngle);
BlockPos newBlockPos = BlockPos.containing(position.add(relNewVec));
if (blocks.containsKey(newBlockPos)) continue;
BlockEntry newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
//rotate block
double angleToCenter = Mth.atan2(relNewVec.x, relNewVec.z); //between -PI and PI
rotateBlockEntry(newBlockEntry, angleToCenter, doAlternate);
blocks.add(newBlockEntry);
}
}
private void rotateBlockEntry(BlockEntry blockEntry, double angleToCenter, boolean alternate) {
if (angleToCenter < -0.751 * Math.PI || angleToCenter > 0.749 * Math.PI) {
blockEntry.rotation = blockEntry.rotation.getRotated(Rotation.CLOCKWISE_180);
if (alternate) {
// blockEntry.mirrorX = !blockEntry.mirrorX;
}
} else if (angleToCenter < -0.251 * Math.PI) {
blockEntry.rotation = blockEntry.rotation.getRotated(Rotation.CLOCKWISE_90);
if (alternate) {
// blockEntry.mirrorZ = !blockEntry.mirrorZ;
}
} else if (angleToCenter > 0.249 * Math.PI) {
blockEntry.rotation = blockEntry.rotation.getRotated(Rotation.COUNTERCLOCKWISE_90);
if (alternate) {
// blockEntry.mirrorZ = !blockEntry.mirrorZ;
}
} else {
if (alternate) {
// blockEntry.mirrorX = !blockEntry.mirrorX;
}
}
}
private static BlockState rotateOriginalBlockState(Player player, BlockPos startPos, double startAngleToCenter, BlockState blockState) {
BlockState newBlockState = blockState;
if (startAngleToCenter < -0.751 * Math.PI || startAngleToCenter > 0.749 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_180);
} else if (startAngleToCenter < -0.251 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.COUNTERCLOCKWISE_90);
} else if (startAngleToCenter > 0.249 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_90);
}
return newBlockState;
}
private static BlockState rotateBlockState(Player player, BlockPos startPos, Vec3 relVec, BlockState blockState, boolean alternate) {
BlockState newBlockState;
double angleToCenter = Mth.atan2(relVec.x, relVec.z); //between -PI and PI
if (angleToCenter < -0.751 * Math.PI || angleToCenter > 0.749 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_180);
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.FRONT_BACK);
}
} else if (angleToCenter < -0.251 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_90);
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.LEFT_RIGHT);
}
} else if (angleToCenter > 0.249 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.COUNTERCLOCKWISE_90);
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.LEFT_RIGHT);
}
} else {
newBlockState = blockState;
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.FRONT_BACK);
}
}
return newBlockState;
}
public boolean isWithinRange(BlockPos startPos) {
return (new Vec3(startPos.getX() + 0.5, startPos.getY() + 0.5, startPos.getZ() + 0.5).subtract(position).lengthSqr() < radius * radius);
}
public int getReach() {
return radius * 2;
}
@Override
public CompoundTag serializeNBT() {
var compound = super.serializeNBT();
compound.putDouble("positionX", position.x);
compound.putDouble("positionY", position.y);
compound.putDouble("positionZ", position.z);
compound.putInt("slices", slices);
compound.putBoolean("alternate", alternate);
compound.putInt("radius", radius);
compound.putBoolean("drawLines", drawLines);
compound.putBoolean("drawPlanes", drawPlanes);
return compound;
}
@Override
public void deserializeNBT(CompoundTag nbt) {
super.deserializeNBT(nbt);
position = new Vec3(nbt.getDouble("positionX"), nbt.getDouble("positionY"), nbt.getDouble("positionZ"));
slices = nbt.getInt("slices");
alternate = nbt.getBoolean("alternate");
radius = nbt.getInt("radius");
drawLines = nbt.getBoolean("drawLines");
drawPlanes = nbt.getBoolean("drawPlanes");
}
}

View File

@@ -1,138 +0,0 @@
package nl.requios.effortlessbuilding.capability;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import nl.requios.effortlessbuilding.Array;
import nl.requios.effortlessbuilding.BuildSettingsManager.BuildSettings;
import nl.requios.effortlessbuilding.Mirror;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@Mod.EventBusSubscriber
public class BuildModifierCapability {
@CapabilityInject(IBuildModifier.class)
public final static Capability<IBuildModifier> buildModifier = null;
public interface IBuildModifier {
BuildSettings getBuildModifierData();
void setBuildModifierData(BuildSettings buildSettings);
}
public static class BuildModifier implements IBuildModifier {
private BuildSettings buildSettings;
@Override
public BuildSettings getBuildModifierData() {
return buildSettings;
}
@Override
public void setBuildModifierData(BuildSettings buildSettings) {
this.buildSettings = buildSettings;
}
}
public static class Storage implements Capability.IStorage<IBuildModifier> {
@Override
public NBTBase writeNBT(Capability<IBuildModifier> capability, IBuildModifier instance, EnumFacing side) {
NBTTagCompound compound = new NBTTagCompound();
BuildSettings buildSettings = instance.getBuildModifierData();
//MIRROR
Mirror.MirrorSettings m = buildSettings.getMirrorSettings();
compound.setBoolean("mirrorEnabled", m.enabled);
compound.setDouble("mirrorPosX", m.position.x);
compound.setDouble("mirrorPosY", m.position.y);
compound.setDouble("mirrorPosZ", m.position.z);
compound.setBoolean("mirrorX", m.mirrorX);
compound.setBoolean("mirrorY", m.mirrorY);
compound.setBoolean("mirrorZ", m.mirrorZ);
compound.setInteger("mirrorRadius", m.radius);
compound.setBoolean("mirrorDrawLines", m.drawLines);
compound.setBoolean("mirrorDrawPlanes", m.drawPlanes);
//ARRAY
Array.ArraySettings a = buildSettings.getArraySettings();
compound.setBoolean("arrayEnabled", a.enabled);
compound.setInteger("arrayOffsetX", a.offset.getX());
compound.setInteger("arrayOffsetY", a.offset.getY());
compound.setInteger("arrayOffsetZ", a.offset.getZ());
compound.setInteger("arrayCount", a.count);
//compound.setBoolean("quickReplace", buildSettings.doQuickReplace()); dont save quickreplace
return compound;
}
@Override
public void readNBT(Capability<IBuildModifier> capability, IBuildModifier instance, EnumFacing side, NBTBase nbt) {
NBTTagCompound compound = (NBTTagCompound) nbt;
//MIRROR
boolean mirrorEnabled = compound.getBoolean("mirrorEnabled");
Vec3d mirrorPosition = new Vec3d(compound.getDouble("mirrorPosX"), compound.getDouble("mirrorPosY"), compound.getDouble("mirrorPosZ"));
boolean mirrorX = compound.getBoolean("mirrorX");
boolean mirrorY = compound.getBoolean("mirrorY");
boolean mirrorZ = compound.getBoolean("mirrorZ");
int mirrorRadius = compound.getInteger("mirrorRadius");
boolean mirrorDrawLines = compound.getBoolean("mirrorDrawLines");
boolean mirrorDrawPlanes = compound.getBoolean("mirrorDrawPlanes");
Mirror.MirrorSettings mirrorSettings = new Mirror.MirrorSettings(mirrorEnabled, mirrorPosition, mirrorX, mirrorY, mirrorZ, mirrorRadius, mirrorDrawLines, mirrorDrawPlanes);
//ARRAY
boolean arrayEnabled = compound.getBoolean("arrayEnabled");
BlockPos arrayOffset = new BlockPos(compound.getInteger("arrayOffsetX"), compound.getInteger("arrayOffsetY"), compound.getInteger("arrayOffsetZ"));
int arrayCount = compound.getInteger("arrayCount");
Array.ArraySettings arraySettings = new Array.ArraySettings(arrayEnabled, arrayOffset, arrayCount);
//boolean quickReplace = compound.getBoolean("quickReplace"); //dont load quickreplace
BuildSettings buildSettings = new BuildSettings(mirrorSettings, arraySettings, false);
instance.setBuildModifierData(buildSettings);
}
}
public static class Provider implements ICapabilitySerializable<NBTBase> {
IBuildModifier inst = buildModifier.getDefaultInstance();
@Override
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing) {
return capability == buildModifier;
}
@Override
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing) {
if (capability == buildModifier) return buildModifier.<T>cast(inst);
return null;
}
@Override
public NBTBase serializeNBT() {
return buildModifier.getStorage().writeNBT(buildModifier, inst, null);
}
@Override
public void deserializeNBT(NBTBase nbt) {
buildModifier.getStorage().readNBT(buildModifier, inst, null, nbt);
}
}
// Allows for the capability to persist after death.
@SubscribeEvent
public static void clonePlayer(PlayerEvent.Clone event) {
IBuildModifier original = event.getOriginal().getCapability(buildModifier, null);
IBuildModifier clone = event.getEntity().getCapability(buildModifier, null);
clone.setBuildModifierData(original.getBuildModifierData());
}
}

View File

@@ -0,0 +1,87 @@
package nl.requios.effortlessbuilding.compatibility;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.create.foundation.item.ItemHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
public class CompatHelper {
public static void setup() {
}
public static boolean isItemBlockProxy(ItemStack stack) {
return isItemBlockProxy(stack, true);
}
// Check if the item given is a proxy for blocks. For now, we check for the randomizer bag,
// /dank/null, or plain old blocks.
public static boolean isItemBlockProxy(ItemStack stack, boolean seeBlockItemsAsProxies) {
Item item = stack.getItem();
if (item instanceof BlockItem)
return seeBlockItemsAsProxies;
return item instanceof AbstractRandomizerBagItem;
}
// Get the block to be placed by this proxy. For the /dank/null, it's the slot stack
// pointed to by nbt integer selectedIndex.
public static ItemStack getItemBlockFromStack(ItemStack proxy) {
Item proxyItem = proxy.getItem();
if (proxyItem instanceof BlockItem)
return proxy;
//Randomizer Bag
if (proxyItem instanceof AbstractRandomizerBagItem) {
ItemStack itemStack = proxy;
while (!(itemStack.getItem() instanceof BlockItem || itemStack.isEmpty())) {
if (itemStack.getItem() instanceof AbstractRandomizerBagItem) {
AbstractRandomizerBagItem randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem();
itemStack = randomizerBagItem.pickRandomStack(randomizerBagItem.getBagInventory(itemStack));
}
}
return itemStack;
}
return ItemStack.EMPTY;
}
public static ItemStack getItemBlockByState(ItemStack stack, BlockState state) {
if (state == null) return ItemStack.EMPTY;
Item blockItem = Item.byBlock(state.getBlock());
if (stack.getItem() instanceof BlockItem)
return stack;
else if (stack.getItem() instanceof AbstractRandomizerBagItem) {
AbstractRandomizerBagItem randomizerBagItem = (AbstractRandomizerBagItem) stack.getItem();
IItemHandler bagInventory = randomizerBagItem.getBagInventory(stack);
return randomizerBagItem.findStack(bagInventory, blockItem);
}
return ItemStack.EMPTY;
}
public static boolean containsBlock(ItemStack stack, Block block) {
if (stack == null || stack.isEmpty() || !isItemBlockProxy(stack)) {
return block == null || block == Blocks.AIR;
}
if (stack.getItem() instanceof BlockItem) {
return ((BlockItem) stack.getItem()).getBlock() == block;
}
if (stack.getItem() instanceof AbstractRandomizerBagItem randomizerBagItem) {
IItemHandler bagInventory = randomizerBagItem.getBagInventory(stack);
ItemStack firstMatch = ItemHelper.findFirstMatch(bagInventory, s -> s.getItem() instanceof BlockItem);
return firstMatch != null && !firstMatch.isEmpty();
}
return false;
}
}

View File

@@ -0,0 +1,34 @@
package nl.requios.effortlessbuilding.create;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import org.lwjgl.glfw.GLFW;
public class AllKeys {
public static boolean isKeyDown(int key) {
return InputConstants.isKeyDown(Minecraft.getInstance()
.getWindow()
.getWindow(), key);
}
public static boolean isMouseButtonDown(int button) {
return GLFW.glfwGetMouseButton(Minecraft.getInstance()
.getWindow()
.getWindow(), button) == 1;
}
public static boolean ctrlDown() {
return Screen.hasControlDown();
}
public static boolean shiftDown() {
return Screen.hasShiftDown();
}
public static boolean altDown() {
return Screen.hasAltDown();
}
}

View File

@@ -0,0 +1,33 @@
package nl.requios.effortlessbuilding.create;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.resources.ResourceLocation;
public enum AllSpecialTextures {
BLANK("blank.png"),
CHECKERED("checkerboard.png"),
THIN_CHECKERED("thin_checkerboard.png"),
CUTOUT_CHECKERED("cutout_checkerboard.png"),
HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"),
SELECTION("selection.png"),
GLUE("glue.png"),
;
public static final String ASSET_PATH = "textures/special/";
private ResourceLocation location;
private AllSpecialTextures(String filename) {
location = Create.asResource(ASSET_PATH + filename);
}
public void bind() {
RenderSystem.setShaderTexture(0, location);
}
public ResourceLocation getLocation() {
return location;
}
}

View File

@@ -0,0 +1,16 @@
package nl.requios.effortlessbuilding.create;
import com.mojang.logging.LogUtils;
import net.minecraft.resources.ResourceLocation;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import org.slf4j.Logger;
public class Create {
public static final String ID = EffortlessBuilding.MODID;
public static final Logger LOGGER = LogUtils.getLogger();
public static ResourceLocation asResource(String path) {
return new ResourceLocation(EffortlessBuilding.MODID, path);
}
}

View File

@@ -0,0 +1,15 @@
package nl.requios.effortlessbuilding.create;
import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache;
import nl.requios.effortlessbuilding.create.foundation.utility.ghost.GhostBlocks;
import nl.requios.effortlessbuilding.create.foundation.utility.outliner.Outliner;
public class CreateClient {
public static final SuperByteBufferCache BUFFER_CACHE = new SuperByteBufferCache();
public static final Outliner OUTLINER = new Outliner();
public static final GhostBlocks GHOST_BLOCKS = new GhostBlocks();
public static void invalidateRenderers() {
CreateClient.BUFFER_CACHE.invalidate();
}
}

View File

@@ -0,0 +1,26 @@
package nl.requios.effortlessbuilding.create;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class CreateClientTest {
// @SubscribeEvent
// public static void onTick(TickEvent.ClientTickEvent event) {
// CreateClient.GHOST_BLOCKS.showGhostState(1, Blocks.SPRUCE_LOG.defaultBlockState())
// .at(0, 120, 0)
// .breathingAlpha();
// CreateClient.GHOST_BLOCKS.showGhostState(2, Blocks.SPRUCE_LOG.defaultBlockState())
// .at(1, 120, 0)
// .breathingAlpha();
//
// CreateClient.OUTLINER.showAABB(1, new AABB(0, 0, 0, 10, 2, 6)
// .move(10, 120, 0))
// .withFaceTexture(AllSpecialTextures.CHECKERED)
// .colored(new Color(0.11f, 0.49f, 0.7f, 1f))
//// .colored(0xbfbfbf)
// .disableNormals()
// .lineWidth(1 / 32f);
// }
}

View File

@@ -0,0 +1,96 @@
package nl.requios.effortlessbuilding.create.events;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderLevelStageEvent;
import net.minecraftforge.client.event.ViewportEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.TickEvent.ClientTickEvent;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import nl.requios.effortlessbuilding.create.Create;
import nl.requios.effortlessbuilding.create.CreateClient;
import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer;
import nl.requios.effortlessbuilding.create.foundation.utility.AnimationTickHolder;
import nl.requios.effortlessbuilding.create.foundation.utility.CameraAngleAnimationService;
import nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers.WrappedClientWorld;
@EventBusSubscriber(Dist.CLIENT)
public class ClientEvents {
private static final String ITEM_PREFIX = "item." + Create.ID;
private static final String BLOCK_PREFIX = "block." + Create.ID;
@SubscribeEvent
public static void onTick(ClientTickEvent event) {
if (!isGameActive() || event.phase != TickEvent.Phase.END) return;
AnimationTickHolder.tick();
CreateClient.GHOST_BLOCKS.tickGhosts();
CreateClient.OUTLINER.tickOutlines();
CameraAngleAnimationService.tick();
}
@SubscribeEvent
public static void onLoadWorld(LevelEvent.Load event) {
LevelAccessor world = event.getLevel();
if (world.isClientSide() && world instanceof ClientLevel && !(world instanceof WrappedClientWorld)) {
CreateClient.invalidateRenderers();
AnimationTickHolder.reset();
}
}
@SubscribeEvent
public static void onUnloadWorld(LevelEvent.Unload event) {
if (!event.getLevel()
.isClientSide())
return;
CreateClient.invalidateRenderers();
AnimationTickHolder.reset();
}
@SubscribeEvent
public static void onRenderWorld(RenderLevelStageEvent event) {
if(event.getStage() != RenderLevelStageEvent.Stage.AFTER_CUTOUT_BLOCKS) return;
Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera()
.getPosition();
float pt = AnimationTickHolder.getPartialTicks();
PoseStack ms = event.getPoseStack();
ms.pushPose();
ms.translate(-cameraPos.x(), -cameraPos.y(), -cameraPos.z());
SuperRenderTypeBuffer buffer = SuperRenderTypeBuffer.getInstance();
CreateClient.GHOST_BLOCKS.renderAll(ms, buffer);
CreateClient.OUTLINER.renderOutlines(ms, buffer, pt);
buffer.draw();
RenderSystem.enableCull();
ms.popPose();
}
@SubscribeEvent
public static void onCameraSetup(ViewportEvent.ComputeCameraAngles event) {
float partialTicks = AnimationTickHolder.getPartialTicks();
if (CameraAngleAnimationService.isYawAnimating())
event.setYaw(CameraAngleAnimationService.getYaw(partialTicks));
if (CameraAngleAnimationService.isPitchAnimating())
event.setPitch(CameraAngleAnimationService.getPitch(partialTicks));
}
public static boolean isGameActive() {
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
}
}

View File

@@ -0,0 +1,39 @@
package nl.requios.effortlessbuilding.create.events;
import net.minecraft.world.level.LevelAccessor;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import nl.requios.effortlessbuilding.create.foundation.utility.WorldAttached;
@EventBusSubscriber
public class CommonEvents {
@SubscribeEvent
public static void onUnloadWorld(LevelEvent.Unload event) {
LevelAccessor world = event.getLevel();
WorldAttached.invalidateWorld(world);
}
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)
public static class ModBusEvents {
// @SubscribeEvent
// public static void addPackFinders(AddPackFindersEvent event) {
// if (event.getPackType() == PackType.CLIENT_RESOURCES) {
// IModFileInfo modFileInfo = ModList.get().getModFileById(Create.ID);
// if (modFileInfo == null) {
// Create.LOGGER.error("Could not find Create mod file info; built-in resource packs will be missing!");
// return;
// }
// IModFile modFile = modFileInfo.getFile();
// event.addRepositorySource((consumer, constructor) -> {
// consumer.accept(Pack.create(Create.asResource("legacy_copper").toString(), false, () -> new ModFilePackResources("Create Legacy Copper", modFile, "resourcepacks/legacy_copper"), constructor, Pack.Position.TOP, PackSource.DEFAULT));
// });
// }
// }
}
}

View File

@@ -0,0 +1,16 @@
package nl.requios.effortlessbuilding.create.foundation;
import nl.requios.effortlessbuilding.create.foundation.utility.LangNumberFormat;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
public class ClientResourceReloadListener implements ResourceManagerReloadListener {
@Override
public void onResourceManagerReload(ResourceManager resourceManager) {
// CreateClient.invalidateRenderers();
// SoundScapes.invalidateAll();
LangNumberFormat.numberFormat.update();
}
}

View File

@@ -0,0 +1,25 @@
package nl.requios.effortlessbuilding.create.foundation;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.resource.PathPackResources;
import java.nio.file.Path;
public class ModFilePackResources extends PathPackResources {
protected final IModFile modFile;
protected final String sourcePath;
public ModFilePackResources(String name, IModFile modFile, String sourcePath) {
super(name, true, modFile.findResource(sourcePath));
this.modFile = modFile;
this.sourcePath = sourcePath;
}
@Override
protected Path resolve(String... paths) {
String[] allPaths = new String[paths.length + 1];
allPaths[0] = sourcePath;
System.arraycopy(paths, 0, allPaths, 1, paths.length);
return modFile.findResource(allPaths);
}
}

View File

@@ -0,0 +1,49 @@
package nl.requios.effortlessbuilding.create.foundation.block.render;
import com.jozufozu.flywheel.core.StitchedSprite;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.ResourceLocation;
public class SpriteShiftEntry {
protected StitchedSprite original;
protected StitchedSprite target;
public void set(ResourceLocation originalTextureLocation, ResourceLocation targetTextureLocation) {
original = new StitchedSprite(originalTextureLocation);
target = new StitchedSprite(targetTextureLocation);
}
public ResourceLocation getOriginalResourceLocation() {
return original.getLocation();
}
public ResourceLocation getTargetResourceLocation() {
return target.getLocation();
}
public TextureAtlasSprite getOriginal() {
return original.get();
}
public TextureAtlasSprite getTarget() {
return target.get();
}
public float getTargetU(float localU) {
return getTarget().getU(getUnInterpolatedU(getOriginal(), localU));
}
public float getTargetV(float localV) {
return getTarget().getV(getUnInterpolatedV(getOriginal(), localV));
}
public static float getUnInterpolatedU(TextureAtlasSprite sprite, float u) {
float f = sprite.getU1() - sprite.getU0();
return (u - sprite.getU0()) / f * 16.0F;
}
public static float getUnInterpolatedV(TextureAtlasSprite sprite, float v) {
float f = sprite.getV1() - sprite.getV0();
return (v - sprite.getV0()) / f * 16.0F;
}
}

View File

@@ -0,0 +1,181 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.gui.widget.AbstractSimiWidget;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.gui.buildmodifier.ModifiersScreenList;
import java.util.Collection;
import java.util.List;
@OnlyIn(Dist.CLIENT)
public abstract class AbstractSimiScreen extends Screen {
protected int windowWidth, windowHeight;
protected int windowXOffset, windowYOffset;
protected int guiLeft, guiTop;
protected AbstractSimiScreen(Component title) {
super(title);
}
protected AbstractSimiScreen() {
this(Components.immutableEmpty());
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowSize(int width, int height) {
windowWidth = width;
windowHeight = height;
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowOffset(int xOffset, int yOffset) {
windowXOffset = xOffset;
windowYOffset = yOffset;
}
@Override
protected void init() {
guiLeft = (width - windowWidth) / 2;
guiTop = (height - windowHeight) / 2;
guiLeft += windowXOffset;
guiTop += windowYOffset;
}
@Override
public void tick() {
for (GuiEventListener listener : children()) {
if (listener instanceof TickableGuiEventListener tickable) {
tickable.tick();
}
}
}
@Override
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (getFocused() != null && !getFocused().isMouseOver(pMouseX, pMouseY))
setFocused(null);
return super.mouseClicked(pMouseX, pMouseY, pButton);
}
@Override
public boolean isPauseScreen() {
return false;
}
@SuppressWarnings("unchecked")
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(W... widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(Collection<W> widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected void removeWidgets(GuiEventListener... widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
protected void removeWidgets(Collection<? extends GuiEventListener> widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
@Override
public void render(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
partialTicks = minecraft.getFrameTime();
ms.pushPose();
prepareFrame();
renderWindowBackground(ms, mouseX, mouseY, partialTicks);
renderWindow(ms, mouseX, mouseY, partialTicks);
super.render(ms, mouseX, mouseY, partialTicks);
renderWindowForeground(ms, mouseX, mouseY, partialTicks);
endFrame();
ms.popPose();
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
boolean keyPressed = super.keyPressed(keyCode, scanCode, modifiers);
if (keyPressed || getFocused() instanceof EditBox)
return keyPressed;
InputConstants.Key mouseKey = InputConstants.getKey(keyCode, scanCode);
if (this.minecraft.options.keyInventory.isActiveAndMatches(mouseKey)) {
this.onClose();
return true;
}
return false;
}
protected void prepareFrame() {}
protected void renderWindowBackground(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
renderBackground(ms);
}
protected abstract void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks);
protected void renderWindowForeground(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
for (Renderable widget : renderables) {
if (widget instanceof AbstractSimiWidget simiWidget && simiWidget.isMouseOver(mouseX, mouseY)
&& simiWidget.visible) {
List<Component> tooltip = simiWidget.getToolTip();
if (tooltip.isEmpty())
continue;
int ttx = simiWidget.lockedTooltipX == -1 ? mouseX : simiWidget.lockedTooltipX + simiWidget.getX();
int tty = simiWidget.lockedTooltipY == -1 ? mouseY : simiWidget.lockedTooltipY + simiWidget.getY();
renderComponentTooltip(ms, tooltip, ttx, tty);
}
//Added
if (widget instanceof ModifiersScreenList list) {
list.renderWindowForeground(ms, mouseX, mouseY, partialTicks);
}
}
}
protected void endFrame() {}
@Deprecated
protected void debugWindowArea(PoseStack graphics) {
fill(graphics, guiLeft + windowWidth, guiTop + windowHeight, guiLeft, guiTop, 0xD3D3D3D3);
}
@Override
public GuiEventListener getFocused() {
GuiEventListener focused = super.getFocused();
if (focused instanceof AbstractWidget && !((AbstractWidget) focused).isFocused())
focused = null;
setFocused(focused);
return focused;
}
}

View File

@@ -0,0 +1,93 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.Create;
import nl.requios.effortlessbuilding.create.foundation.gui.element.ScreenElement;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
public enum AllGuiTextures implements ScreenElement {
// Widgets
BUTTON("widgets", 18, 18),
BUTTON_HOVER("widgets", 18, 0, 18, 18),
BUTTON_DOWN("widgets", 36, 0, 18, 18),
INDICATOR("widgets", 0, 18, 18, 6),
INDICATOR_WHITE("widgets", 18, 18, 18, 6),
INDICATOR_GREEN("widgets", 36, 18, 18, 6),
INDICATOR_YELLOW("widgets", 54, 18, 18, 6),
INDICATOR_RED("widgets", 72, 18, 18, 6),
HOTSLOT_ARROW("widgets", 24, 51, 20, 12),
HOTSLOT("widgets", 0, 68, 22, 22),
HOTSLOT_ACTIVE("widgets", 0, 46, 22, 22),
HOTSLOT_SUPER_ACTIVE("widgets", 27, 67, 24, 24),
SPEECH_TOOLTIP_BACKGROUND("widgets", 0, 24, 8, 8),
SPEECH_TOOLTIP_COLOR("widgets", 8, 24, 8, 8),
TRAIN_HUD_SPEED_BG("widgets", 0, 190, 182, 5),
TRAIN_HUD_SPEED("widgets", 0, 185, 182, 5),
TRAIN_HUD_THROTTLE("widgets", 0, 195, 182, 5),
TRAIN_HUD_THROTTLE_POINTER("widgets", 0, 209, 6, 9),
TRAIN_HUD_FRAME("widgets", 0, 200, 186, 7),
TRAIN_HUD_DIRECTION("widgets", 77, 165, 28, 20),
TRAIN_PROMPT_L("widgets", 8, 209, 3, 16),
TRAIN_PROMPT_R("widgets", 11, 209, 3, 16),
TRAIN_PROMPT("widgets", 0, 230, 256, 16),
;
public static final int FONT_COLOR = 0x575F7A;
public final ResourceLocation location;
public int width, height;
public int startX, startY;
private AllGuiTextures(String location, int width, int height) {
this(location, 0, 0, width, height);
}
private AllGuiTextures(int startX, int startY) {
this("icons", startX * 16, startY * 16, 16, 16);
}
private AllGuiTextures(String location, int startX, int startY, int width, int height) {
this(Create.ID, location, startX, startY, width, height);
}
private AllGuiTextures(String namespace, String location, int startX, int startY, int width, int height) {
this.location = new ResourceLocation(namespace, "textures/gui/" + location + ".png");
this.width = width;
this.height = height;
this.startX = startX;
this.startY = startY;
}
@OnlyIn(Dist.CLIENT)
public void bind() {
RenderSystem.setShaderTexture(0, location);
}
public void render(PoseStack ms, int x, int y) {
bind();
GuiComponent.blit(ms, x, y, startX, startY, width, height);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack ms, int x, int y, GuiComponent component) {
bind();
component.blit(ms, x, y, startX, startY, width, height);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack ms, int x, int y, Color c) {
bind();
UIRenderHelper.drawColoredTexture(ms, c, x, y, startX, startY, width, height);
}
}

View File

@@ -0,0 +1,218 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.Create;
import nl.requios.effortlessbuilding.create.foundation.gui.element.DelegatedStencilElement;
import nl.requios.effortlessbuilding.create.foundation.gui.element.ScreenElement;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import org.joml.Matrix4f;
public class AllIcons implements ScreenElement {
public static final ResourceLocation ICON_ATLAS = Create.asResource("textures/gui/create_icons.png");
public static final int ICON_ATLAS_SIZE = 256;
private static int x = 0, y = -1;
private int iconX;
private int iconY;
public static final AllIcons
I_ADD = newRow(),
I_TRASH = next(),
I_3x3 = next(),
I_TARGET = next(),
I_PRIORITY_VERY_LOW = next(),
I_PRIORITY_LOW = next(),
I_PRIORITY_HIGH = next(),
I_PRIORITY_VERY_HIGH = next(),
I_BLACKLIST = next(),
I_WHITELIST = next(),
I_WHITELIST_OR = next(),
I_WHITELIST_AND = next(),
I_WHITELIST_NOT = next(),
I_RESPECT_NBT = next(),
I_IGNORE_NBT = next();
public static final AllIcons
I_CONFIRM = newRow(),
I_NONE = next(),
I_OPEN_FOLDER = next(),
I_REFRESH = next(),
I_ACTIVE = next(),
I_PASSIVE = next(),
I_ROTATE_PLACE = next(),
I_ROTATE_PLACE_RETURNED = next(),
I_ROTATE_NEVER_PLACE = next(),
I_MOVE_PLACE = next(),
I_MOVE_PLACE_RETURNED = next(),
I_MOVE_NEVER_PLACE = next(),
I_CART_ROTATE = next(),
I_CART_ROTATE_PAUSED = next(),
I_CART_ROTATE_LOCKED = next();
public static final AllIcons
I_DONT_REPLACE = newRow(),
I_REPLACE_SOLID = next(),
I_REPLACE_ANY = next(),
I_REPLACE_EMPTY = next(),
I_CENTERED = next(),
I_ATTACHED = next(),
I_INSERTED = next(),
I_FILL = next(),
I_PLACE = next(),
I_REPLACE = next(),
I_CLEAR = next(),
I_OVERLAY = next(),
I_FLATTEN = next(),
I_LMB = next(),
I_SCROLL = next(),
I_RMB = next();
public static final AllIcons
I_TOOL_DEPLOY = newRow(),
I_SKIP_MISSING = next(),
I_SKIP_BLOCK_ENTITIES = next(),
I_DICE = next(),
I_TUNNEL_SPLIT = next(),
I_TUNNEL_FORCED_SPLIT = next(),
I_TUNNEL_ROUND_ROBIN = next(),
I_TUNNEL_FORCED_ROUND_ROBIN = next(),
I_TUNNEL_PREFER_NEAREST = next(),
I_TUNNEL_RANDOMIZE = next(),
I_TUNNEL_SYNCHRONIZE = next(),
I_TOOLBOX = next(),
I_VIEW_SCHEDULE = next(),
I_TOOL_MOVE_XZ = newRow(),
I_TOOL_MOVE_Y = next(),
I_TOOL_ROTATE = next(),
I_TOOL_MIRROR = next(),
I_ARM_ROUND_ROBIN = next(),
I_ARM_FORCED_ROUND_ROBIN = next(),
I_ARM_PREFER_FIRST = next(),
I_ADD_INVERTED_ATTRIBUTE = next(),
I_FLIP = next(),
I_PLAY = newRow(),
I_PAUSE = next(),
I_STOP = next(),
I_PLACEMENT_SETTINGS = next(),
I_ROTATE_CCW = next(),
I_HOUR_HAND_FIRST = next(),
I_MINUTE_HAND_FIRST = next(),
I_HOUR_HAND_FIRST_24 = next(),
I_PATTERN_SOLID = newRow(),
I_PATTERN_CHECKERED = next(),
I_PATTERN_CHECKERED_INVERSED = next(),
I_PATTERN_CHANCE_25 = next(),
I_PATTERN_CHANCE_50 = newRow(),
I_PATTERN_CHANCE_75 = next(),
I_FOLLOW_DIAGONAL = next(),
I_FOLLOW_MATERIAL = next(),
I_SCHEMATIC = newRow(),
I_SEQ_REPEAT = next(),
I_MTD_LEFT = newRow(),
I_MTD_CLOSE = next(),
I_MTD_RIGHT = next(),
I_MTD_SCAN = next(),
I_MTD_REPLAY = next(),
I_MTD_USER_MODE = next(),
I_MTD_SLOW_MODE = next(),
I_CONFIG_UNLOCKED = newRow(),
I_CONFIG_LOCKED = next(),
I_CONFIG_DISCARD = next(),
I_CONFIG_SAVE = next(),
I_CONFIG_RESET = next(),
I_CONFIG_BACK = next(),
I_CONFIG_PREV = next(),
I_CONFIG_NEXT = next(),
I_DISABLE = next(),
I_CONFIG_OPEN = next(),
I_FX_SURFACE_OFF = newRow(),
I_FX_SURFACE_ON = next(),
I_FX_FIELD_OFF = next(),
I_FX_FIELD_ON = next(),
I_FX_BLEND = next(),
I_FX_BLEND_OFF = next();
;
public AllIcons(int x, int y) {
iconX = x * 16;
iconY = y * 16;
}
private static AllIcons next() {
return new AllIcons(++x, y);
}
private static AllIcons newRow() {
return new AllIcons(x = 0, ++y);
}
@OnlyIn(Dist.CLIENT)
public void bind() {
RenderSystem.setShaderTexture(0, ICON_ATLAS);
}
@OnlyIn(Dist.CLIENT)
@Override
public void render(PoseStack ms, int x, int y) {
bind();
GuiComponent.blit(ms, x, y, 0, iconX, iconY, 16, 16, 256, 256);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack ms, MultiBufferSource buffer, int color) {
VertexConsumer builder = buffer.getBuffer(RenderType.text(ICON_ATLAS));
Matrix4f matrix = ms.last().pose();
Color rgb = new Color(color);
int light = LightTexture.FULL_BRIGHT;
Vec3 vec1 = new Vec3(0, 0, 0);
Vec3 vec2 = new Vec3(0, 1, 0);
Vec3 vec3 = new Vec3(1, 1, 0);
Vec3 vec4 = new Vec3(1, 0, 0);
float u1 = iconX * 1f / ICON_ATLAS_SIZE;
float u2 = (iconX + 16) * 1f / ICON_ATLAS_SIZE;
float v1 = iconY * 1f / ICON_ATLAS_SIZE;
float v2 = (iconY + 16) * 1f / ICON_ATLAS_SIZE;
vertex(builder, matrix, vec1, rgb, u1, v1, light);
vertex(builder, matrix, vec2, rgb, u1, v2, light);
vertex(builder, matrix, vec3, rgb, u2, v2, light);
vertex(builder, matrix, vec4, rgb, u2, v1, light);
}
@OnlyIn(Dist.CLIENT)
private void vertex(VertexConsumer builder, Matrix4f matrix, Vec3 vec, Color rgb, float u, float v, int light) {
builder.vertex(matrix, (float) vec.x, (float) vec.y, (float) vec.z)
.color(rgb.getRed(), rgb.getGreen(), rgb.getBlue(), 255)
.uv(u, v)
.uv2(light)
.endVertex();
}
@OnlyIn(Dist.CLIENT)
public DelegatedStencilElement asStencil() {
return new DelegatedStencilElement().withStencilRenderer((ms, w, h, alpha) -> this.render(ms, 0, 0)).withBounds(16, 16);
}
}

View File

@@ -0,0 +1,227 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.Style;
import nl.requios.effortlessbuilding.create.foundation.gui.element.BoxElement;
import nl.requios.effortlessbuilding.create.foundation.gui.element.TextStencilElement;
import nl.requios.effortlessbuilding.create.foundation.gui.widget.BoxWidget;
import org.lwjgl.opengl.GL30;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class ConfirmationScreen extends AbstractSimiScreen {
private Screen source;
private Consumer<Response> action = _success -> {
};
private List<FormattedText> text = new ArrayList<>();
private boolean centered = false;
private int x;
private int y;
private int textWidth;
private int textHeight;
private boolean tristate;
private BoxWidget confirm;
private BoxWidget confirmDontSave;
private BoxWidget cancel;
private BoxElement textBackground;
public enum Response {
Confirm, ConfirmDontSave, Cancel
}
/*
* Removes text lines from the back of the list
* */
public ConfirmationScreen removeTextLines(int amount) {
if (amount > text.size())
return clearText();
text.subList(text.size() - amount, text.size()).clear();
return this;
}
public ConfirmationScreen clearText() {
this.text.clear();
return this;
}
public ConfirmationScreen addText(FormattedText text) {
this.text.add(text);
return this;
}
public ConfirmationScreen withText(FormattedText text) {
return clearText().addText(text);
}
public ConfirmationScreen at(int x, int y) {
this.x = Math.max(x, 0);
this.y = Math.max(y, 0);
this.centered = false;
return this;
}
public ConfirmationScreen centered() {
this.centered = true;
return this;
}
public ConfirmationScreen withAction(Consumer<Boolean> action) {
this.action = r -> action.accept(r == Response.Confirm);
return this;
}
public ConfirmationScreen withThreeActions(Consumer<Response> action) {
this.action = action;
this.tristate = true;
return this;
}
public void open(@Nonnull Screen source) {
this.source = source;
Minecraft client = source.getMinecraft();
this.init(client, client.getWindow().getGuiScaledWidth(), client.getWindow().getGuiScaledHeight());
this.minecraft.screen = this;
}
@Override
public void tick() {
super.tick();
source.tick();
}
@Override
protected void init() {
super.init();
ArrayList<FormattedText> copy = new ArrayList<>(text);
text.clear();
copy.forEach(t -> text.addAll(font.getSplitter().splitLines(t, 300, Style.EMPTY)));
textHeight = text.size() * (font.lineHeight + 1) + 4;
textWidth = 300;
if (centered) {
x = width/2 - textWidth/2 - 2;
y = height/2 - textHeight/2 - 16;
} else {
x = Math.max(0, x - textWidth / 2);
y = Math.max(0, y -= textHeight);
}
if (x + textWidth > width) {
x = width - textWidth;
}
if (y + textHeight + 30 > height) {
y = height - textHeight - 30;
}
int buttonX = x + textWidth / 2 - 6 - (int) (70 * (tristate ? 1.5f : 1));
TextStencilElement confirmText =
new TextStencilElement(font, tristate ? "Save" : "Confirm").centered(true, true);
confirm = new BoxWidget(buttonX, y + textHeight + 6, 70, 16).withCallback(() -> accept(Response.Confirm));
confirm.showingElement(confirmText.withElementRenderer(BoxWidget.gradientFactory.apply(confirm)));
addRenderableWidget(confirm);
buttonX += 12 + 70;
if (tristate) {
TextStencilElement confirmDontSaveText =
new TextStencilElement(font, "Don't Save").centered(true, true);
confirmDontSave =
new BoxWidget(buttonX, y + textHeight + 6, 70, 16).withCallback(() -> accept(Response.ConfirmDontSave));
confirmDontSave.showingElement(
confirmDontSaveText.withElementRenderer(BoxWidget.gradientFactory.apply(confirmDontSave)));
addRenderableWidget(confirmDontSave);
buttonX += 12 + 70;
}
TextStencilElement cancelText = new TextStencilElement(font, "Cancel").centered(true, true);
cancel = new BoxWidget(buttonX, y + textHeight + 6, 70, 16)
.withCallback(() -> accept(Response.Cancel));
cancel.showingElement(cancelText.withElementRenderer(BoxWidget.gradientFactory.apply(cancel)));
addRenderableWidget(cancel);
textBackground = new BoxElement()
.gradientBorder(Theme.p(Theme.Key.BUTTON_DISABLE))
.withBounds(width + 10, textHeight + 35)
.at(-5, y - 5);
if (text.size() == 1)
x = (width - font.width(text.get(0))) / 2;
}
@Override
public void onClose() {
accept(Response.Cancel);
}
private void accept(Response success) {
minecraft.screen = source;
action.accept(success);
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
textBackground.render(ms);
int offset = font.lineHeight + 1;
int lineY = y - offset;
ms.pushPose();
ms.translate(0, 0, 200);
for (FormattedText line : text) {
lineY += offset;
if (line == null)
continue;
font.draw(ms, line.getString(), x, lineY, 0xeaeaea);
}
ms.popPose();
}
@Override
protected void renderWindowBackground(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
endFrame();
source.render(ms, 0, 0, 10); // zero mouse coords to prevent further tooltips
prepareFrame();
fillGradient(ms, 0, 0, this.width, this.height, 0x70101010, 0x80101010);
}
@Override
protected void prepareFrame() {
UIRenderHelper.swapAndBlitColor(minecraft.getMainRenderTarget(), UIRenderHelper.framebuffer);
RenderSystem.clear(GL30.GL_STENCIL_BUFFER_BIT | GL30.GL_DEPTH_BUFFER_BIT, Minecraft.ON_OSX);
}
@Override
protected void endFrame() {
UIRenderHelper.swapAndBlitColor(UIRenderHelper.framebuffer, minecraft.getMainRenderTarget());
}
@Override
public void resize(@Nonnull Minecraft client, int width, int height) {
super.resize(client, width, height);
source.resize(client, width, height);
}
@Override
public boolean isPauseScreen() {
return true;
}
}

View File

@@ -0,0 +1,83 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Axis;
public class CustomLightingSettings implements ILightingSettings {
private Vector3f light1;
private Vector3f light2;
private Matrix4f lightMatrix;
protected CustomLightingSettings(float yRot, float xRot) {
init(yRot, xRot, 0, 0, false);
}
protected CustomLightingSettings(float yRot1, float xRot1, float yRot2, float xRot2) {
init(yRot1, xRot1, yRot2, xRot2, true);
}
protected void init(float yRot1, float xRot1, float yRot2, float xRot2, boolean doubleLight) {
light1 = new Vector3f(0, 0, 1);
light1.rotate(Axis.YP.rotationDegrees(yRot1));
light1.rotate(Axis.XN.rotationDegrees(xRot1));
if (doubleLight) {
light2 = new Vector3f(0, 0, 1);
light2.rotate(Axis.YP.rotationDegrees(yRot2));
light2.rotate(Axis.XN.rotationDegrees(xRot2));
} else {
light2 = new Vector3f();
}
lightMatrix = new Matrix4f();
lightMatrix.identity();
}
@Override
public void applyLighting() {
RenderSystem.setupLevelDiffuseLighting(light1, light2, lightMatrix);
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private float yRot1, xRot1;
private float yRot2, xRot2;
private boolean doubleLight;
public Builder firstLightRotation(float yRot, float xRot) {
yRot1 = yRot;
xRot1 = xRot;
return this;
}
public Builder secondLightRotation(float yRot, float xRot) {
yRot2 = yRot;
xRot2 = xRot;
doubleLight = true;
return this;
}
public Builder doubleLight() {
doubleLight = true;
return this;
}
public CustomLightingSettings build() {
if (doubleLight) {
return new CustomLightingSettings(yRot1, xRot1, yRot2, xRot2);
} else {
return new CustomLightingSettings(yRot1, xRot1);
}
}
}
}

View File

@@ -0,0 +1,12 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.platform.Lighting;
public interface ILightingSettings {
void applyLighting();
static final ILightingSettings DEFAULT_3D = () -> Lighting.setupFor3DItems();
static final ILightingSettings DEFAULT_FLAT = () -> Lighting.setupForFlatItems();
}

View File

@@ -0,0 +1,202 @@
//package nl.requios.effortlessbuilding.create.foundation.gui;
//
//import java.util.ArrayList;
//import java.util.List;
//
//import javax.annotation.Nonnull;
//
//import net.minecraft.client.gui.GuiComponent;
//import org.joml.Matrix4f;
//
//import com.mojang.blaze3d.systems.RenderSystem;
//import com.mojang.blaze3d.vertex.PoseStack;
//import com.mojang.blaze3d.vertex.Tesselator;
//
//import net.minecraft.client.gui.Font;
//import com.mojang.blaze3d.vertex.PoseStack;
//import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
//import net.minecraft.client.renderer.MultiBufferSource;
//import net.minecraft.network.chat.FormattedText;
//import net.minecraft.network.chat.Style;
//import net.minecraft.world.item.ItemStack;
//import net.minecraftforge.client.ForgeHooksClient;
//import net.minecraftforge.client.event.RenderTooltipEvent;
//import net.minecraftforge.client.extensions.IForgeGuiGraphics;
//import net.minecraftforge.common.MinecraftForge;
//
//public class RemovedGuiUtils {
// @Nonnull
// private static ItemStack cachedTooltipStack = ItemStack.EMPTY;
//
// public static void preItemToolTip(@Nonnull ItemStack stack) {
// cachedTooltipStack = stack;
// }
//
// public static void postItemToolTip() {
// cachedTooltipStack = ItemStack.EMPTY;
// }
//
// public static void drawHoveringText(PoseStack graphics, List<? extends FormattedText> textLines, int mouseX,
// int mouseY, int screenWidth, int screenHeight, int maxTextWidth, Font font) {
// drawHoveringText(graphics, textLines, mouseX, mouseY, screenWidth, screenHeight, maxTextWidth,
// IForgeGuiGraphics.DEFAULT_BACKGROUND_COLOR, IForgeGuiGraphics.DEFAULT_BORDER_COLOR_START, IForgeGuiGraphics.DEFAULT_BORDER_COLOR_END,
// font);
// }
//
// public static void drawHoveringText(PoseStack graphics, List<? extends FormattedText> textLines, int mouseX,
// int mouseY, int screenWidth, int screenHeight, int maxTextWidth, int backgroundColor, int borderColorStart,
// int borderColorEnd, Font font) {
// drawHoveringText(cachedTooltipStack, graphics, textLines, mouseX, mouseY, screenWidth, screenHeight, maxTextWidth,
// backgroundColor, borderColorStart, borderColorEnd, font);
// }
//
// public static void drawHoveringText(@Nonnull final ItemStack stack, PoseStack graphics,
// List<? extends FormattedText> textLines, int mouseX, int mouseY, int screenWidth, int screenHeight,
// int maxTextWidth, Font font) {
// drawHoveringText(stack, graphics, textLines, mouseX, mouseY, screenWidth, screenHeight, maxTextWidth,
// IForgeGuiGraphics.DEFAULT_BACKGROUND_COLOR, IForgeGuiGraphics.DEFAULT_BORDER_COLOR_START, IForgeGuiGraphics.DEFAULT_BORDER_COLOR_END,
// font);
// }
//
// public static void drawHoveringText(@Nonnull final ItemStack stack, PoseStack pStack,
// List<? extends FormattedText> textLines, int mouseX, int mouseY, int screenWidth, int screenHeight,
// int maxTextWidth, int backgroundColor, int borderColorStart, int borderColorEnd, Font font) {
// if (textLines.isEmpty())
// return;
//
// List<ClientTooltipComponent> list = ForgeHooksClient.gatherTooltipComponents(stack, textLines,
// stack.getTooltipImage(), mouseX, screenWidth, screenHeight, font);
// RenderTooltipEvent.Pre event =
// new RenderTooltipEvent.Pre(stack, pStack, mouseX, mouseY, screenWidth, screenHeight, font, list, null);
// if (MinecraftForge.EVENT_BUS.post(event))
// return;
//
// mouseX = event.getX();
// mouseY = event.getY();
// screenWidth = event.getScreenWidth();
// screenHeight = event.getScreenHeight();
// font = event.getFont();
//
// // RenderSystem.disableRescaleNormal();
// RenderSystem.disableDepthTest();
// int tooltipTextWidth = 0;
//
// for (FormattedText textLine : textLines) {
// int textLineWidth = font.width(textLine);
// if (textLineWidth > tooltipTextWidth)
// tooltipTextWidth = textLineWidth;
// }
//
// boolean needsWrap = false;
//
// int titleLinesCount = 1;
// int tooltipX = mouseX + 12;
// if (tooltipX + tooltipTextWidth + 4 > screenWidth) {
// tooltipX = mouseX - 16 - tooltipTextWidth;
// if (tooltipX < 4) // if the tooltip doesn't fit on the screen
// {
// if (mouseX > screenWidth / 2)
// tooltipTextWidth = mouseX - 12 - 8;
// else
// tooltipTextWidth = screenWidth - 16 - mouseX;
// needsWrap = true;
// }
// }
//
// if (maxTextWidth > 0 && tooltipTextWidth > maxTextWidth) {
// tooltipTextWidth = maxTextWidth;
// needsWrap = true;
// }
//
// if (needsWrap) {
// int wrappedTooltipWidth = 0;
// List<FormattedText> wrappedTextLines = new ArrayList<>();
// for (int i = 0; i < textLines.size(); i++) {
// FormattedText textLine = textLines.get(i);
// List<FormattedText> wrappedLine = font.getSplitter()
// .splitLines(textLine, tooltipTextWidth, Style.EMPTY);
// if (i == 0)
// titleLinesCount = wrappedLine.size();
//
// for (FormattedText line : wrappedLine) {
// int lineWidth = font.width(line);
// if (lineWidth > wrappedTooltipWidth)
// wrappedTooltipWidth = lineWidth;
// wrappedTextLines.add(line);
// }
// }
// tooltipTextWidth = wrappedTooltipWidth;
// textLines = wrappedTextLines;
//
// if (mouseX > screenWidth / 2)
// tooltipX = mouseX - 16 - tooltipTextWidth;
// else
// tooltipX = mouseX + 12;
// }
//
// int tooltipY = mouseY - 12;
// int tooltipHeight = 8;
//
// if (textLines.size() > 1) {
// tooltipHeight += (textLines.size() - 1) * 10;
// if (textLines.size() > titleLinesCount)
// tooltipHeight += 2; // gap between title lines and next lines
// }
//
// if (tooltipY < 4)
// tooltipY = 4;
// else if (tooltipY + tooltipHeight + 4 > screenHeight)
// tooltipY = screenHeight - tooltipHeight - 4;
//
// final int zLevel = 400;
// RenderTooltipEvent.Color colorEvent = new RenderTooltipEvent.Color(stack, pStack, tooltipX, tooltipY,
// font, backgroundColor, borderColorStart, borderColorEnd, list);
// MinecraftForge.EVENT_BUS.post(colorEvent);
// backgroundColor = colorEvent.getBackgroundStart();
// borderColorStart = colorEvent.getBorderStart();
// borderColorEnd = colorEvent.getBorderEnd();
//
// pStack.pushPose();
// Matrix4f mat = pStack.last()
// .pose();
// GuiComponent.fillGradient(pStack, tooltipX - 3, tooltipY - 4, tooltipX + tooltipTextWidth + 3,
// tooltipY - 3, zLevel, backgroundColor, backgroundColor);
// GuiComponent.fillGradient(pStack, tooltipX - 3, tooltipY + tooltipHeight + 3,
// tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 4, zLevel, backgroundColor, backgroundColor);
// GuiComponent.fillGradient(pStack, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3,
// tooltipY + tooltipHeight + 3, zLevel, backgroundColor, backgroundColor);
// GuiComponent.fillGradient(pStack, tooltipX - 4, tooltipY - 3, tooltipX - 3, tooltipY + tooltipHeight + 3,
// zLevel, backgroundColor, backgroundColor);
// GuiComponent.fillGradient(pStack, tooltipX + tooltipTextWidth + 3, tooltipY - 3,
// tooltipX + tooltipTextWidth + 4, tooltipY + tooltipHeight + 3, zLevel, backgroundColor, backgroundColor);
// GuiComponent.fillGradient(pStack, tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1,
// tooltipY + tooltipHeight + 3 - 1, zLevel, borderColorStart, borderColorEnd);
// GuiComponent.fillGradient(pStack, tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1,
// tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, zLevel, borderColorStart, borderColorEnd);
// GuiComponent.fillGradient(pStack, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3,
// tooltipY - 3 + 1, zLevel, borderColorStart, borderColorStart);
// GuiComponent.fillGradient(pStack, tooltipX - 3, tooltipY + tooltipHeight + 2,
// tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, zLevel, borderColorEnd, borderColorEnd);
//
// MultiBufferSource.BufferSource renderType = MultiBufferSource.immediate(Tesselator.getInstance()
// .getBuilder());
// pStack.translate(0.0D, 0.0D, zLevel);
//
// for (int lineNumber = 0; lineNumber < list.size(); ++lineNumber) {
// ClientTooltipComponent line = list.get(lineNumber);
//
// if (line != null)
// line.renderText(font, tooltipX, tooltipY, mat, renderType);
//
// if (lineNumber + 1 == titleLinesCount)
// tooltipY += 2;
//
// tooltipY += 10;
// }
//
// renderType.endBatch();
// pStack.popPose();
//
// RenderSystem.enableDepthTest();
// }
//}

View File

@@ -0,0 +1,229 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import nl.requios.effortlessbuilding.create.foundation.utility.Couple;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
public class Theme {
private static final List<Theme> THEMES = new ArrayList<>();
public static final Theme BASE = addTheme(new Theme());
public static Theme addTheme(@Nonnull Theme theme) {
THEMES.add(theme);
THEMES.sort(Comparator.comparingInt(Theme::getPriority).reversed());
return theme;
}
public static void removeTheme(Theme theme) {
THEMES.remove(theme);
}
public static void reload() {
THEMES.forEach(Theme::init);
}
private static ColorHolder resolve(String key) {
return THEMES
.stream()
.map(theme -> theme.get(key))
.filter(Objects::nonNull)
.findFirst()
.map(holder -> holder.lookupKey == null ? holder : resolve(holder.lookupKey))
.orElse(ColorHolder.MISSING);
}
@Nonnull public static Couple<Color> p(@Nonnull Key key) {return p(key.get());}
@Nonnull public static Couple<Color> p(String key) {return resolve(key).asPair();}
@Nonnull public static Color c(@Nonnull Key key, boolean first) {return c(key.get(), first);}
@Nonnull public static Color c(String key, boolean first) {return p(key).get(first);}
public static int i(@Nonnull Key key, boolean first) {return i(key.get(), first);}
public static int i(String key, boolean first) {return p(key).get(first).getRGB();}
@Nonnull public static Color c(@Nonnull Key key) {return c(key.get());}
@Nonnull public static Color c(String key) {return resolve(key).get();}
public static int i(@Nonnull Key key) {return i(key.get());}
public static int i(String key) {return resolve(key).get().getRGB();}
//-----------//
protected final Map<String, ColorHolder> colors;
private int priority = 0;
protected Theme() {
colors = new HashMap<>();
init();
}
/*
* Small note to addons: if you also want to make use of Theme,
* and add new Keys, please do not use mixins. Instead make a Theme
* subclass, override init and apply it via the static #addTheme
*
**/
protected void init() {
put(Key.BUTTON_IDLE, new Color(0xdd_8ab6d6, true), new Color(0x90_8ab6d6, true));
put(Key.BUTTON_HOVER, new Color(0xff_9ABBD3, true), new Color(0xd0_9ABBD3, true));
put(Key.BUTTON_CLICK, new Color(0xff_ffffff), new Color(0xee_ffffff));
put(Key.BUTTON_DISABLE, new Color(0x80_909090, true), new Color(0x60_909090, true));
put(Key.BUTTON_SUCCESS, new Color(0xcc_88f788, true), new Color(0xcc_20cc20, true));
put(Key.BUTTON_FAIL, new Color(0xcc_f78888, true), new Color(0xcc_cc2020, true));
put(Key.TEXT, new Color(0xff_eeeeee), new Color(0xff_a3a3a3));
put(Key.TEXT_DARKER, new Color(0xff_a3a3a3), new Color(0xff_808080));
put(Key.TEXT_ACCENT_STRONG, new Color(0xff_8ab6d6), new Color(0xff_8ab6d6));
put(Key.TEXT_ACCENT_SLIGHT, new Color(0xff_ddeeff), new Color(0xff_a0b0c0));
put(Key.STREAK, new Color(0x101010, false));
put(Key.VANILLA_TOOLTIP_BORDER, new Color(0x50_5000ff, true), new Color(0x50_28007f, true));
put(Key.VANILLA_TOOLTIP_BACKGROUND, new Color(0xf0_100010, true));
put(Key.PONDER_BUTTON_IDLE, new Color(0x60_c0c0ff, true), new Color(0x30_c0c0ff, true));
put(Key.PONDER_BUTTON_HOVER, new Color(0xf0_c0c0ff, true), new Color(0xa0_c0c0ff, true));
put(Key.PONDER_BUTTON_CLICK, new Color(0xff_ffffff), new Color(0xdd_ffffff));
put(Key.PONDER_BUTTON_DISABLE, new Color(0x80_909090, true), new Color(0x20_909090, true));
put(Key.PONDER_BACKGROUND_TRANSPARENT, new Color(0xdd_000000, true));
put(Key.PONDER_BACKGROUND_FLAT, new Color(0xff_000000, false));
put(Key.PONDER_BACKGROUND_IMPORTANT, new Color(0xdd_0e0e20, true));
put(Key.PONDER_IDLE, new Color(0x40ffeedd, true), new Color(0x20ffeedd, true));
put(Key.PONDER_HOVER, new Color(0x70ffffff, true), new Color(0x30ffffff, true));
put(Key.PONDER_HIGHLIGHT, new Color(0xf0ffeedd, true), new Color(0x60ffeedd, true));
put(Key.TEXT_WINDOW_BORDER, new Color(0x607a6000, true), new Color(0x207a6000, true));
put(Key.PONDER_BACK_ARROW, new Color(0xf0aa9999, true), new Color(0x30aa9999, true));
put(Key.PONDER_PROGRESSBAR, new Color(0x80ffeedd, true), new Color(0x50ffeedd, true));
put(Key.PONDER_MISSING_CREATE, new Color(0x70_984500, true), new Color(0x70_692400, true));
//put(Key.PONDER_MISSING_VANILLA, new Color(0x50_5000ff, true), new Color(0x50_300077, true));
lookup(Key.PONDER_MISSING_VANILLA, Key.VANILLA_TOOLTIP_BORDER);
put(Key.CONFIG_TITLE_A, new Color(0xffc69fbc, true), new Color(0xfff6b8bb, true));
put(Key.CONFIG_TITLE_B, new Color(0xfff6b8bb, true), new Color(0xfffbf994, true));
//put(Key., new Color(0x, true), new Color(0x, true));
}
protected void put(String key, Color c) {
colors.put(key, ColorHolder.single(c));
}
protected void put(Key key, Color c) {
put(key.get(), c);
}
protected void put(String key, Color c1, Color c2) {
colors.put(key, ColorHolder.pair(c1, c2));
}
protected void put(Key key, Color c1, Color c2) {
put(key.get(), c1, c2);
}
protected void lookup(Key key, Key source) {
colors.put(key.get(), ColorHolder.lookup(source.get()));
}
@Nullable protected ColorHolder get(String key) {
return colors.get(key);
}
public int getPriority() {
return priority;
}
public Theme setPriority(int priority) {
this.priority = priority;
return this;
}
public static class Key {
public static final Key BUTTON_IDLE = new Key();
public static final Key BUTTON_HOVER = new Key();
public static final Key BUTTON_CLICK = new Key();
public static final Key BUTTON_DISABLE = new Key();
public static final Key BUTTON_SUCCESS = new Key();
public static final Key BUTTON_FAIL = new Key();
public static final Key TEXT = new Key();
public static final Key TEXT_DARKER = new Key();
public static final Key TEXT_ACCENT_STRONG = new Key();
public static final Key TEXT_ACCENT_SLIGHT = new Key();
public static final Key STREAK = new Key();
public static final Key VANILLA_TOOLTIP_BORDER = new Key();
public static final Key VANILLA_TOOLTIP_BACKGROUND = new Key();
public static final Key PONDER_BACKGROUND_TRANSPARENT = new Key();
public static final Key PONDER_BACKGROUND_FLAT = new Key();
public static final Key PONDER_BACKGROUND_IMPORTANT = new Key();
public static final Key PONDER_IDLE = new Key();
public static final Key PONDER_HOVER = new Key();
public static final Key PONDER_HIGHLIGHT = new Key();
public static final Key TEXT_WINDOW_BORDER = new Key();
public static final Key PONDER_BACK_ARROW = new Key();
public static final Key PONDER_PROGRESSBAR = new Key();
public static final Key PONDER_MISSING_CREATE = new Key();
public static final Key PONDER_MISSING_VANILLA = new Key();
public static final Key PONDER_BUTTON_IDLE = new Key();
public static final Key PONDER_BUTTON_HOVER = new Key();
public static final Key PONDER_BUTTON_CLICK = new Key();
public static final Key PONDER_BUTTON_DISABLE = new Key();
public static final Key CONFIG_TITLE_A = new Key();
public static final Key CONFIG_TITLE_B = new Key();
private static int index = 0;
private final String s;
protected Key() {
this.s = "_" + index++;
}
protected Key(String s) {
this.s = s;
}
public String get() {
return s;
}
}
private static class ColorHolder {
private static final ColorHolder MISSING = ColorHolder.single(Color.BLACK);
private Couple<Color> colors;
private String lookupKey;
private static ColorHolder single(Color c) {
ColorHolder h = new ColorHolder();
h.colors = Couple.create(c.setImmutable(), c.setImmutable());
return h;
}
private static ColorHolder pair(Color first, Color second) {
ColorHolder h = new ColorHolder();
h.colors = Couple.create(first.setImmutable(), second.setImmutable());
return h;
}
private static ColorHolder lookup(String key) {
ColorHolder h = new ColorHolder();
h.lookupKey = key;
return h;
}
private Color get() {
return colors.getFirst();
}
private Couple<Color> asPair() {
return colors;
}
}
}

View File

@@ -0,0 +1,7 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import net.minecraft.client.gui.components.events.GuiEventListener;
public interface TickableGuiEventListener extends GuiEventListener {
void tick();
}

View File

@@ -0,0 +1,325 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.GlConst;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraftforge.client.gui.ScreenUtils;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import nl.requios.effortlessbuilding.create.foundation.utility.Couple;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
public class UIRenderHelper {
/**
* An FBO that has a stencil buffer for use wherever stencil are necessary. Forcing the main FBO to have a stencil
* buffer will cause GL error spam when using fabulous graphics.
*/
public static CustomRenderTarget framebuffer;
public static void init() {
RenderSystem.recordRenderCall(() -> {
Window mainWindow = Minecraft.getInstance().getWindow();
framebuffer = CustomRenderTarget.create(mainWindow);
});
}
public static void updateWindowSize(Window mainWindow) {
if (framebuffer != null)
framebuffer.resize(mainWindow.getWidth(), mainWindow.getHeight(), Minecraft.ON_OSX);
}
public static void drawFramebuffer(float alpha) {
framebuffer.renderWithAlpha(alpha);
}
/**
* Switch from src to dst, after copying the contents of src to dst.
*/
public static void swapAndBlitColor(RenderTarget src, RenderTarget dst) {
GlStateManager._glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, src.frameBufferId);
GlStateManager._glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, dst.frameBufferId);
GlStateManager._glBlitFrameBuffer(0, 0, src.viewWidth, src.viewHeight, 0, 0, dst.viewWidth, dst.viewHeight, GL30.GL_COLOR_BUFFER_BIT, GL20.GL_LINEAR);
GlStateManager._glBindFramebuffer(GlConst.GL_FRAMEBUFFER, dst.frameBufferId);
}
public static void streak(PoseStack ms, float angle, int x, int y, int breadth, int length) {
streak(ms, angle, x, y, breadth, length, Theme.i(Theme.Key.STREAK));
}
// angle in degrees; 0° -> fading to the right
// x and y specify the middle point of the starting edge
// breadth is the total width of the streak
public static void streak(PoseStack ms, float angle, int x, int y, int breadth, int length, int color) {
int a1 = 0xa0 << 24;
int a2 = 0x80 << 24;
int a3 = 0x10 << 24;
int a4 = 0x00 << 24;
color &= 0x00FFFFFF;
int c1 = a1 | color;
int c2 = a2 | color;
int c3 = a3 | color;
int c4 = a4 | color;
ms.pushPose();
ms.translate(x, y, 0);
ms.mulPose(Axis.ZP.rotationDegrees(angle - 90));
streak(ms, breadth / 2, length, c1, c2, c3, c4);
ms.popPose();
}
public static void streak(PoseStack ms, float angle, int x, int y, int breadth, int length, Color c) {
Color color = c.copy().setImmutable();
int c1 = color.scaleAlpha(0.625f).getRGB();
int c2 = color.scaleAlpha(0.5f).getRGB();
int c3 = color.scaleAlpha(0.0625f).getRGB();
int c4 = color.scaleAlpha(0f).getRGB();
ms.pushPose();
ms.translate(x, y, 0);
ms.mulPose(Axis.ZP.rotationDegrees(angle - 90));
streak(ms, breadth / 2, length, c1, c2, c3, c4);
ms.popPose();
}
private static void streak(PoseStack ms, int width, int height, int c1, int c2, int c3, int c4) {
double split1 = .5;
double split2 = .75;
Matrix4f model = ms.last().pose();
ScreenUtils.drawGradientRect(model, -width, 0, width, (int) (split1 * height), 0, c1, c2);
ScreenUtils.drawGradientRect(model, -width, (int) (split1 * height), width, (int) (split2 * height), 0, c2, c3);
ScreenUtils.drawGradientRect(model, -width, (int) (split2 * height), width, height, 0, c3, c4);
}
/**
* @see #angledGradient(PoseStack, float, int, int, int, int, int, Color, Color)
*/
public static void angledGradient(PoseStack ms, float angle, int x, int y, int breadth, int length, Couple<Color> c) {
angledGradient(ms, angle, x, y, 0, breadth, length, c);
}
/**
* @see #angledGradient(PoseStack, float, int, int, int, int, int, Color, Color)
*/
public static void angledGradient(PoseStack ms, float angle, int x, int y, int z, int breadth, int length, Couple<Color> c) {
angledGradient(ms, angle, x, y, z, breadth, length, c.getFirst(), c.getSecond());
}
/**
* @see #angledGradient(PoseStack, float, int, int, int, int, int, Color, Color)
*/
public static void angledGradient(PoseStack ms, float angle, int x, int y, int breadth, int length, Color color1, Color color2) {
angledGradient(ms, angle, x, y, 0, breadth, length, color1, color2);
}
/**
* x and y specify the middle point of the starting edge
*
* @param angle the angle of the gradient in degrees; 0° means from left to right
* @param color1 the color at the starting edge
* @param color2 the color at the ending edge
* @param breadth the total width of the gradient
*/
public static void angledGradient(PoseStack ms, float angle, int x, int y, int z, int breadth, int length, Color color1, Color color2) {
ms.pushPose();
ms.translate(x, y, z);
ms.mulPose(Axis.ZP.rotationDegrees(angle - 90));
int w = breadth / 2;
Matrix4f model = ms.last().pose();
ScreenUtils.drawGradientRect(model, -w, 0, w, length, 0, color1.getRGB(), color2.getRGB());
ms.popPose();
}
public static void breadcrumbArrow(PoseStack ms, int x, int y, int z, int width, int height, int indent, Couple<Color> colors) {breadcrumbArrow(ms, x, y, z, width, height, indent, colors.getFirst(), colors.getSecond());}
// draws a wide chevron-style breadcrumb arrow pointing left
public static void breadcrumbArrow(PoseStack ms, int x, int y, int z, int width, int height, int indent, Color startColor, Color endColor) {
ms.pushPose();
ms.translate(x - indent, y, z);
breadcrumbArrow(ms, width, height, indent, startColor, endColor);
ms.popPose();
}
private static void breadcrumbArrow(PoseStack ms, int width, int height, int indent, Color c1, Color c2) {
/*
* 0,0 x1,y1 ********************* x4,y4 ***** x7,y7
* **** ****
* **** ****
* x0,y0 x2,y2 x5,y5
* **** ****
* **** ****
* x3,y3 ********************* x6,y6 ***** x8,y8
*
*/
float x0 = 0, y0 = height / 2f;
float x1 = indent, y1 = 0;
float x2 = indent, y2 = height / 2f;
float x3 = indent, y3 = height;
float x4 = width, y4 = 0;
float x5 = width, y5 = height / 2f;
float x6 = width, y6 = height;
float x7 = indent + width, y7 = 0;
float x8 = indent + width, y8 = height;
indent = Math.abs(indent);
width = Math.abs(width);
Color fc1 = Color.mixColors(c1, c2, 0);
Color fc2 = Color.mixColors(c1, c2, (indent) / (width + 2f * indent));
Color fc3 = Color.mixColors(c1, c2, (indent + width) / (width + 2f * indent));
Color fc4 = Color.mixColors(c1, c2, 1);
// RenderSystem.disableTexture();
RenderSystem.enableBlend();
RenderSystem.disableCull();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
Tesselator tessellator = Tesselator.getInstance();
BufferBuilder bufferbuilder = tessellator.getBuilder();
Matrix4f model = ms.last().pose();
bufferbuilder.begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_COLOR);
bufferbuilder.vertex(model, x0, y0, 0).color(fc1.getRed(), fc1.getGreen(), fc1.getBlue(), fc1.getAlpha()).endVertex();
bufferbuilder.vertex(model, x1, y1, 0).color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), fc2.getAlpha()).endVertex();
bufferbuilder.vertex(model, x2, y2, 0).color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), fc2.getAlpha()).endVertex();
bufferbuilder.vertex(model, x0, y0, 0).color(fc1.getRed(), fc1.getGreen(), fc1.getBlue(), fc1.getAlpha()).endVertex();
bufferbuilder.vertex(model, x2, y2, 0).color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), fc2.getAlpha()).endVertex();
bufferbuilder.vertex(model, x3, y3, 0).color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), fc2.getAlpha()).endVertex();
bufferbuilder.vertex(model, x3, y3, 0).color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), fc2.getAlpha()).endVertex();
bufferbuilder.vertex(model, x1, y1, 0).color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), fc2.getAlpha()).endVertex();
bufferbuilder.vertex(model, x4, y4, 0).color(fc3.getRed(), fc3.getGreen(), fc3.getBlue(), fc3.getAlpha()).endVertex();
bufferbuilder.vertex(model, x3, y3, 0).color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), fc2.getAlpha()).endVertex();
bufferbuilder.vertex(model, x4, y4, 0).color(fc3.getRed(), fc3.getGreen(), fc3.getBlue(), fc3.getAlpha()).endVertex();
bufferbuilder.vertex(model, x6, y6, 0).color(fc3.getRed(), fc3.getGreen(), fc3.getBlue(), fc3.getAlpha()).endVertex();
bufferbuilder.vertex(model, x5, y5, 0).color(fc3.getRed(), fc3.getGreen(), fc3.getBlue(), fc3.getAlpha()).endVertex();
bufferbuilder.vertex(model, x4, y4, 0).color(fc3.getRed(), fc3.getGreen(), fc3.getBlue(), fc3.getAlpha()).endVertex();
bufferbuilder.vertex(model, x7, y7, 0).color(fc4.getRed(), fc4.getGreen(), fc4.getBlue(), fc4.getAlpha()).endVertex();
bufferbuilder.vertex(model, x6, y6, 0).color(fc3.getRed(), fc3.getGreen(), fc3.getBlue(), fc3.getAlpha()).endVertex();
bufferbuilder.vertex(model, x5, y5, 0).color(fc3.getRed(), fc3.getGreen(), fc3.getBlue(), fc3.getAlpha()).endVertex();
bufferbuilder.vertex(model, x8, y8, 0).color(fc4.getRed(), fc4.getGreen(), fc4.getBlue(), fc4.getAlpha()).endVertex();
tessellator.end();
RenderSystem.enableCull();
RenderSystem.disableBlend();
// RenderSystem.enableTexture();
}
//just like AbstractGui#drawTexture, but with a color at every vertex
public static void drawColoredTexture(PoseStack ms, Color c, int x, int y, int tex_left, int tex_top, int width, int height) {
drawColoredTexture(ms, c, x, y, 0, (float) tex_left, (float) tex_top, width, height, 256, 256);
}
public static void drawColoredTexture(PoseStack ms, Color c, int x, int y, int z, float tex_left, float tex_top, int width, int height, int sheet_width, int sheet_height) {
drawColoredTexture(ms, c, x, x + width, y, y + height, z, width, height, tex_left, tex_top, sheet_width, sheet_height);
}
public static void drawStretched(PoseStack ms, int left, int top, int w, int h, int z, AllGuiTextures tex) {
tex.bind();
drawTexturedQuad(ms.last()
.pose(), Color.WHITE, left, left + w, top, top + h, z, tex.startX / 256f, (tex.startX + tex.width) / 256f,
tex.startY / 256f, (tex.startY + tex.height) / 256f);
}
public static void drawCropped(PoseStack ms, int left, int top, int w, int h, int z, AllGuiTextures tex) {
tex.bind();
drawTexturedQuad(ms.last()
.pose(), Color.WHITE, left, left + w, top, top + h, z, tex.startX / 256f, (tex.startX + w) / 256f,
tex.startY / 256f, (tex.startY + h) / 256f);
}
private static void drawColoredTexture(PoseStack ms, Color c, int left, int right, int top, int bot, int z, int tex_width, int tex_height, float tex_left, float tex_top, int sheet_width, int sheet_height) {
drawTexturedQuad(ms.last().pose(), c, left, right, top, bot, z, (tex_left + 0.0F) / (float) sheet_width, (tex_left + (float) tex_width) / (float) sheet_width, (tex_top + 0.0F) / (float) sheet_height, (tex_top + (float) tex_height) / (float) sheet_height);
}
private static void drawTexturedQuad(Matrix4f m, Color c, int left, int right, int top, int bot, int z, float u1, float u2, float v1, float v2) {
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder bufferbuilder = tesselator.getBuilder();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionColorTexShader);
bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX);
bufferbuilder.vertex(m, (float) left , (float) bot, (float) z).color(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()).uv(u1, v2).endVertex();
bufferbuilder.vertex(m, (float) right, (float) bot, (float) z).color(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()).uv(u2, v2).endVertex();
bufferbuilder.vertex(m, (float) right, (float) top, (float) z).color(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()).uv(u2, v1).endVertex();
bufferbuilder.vertex(m, (float) left , (float) top, (float) z).color(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()).uv(u1, v1).endVertex();
tesselator.end();
RenderSystem.disableBlend();
}
public static void flipForGuiRender(PoseStack poseStack) {
poseStack.mulPoseMatrix(new Matrix4f().scaling(1, -1, 1));
}
public static class CustomRenderTarget extends RenderTarget {
public CustomRenderTarget(boolean useDepth) {
super(useDepth);
}
public static CustomRenderTarget create(Window mainWindow) {
CustomRenderTarget framebuffer = new CustomRenderTarget(true);
framebuffer.resize(mainWindow.getWidth(), mainWindow.getHeight(), Minecraft.ON_OSX);
framebuffer.setClearColor(0, 0, 0, 0);
framebuffer.enableStencil();
return framebuffer;
}
public void renderWithAlpha(float alpha) {
Window window = Minecraft.getInstance().getWindow();
float vx = (float) window.getGuiScaledWidth();
float vy = (float) window.getGuiScaledHeight();
float tx = (float) viewWidth / (float) width;
float ty = (float) viewHeight / (float) height;
// RenderSystem.enableTexture();
RenderSystem.enableDepthTest();
RenderSystem.setShader(() -> Minecraft.getInstance().gameRenderer.blitShader);
RenderSystem.getShader().setSampler("DiffuseSampler", colorTextureId);
bindRead();
Tesselator tessellator = Tesselator.getInstance();
BufferBuilder bufferbuilder = tessellator.getBuilder();
bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX);
bufferbuilder.vertex(0, vy, 0).color(1, 1, 1, alpha).uv(0, 0).endVertex();
bufferbuilder.vertex(vx, vy, 0).color(1, 1, 1, alpha).uv(tx, 0).endVertex();
bufferbuilder.vertex(vx, 0, 0).color(1, 1, 1, alpha).uv(tx, ty).endVertex();
bufferbuilder.vertex(0, 0, 0).color(1, 1, 1, alpha).uv(0, ty).endVertex();
tessellator.end();
unbindRead();
}
}
}

View File

@@ -0,0 +1,182 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.gui.screens.inventory.ContainerScreen;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.gui.TickableGuiEventListener;
import nl.requios.effortlessbuilding.create.foundation.gui.widget.AbstractSimiWidget;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@OnlyIn(Dist.CLIENT)
@ParametersAreNonnullByDefault
public abstract class AbstractSimiContainerScreen<T extends AbstractContainerMenu> extends AbstractContainerScreen<T> {
protected int windowXOffset, windowYOffset;
public AbstractSimiContainerScreen(T container, Inventory inv, Component title) {
super(container, inv, title);
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowSize(int width, int height) {
imageWidth = width;
imageHeight = height;
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowOffset(int xOffset, int yOffset) {
windowXOffset = xOffset;
windowYOffset = yOffset;
}
@Override
protected void init() {
super.init();
leftPos += windowXOffset;
topPos += windowYOffset;
}
@Override
protected void containerTick() {
for (GuiEventListener listener : children()) {
if (listener instanceof TickableGuiEventListener tickable) {
tickable.tick();
}
}
}
@SuppressWarnings("unchecked")
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(W... widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(Collection<W> widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected void removeWidgets(GuiEventListener... widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
protected void removeWidgets(Collection<? extends GuiEventListener> widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
@Override
public void render(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
partialTicks = minecraft.getFrameTime();
renderBackground(ms);
super.render(ms, mouseX, mouseY, partialTicks);
renderForeground(ms, mouseX, mouseY, partialTicks);
}
@Override
protected void renderLabels(PoseStack ms, int mouseX, int mouseY) {
// no-op to prevent screen- and inventory-title from being rendered at incorrect
// location
// could also set this.titleX/Y and this.playerInventoryTitleX/Y to the proper
// values instead
}
protected void renderForeground(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
renderTooltip(ms, mouseX, mouseY);
for (Renderable widget : renderables) {
if (widget instanceof AbstractSimiWidget simiWidget && simiWidget.isMouseOver(mouseX, mouseY)) {
List<Component> tooltip = simiWidget.getToolTip();
if (tooltip.isEmpty())
continue;
int ttx = simiWidget.lockedTooltipX == -1 ? mouseX : simiWidget.lockedTooltipX + simiWidget.getX();
int tty = simiWidget.lockedTooltipY == -1 ? mouseY : simiWidget.lockedTooltipY + simiWidget.getY();
renderComponentTooltip(ms, tooltip, ttx, tty);
}
}
}
public int getLeftOfCentered(int textureWidth) {
return leftPos - windowXOffset + (imageWidth - textureWidth) / 2;
}
// public void renderPlayerInventory(PoseStack graphics, int x, int y) {
// AllGuiTextures.PLAYER_INVENTORY.render(graphics, x, y);
// graphics.drawString(font, playerInventoryTitle, x + 8, y + 6, 0x404040, false);
// }
@Override
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
InputConstants.Key mouseKey = InputConstants.getKey(pKeyCode, pScanCode);
if (getFocused() instanceof EditBox && this.minecraft.options.keyInventory.isActiveAndMatches(mouseKey))
return false;
return super.keyPressed(pKeyCode, pScanCode, pModifiers);
}
@Override
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (getFocused() != null && !getFocused().isMouseOver(pMouseX, pMouseY))
setFocused(null);
return super.mouseClicked(pMouseX, pMouseY, pButton);
}
@Override
public GuiEventListener getFocused() {
GuiEventListener focused = super.getFocused();
if (focused instanceof AbstractWidget && !((AbstractWidget) focused).isFocused())
focused = null;
setFocused(focused);
return focused;
}
/**
* Used for moving JEI out of the way of extra things like block renders.
*
* @return the space that the GUI takes up outside the normal rectangle defined
* by {@link ContainerScreen}.
*/
public List<Rect2i> getExtraAreas() {
return Collections.emptyList();
}
@Deprecated
protected void debugWindowArea(PoseStack ms) {
fill(ms, leftPos + imageWidth, topPos + imageHeight, leftPos, topPos, 0xD3D3D3D3);
}
@Deprecated
protected void debugExtraAreas(PoseStack ms) {
for (Rect2i area : getExtraAreas()) {
fill(ms, area.getX() + area.getWidth(), area.getY() + area.getHeight(), area.getX(), area.getY(),
0xD3D3D3D3);
}
}
}

View File

@@ -0,0 +1,30 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent.Context;
import nl.requios.effortlessbuilding.create.foundation.networking.SimplePacketBase;
public class ClearMenuPacket extends SimplePacketBase {
public ClearMenuPacket() {}
public ClearMenuPacket(FriendlyByteBuf buffer) {}
@Override
public void write(FriendlyByteBuf buffer) {}
@Override
public boolean handle(Context context) {
context.enqueueWork(() -> {
ServerPlayer player = context.getSender();
if (player == null)
return;
if (!(player.containerMenu instanceof IClearableMenu))
return;
((IClearableMenu) player.containerMenu).clearContents();
});
return true;
}
}

View File

@@ -0,0 +1,108 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
public abstract class GhostItemMenu<T> extends MenuBase<T> implements IClearableMenu {
public ItemStackHandler ghostInventory;
protected GhostItemMenu(MenuType<?> type, int id, Inventory inv, FriendlyByteBuf extraData) {
super(type, id, inv, extraData);
}
protected GhostItemMenu(MenuType<?> type, int id, Inventory inv, T contentHolder) {
super(type, id, inv, contentHolder);
}
protected abstract ItemStackHandler createGhostInventory();
protected abstract boolean allowRepeats();
@Override
protected void initAndReadInventory(T contentHolder) {
ghostInventory = createGhostInventory();
}
@Override
public void clearContents() {
for (int i = 0; i < ghostInventory.getSlots(); i++)
ghostInventory.setStackInSlot(i, ItemStack.EMPTY);
}
@Override
public boolean canTakeItemForPickAll(ItemStack stack, Slot slotIn) {
return slotIn.container == playerInventory;
}
@Override
public boolean canDragTo(Slot slotIn) {
if (allowRepeats())
return true;
return slotIn.container == playerInventory;
}
@Override
public void clicked(int slotId, int dragType, ClickType clickTypeIn, Player player) {
if (slotId < 36) {
super.clicked(slotId, dragType, clickTypeIn, player);
return;
}
if (clickTypeIn == ClickType.THROW)
return;
ItemStack held = getCarried();
int slot = slotId - 36;
if (clickTypeIn == ClickType.CLONE) {
if (player.isCreative() && held.isEmpty()) {
ItemStack stackInSlot = ghostInventory.getStackInSlot(slot)
.copy();
stackInSlot.setCount(stackInSlot.getMaxStackSize());
setCarried(stackInSlot);
return;
}
return;
}
ItemStack insert;
if (held.isEmpty()) {
insert = ItemStack.EMPTY;
} else {
insert = held.copy();
insert.setCount(1);
}
ghostInventory.setStackInSlot(slot, insert);
getSlot(slotId).setChanged();
}
@Override
public ItemStack quickMoveStack(Player playerIn, int index) {
if (index < 36) {
ItemStack stackToInsert = playerInventory.getItem(index);
for (int i = 0; i < ghostInventory.getSlots(); i++) {
ItemStack stack = ghostInventory.getStackInSlot(i);
if (!allowRepeats() && ItemHandlerHelper.canItemStacksStack(stack, stackToInsert))
break;
if (stack.isEmpty()) {
ItemStack copy = stackToInsert.copy();
copy.setCount(1);
ghostInventory.insertItem(i, copy, false);
getSlot(i + 36).setChanged();
break;
}
}
} else {
ghostInventory.extractItem(index - 36, 1, false);
getSlot(index).setChanged();
}
return ItemStack.EMPTY;
}
}

View File

@@ -0,0 +1,45 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent.Context;
import nl.requios.effortlessbuilding.create.foundation.networking.SimplePacketBase;
public class GhostItemSubmitPacket extends SimplePacketBase {
private final ItemStack item;
private final int slot;
public GhostItemSubmitPacket(ItemStack item, int slot) {
this.item = item;
this.slot = slot;
}
public GhostItemSubmitPacket(FriendlyByteBuf buffer) {
item = buffer.readItem();
slot = buffer.readInt();
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeItem(item);
buffer.writeInt(slot);
}
@Override
public boolean handle(Context context) {
context.enqueueWork(() -> {
ServerPlayer player = context.getSender();
if (player == null)
return;
if (player.containerMenu instanceof GhostItemMenu<?> menu) {
menu.ghostInventory.setStackInSlot(slot, item);
menu.getSlot(36 + slot).setChanged();
}
});
return true;
}
}

View File

@@ -0,0 +1,11 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
public interface IClearableMenu {
default void sendClearPacket() {
// PacketHandler.INSTANCE.sendToServer(new ClearMenuPacket());
}
public void clearContents();
}

View File

@@ -0,0 +1,70 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.utility.IInteractionChecker;
public abstract class MenuBase<T> extends AbstractContainerMenu {
public Player player;
public Inventory playerInventory;
public T contentHolder;
protected MenuBase(MenuType<?> type, int id, Inventory inv, FriendlyByteBuf extraData) {
super(type, id);
init(inv, createOnClient(extraData));
}
protected MenuBase(MenuType<?> type, int id, Inventory inv, T contentHolder) {
super(type, id);
init(inv, contentHolder);
}
protected void init(Inventory inv, T contentHolderIn) {
player = inv.player;
playerInventory = inv;
contentHolder = contentHolderIn;
initAndReadInventory(contentHolder);
addSlots();
broadcastChanges();
}
@OnlyIn(Dist.CLIENT)
protected abstract T createOnClient(FriendlyByteBuf extraData);
protected abstract void initAndReadInventory(T contentHolder);
protected abstract void addSlots();
protected abstract void saveData(T contentHolder);
protected void addPlayerSlots(int x, int y) {
for (int hotbarSlot = 0; hotbarSlot < 9; ++hotbarSlot)
this.addSlot(new Slot(playerInventory, hotbarSlot, x + hotbarSlot * 18, y + 58));
for (int row = 0; row < 3; ++row)
for (int col = 0; col < 9; ++col)
this.addSlot(new Slot(playerInventory, col + row * 9 + 9, x + col * 18, y + row * 18));
}
@Override
public void removed(Player playerIn) {
super.removed(playerIn);
saveData(contentHolder);
}
@Override
public boolean stillValid(Player player) {
if (contentHolder == null)
return false;
if (contentHolder instanceof IInteractionChecker)
return ((IInteractionChecker) contentHolder).canPlayerUse(player);
return true;
}
}

View File

@@ -0,0 +1,157 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.GameRenderer;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import nl.requios.effortlessbuilding.create.foundation.utility.Couple;
import org.joml.Matrix4f;
public class BoxElement extends RenderElement {
protected Color background = new Color(0xff000000, true);
protected Color borderTop = new Color(0x40ffeedd, true);
protected Color borderBot = new Color(0x20ffeedd, true);
protected int borderOffset = 2;
public <T extends BoxElement> T withBackground(Color color) {
this.background = color;
//noinspection unchecked
return (T) this;
}
public <T extends BoxElement> T withBackground(int color) {
return withBackground(new Color(color, true));
}
public <T extends BoxElement> T flatBorder(Color color) {
this.borderTop = color;
this.borderBot = color;
//noinspection unchecked
return (T) this;
}
public <T extends BoxElement> T flatBorder(int color) {
return flatBorder(new Color(color, true));
}
public <T extends BoxElement> T gradientBorder(Couple<Color> colors) {
this.borderTop = colors.getFirst();
this.borderBot = colors.getSecond();
//noinspection unchecked
return (T) this;
}
public <T extends BoxElement> T gradientBorder(Color top, Color bot) {
this.borderTop = top;
this.borderBot = bot;
//noinspection unchecked
return (T) this;
}
public <T extends BoxElement> T gradientBorder(int top, int bot) {
return gradientBorder(new Color(top, true), new Color(bot, true));
}
public <T extends BoxElement> T withBorderOffset(int offset) {
this.borderOffset = offset;
//noinspection unchecked
return (T) this;
}
@Override
public void render(PoseStack ms) {
renderBox(ms);
}
//total box width = 1 * 2 (outer border) + 1 * 2 (inner color border) + 2 * borderOffset + width
//defaults to 2 + 2 + 4 + 16 = 24px
//batch everything together to save a bunch of gl calls over ScreenUtils
protected void renderBox(PoseStack ms) {
/*
* _____________
* _|_____________|_
* | | ___________ | |
* | | | | | | |
* | | | | | | |
* | | |--* | | | |
* | | | h | | |
* | | | --w-+ | | |
* | | | | | |
* | | |_________| | |
* |_|_____________|_|
* |_____________|
*
* */
// RenderSystem.disableTexture();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
Matrix4f model = ms.last().pose();
int f = borderOffset;
Color c1 = background.copy().scaleAlpha(alpha);
Color c2 = borderTop.copy().scaleAlpha(alpha);
Color c3 = borderBot.copy().scaleAlpha(alpha);
Tesselator tessellator = Tesselator.getInstance();
BufferBuilder b = tessellator.getBuilder();
b.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
//outer top
b.vertex(model, x - f - 1 , y - f - 2 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y - f - 2 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
//outer left
b.vertex(model, x - f - 2 , y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x - f - 2 , y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
//outer bottom
b.vertex(model, x - f - 1 , y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y + f + 2 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y + f + 2 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
//outer right
b.vertex(model, x + f + 1 + width, y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 2 + width, y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 2 + width, y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
//inner background - also render behind the inner edges
b.vertex(model, x - f - 1 , y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y + f + 1 + height, z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y - f - 1 , z).color(c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha()).endVertex();
tessellator.end();
b.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
//inner top - includes corners
b.vertex(model, x - f - 1 , y - f - 1 , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y - f , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y - f , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y - f - 1 , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
//inner left - excludes corners
b.vertex(model, x - f - 1 , y - f , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y + f + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
b.vertex(model, x - f , y + f + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
b.vertex(model, x - f , y - f , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
//inner bottom - includes corners
b.vertex(model, x - f - 1 , y + f + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
b.vertex(model, x - f - 1 , y + f + 1 + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y + f + 1 + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y + f + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
//inner right - excludes corners
b.vertex(model, x + f + width, y - f , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
b.vertex(model, x + f + width, y + f + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y + f + height, z).color(c3.getRed(), c3.getGreen(), c3.getBlue(), c3.getAlpha()).endVertex();
b.vertex(model, x + f + 1 + width, y - f , z).color(c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha()).endVertex();
tessellator.end();
RenderSystem.disableBlend();
// RenderSystem.enableTexture();
}
}

View File

@@ -0,0 +1,79 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.vertex.PoseStack;
public class CombinedStencilElement extends StencilElement {
private StencilElement element1;
private StencilElement element2;
private ElementMode mode;
private CombinedStencilElement() {}
public static CombinedStencilElement of(@Nonnull StencilElement element1, @Nonnull StencilElement element2) {
return of(element1, element2, ElementMode.FIRST);
}
public static CombinedStencilElement of(@Nonnull StencilElement element1, @Nonnull StencilElement element2, ElementMode mode) {
CombinedStencilElement e = new CombinedStencilElement();
e.element1 = element1;
e.element2 = element2;
e.mode = mode;
return e;
}
public <T extends CombinedStencilElement> T withFirst(StencilElement element) {
this.element1 = element;
//noinspection unchecked
return (T) this;
}
public <T extends CombinedStencilElement> T withSecond(StencilElement element) {
this.element2 = element;
//noinspection unchecked
return (T) this;
}
public <T extends CombinedStencilElement> T withMode(ElementMode mode) {
this.mode = mode;
//noinspection unchecked
return (T) this;
}
@Override
protected void renderStencil(PoseStack ms) {
ms.pushPose();
element1.transform(ms);
element1.withBounds(width, height);
element1.renderStencil(ms);
ms.popPose();
ms.pushPose();
element2.transform(ms);
element2.withBounds(width, height);
element2.renderStencil(ms);
ms.popPose();
}
@Override
protected void renderElement(PoseStack graphics) {
if (mode.rendersFirst())
element1.<StencilElement>withBounds(width, height).renderElement(graphics);
if (mode.rendersSecond())
element2.<StencilElement>withBounds(width, height).renderElement(graphics);
}
public enum ElementMode {
FIRST, SECOND, BOTH;
boolean rendersFirst() {
return this == FIRST || this == BOTH;
}
boolean rendersSecond() {
return this == SECOND || this == BOTH;
}
}
}

View File

@@ -0,0 +1,52 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import com.mojang.blaze3d.vertex.PoseStack;
import nl.requios.effortlessbuilding.create.foundation.gui.UIRenderHelper;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
public class DelegatedStencilElement extends StencilElement {
protected static final ElementRenderer EMPTY_RENDERER = (graphics, width, height, alpha) -> {};
protected static final ElementRenderer DEFAULT_ELEMENT = (graphics, width, height, alpha) -> UIRenderHelper.angledGradient(graphics, 0, -3, 5, height+4, width+6, new Color(0xff_10dd10).scaleAlpha(alpha), new Color(0xff_1010dd).scaleAlpha(alpha));
protected ElementRenderer stencil;
protected ElementRenderer element;
public DelegatedStencilElement() {
stencil = EMPTY_RENDERER;
element = DEFAULT_ELEMENT;
}
public DelegatedStencilElement(ElementRenderer stencil, ElementRenderer element) {
this.stencil = stencil;
this.element = element;
}
public <T extends DelegatedStencilElement> T withStencilRenderer(ElementRenderer renderer) {
stencil = renderer;
//noinspection unchecked
return (T) this;
}
public <T extends DelegatedStencilElement> T withElementRenderer(ElementRenderer renderer) {
element = renderer;
//noinspection unchecked
return (T) this;
}
@Override
protected void renderStencil(PoseStack ms) {
stencil.render(ms, width, height, 1);
}
@Override
protected void renderElement(PoseStack ms) {
element.render(ms, width, height, alpha);
}
@FunctionalInterface
public interface ElementRenderer {
void render(PoseStack ms, int width, int height, float alpha);
}
}

View File

@@ -0,0 +1,298 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.model.ModelUtil;
import com.mojang.blaze3d.platform.GlStateManager.DestFactor;
import com.mojang.blaze3d.platform.GlStateManager.SourceFactor;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.BaseFireBlock;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.client.RenderTypeHelper;
import nl.requios.effortlessbuilding.create.foundation.gui.ILightingSettings;
import nl.requios.effortlessbuilding.create.foundation.gui.UIRenderHelper;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import nl.requios.effortlessbuilding.create.foundation.utility.VecHelper;
import javax.annotation.Nullable;
public class GuiGameElement {
public static GuiRenderBuilder of(ItemStack stack) {
return new GuiItemRenderBuilder(stack);
}
public static GuiRenderBuilder of(ItemLike itemProvider) {
return new GuiItemRenderBuilder(itemProvider);
}
public static GuiRenderBuilder of(BlockState state) {
return new GuiBlockStateRenderBuilder(state);
}
public static GuiRenderBuilder of(PartialModel partial) {
return new GuiBlockPartialRenderBuilder(partial);
}
public static GuiRenderBuilder of(Fluid fluid) {
return new GuiBlockStateRenderBuilder(fluid.defaultFluidState()
.createLegacyBlock()
.setValue(LiquidBlock.LEVEL, 0));
}
public static abstract class GuiRenderBuilder extends RenderElement {
protected double xLocal, yLocal, zLocal;
protected double xRot, yRot, zRot;
protected double scale = 1;
protected int color = 0xFFFFFF;
protected Vec3 rotationOffset = Vec3.ZERO;
protected ILightingSettings customLighting = null;
public GuiRenderBuilder atLocal(double x, double y, double z) {
this.xLocal = x;
this.yLocal = y;
this.zLocal = z;
return this;
}
public GuiRenderBuilder rotate(double xRot, double yRot, double zRot) {
this.xRot = xRot;
this.yRot = yRot;
this.zRot = zRot;
return this;
}
public GuiRenderBuilder rotateBlock(double xRot, double yRot, double zRot) {
return this.rotate(xRot, yRot, zRot)
.withRotationOffset(VecHelper.getCenterOf(BlockPos.ZERO));
}
public GuiRenderBuilder scale(double scale) {
this.scale = scale;
return this;
}
public GuiRenderBuilder color(int color) {
this.color = color;
return this;
}
public GuiRenderBuilder withRotationOffset(Vec3 offset) {
this.rotationOffset = offset;
return this;
}
public GuiRenderBuilder lighting(ILightingSettings lighting) {
customLighting = lighting;
return this;
}
protected void prepareMatrix(PoseStack matrixStack) {
matrixStack.pushPose();
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.enableDepthTest();
RenderSystem.enableBlend();
RenderSystem.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
prepareLighting(matrixStack);
}
protected void transformMatrix(PoseStack matrixStack) {
matrixStack.translate(x, y, z);
matrixStack.scale((float) scale, (float) scale, (float) scale);
matrixStack.translate(xLocal, yLocal, zLocal);
UIRenderHelper.flipForGuiRender(matrixStack);
matrixStack.translate(rotationOffset.x, rotationOffset.y, rotationOffset.z);
matrixStack.mulPose(Axis.ZP.rotationDegrees((float) zRot));
matrixStack.mulPose(Axis.XP.rotationDegrees((float) xRot));
matrixStack.mulPose(Axis.YP.rotationDegrees((float) yRot));
matrixStack.translate(-rotationOffset.x, -rotationOffset.y, -rotationOffset.z);
}
protected void cleanUpMatrix(PoseStack matrixStack) {
matrixStack.popPose();
cleanUpLighting(matrixStack);
}
protected void prepareLighting(PoseStack matrixStack) {
if (customLighting != null) {
customLighting.applyLighting();
} else {
Lighting.setupFor3DItems();
}
}
protected void cleanUpLighting(PoseStack matrixStack) {
if (customLighting != null) {
Lighting.setupFor3DItems();
}
}
}
private static class GuiBlockModelRenderBuilder extends GuiRenderBuilder {
protected BakedModel blockModel;
protected BlockState blockState;
public GuiBlockModelRenderBuilder(BakedModel blockmodel, @Nullable BlockState blockState) {
this.blockState = blockState == null ? Blocks.AIR.defaultBlockState() : blockState;
this.blockModel = blockmodel;
}
@Override
public void render(PoseStack matrixStack) {
prepareMatrix(matrixStack);
Minecraft mc = Minecraft.getInstance();
BlockRenderDispatcher blockRenderer = mc.getBlockRenderer();
MultiBufferSource.BufferSource buffer = mc.renderBuffers()
.bufferSource();
transformMatrix(matrixStack);
RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS);
renderModel(blockRenderer, buffer, matrixStack);
cleanUpMatrix(matrixStack);
}
protected void renderModel(BlockRenderDispatcher blockRenderer, MultiBufferSource.BufferSource buffer,
PoseStack ms) {
if (blockState.getBlock() == Blocks.AIR) {
RenderType renderType = Sheets.translucentCullBlockSheet();
blockRenderer.getModelRenderer()
.renderModel(ms.last(), buffer.getBuffer(renderType), blockState, blockModel, 1, 1, 1,
LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, ModelUtil.VIRTUAL_DATA, null);
} else {
int color = Minecraft.getInstance()
.getBlockColors()
.getColor(blockState, null, null, 0);
Color rgb = new Color(color == -1 ? this.color : color);
for (RenderType chunkType : blockModel.getRenderTypes(blockState, RandomSource.create(42L), ModelUtil.VIRTUAL_DATA)) {
RenderType renderType = RenderTypeHelper.getEntityRenderType(chunkType, true);
blockRenderer.getModelRenderer()
.renderModel(ms.last(), buffer.getBuffer(renderType), blockState, blockModel,
rgb.getRedAsFloat(), rgb.getGreenAsFloat(), rgb.getBlueAsFloat(),
LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, ModelUtil.VIRTUAL_DATA, chunkType);
}
}
buffer.endBatch();
}
}
public static class GuiBlockStateRenderBuilder extends GuiBlockModelRenderBuilder {
public GuiBlockStateRenderBuilder(BlockState blockstate) {
super(Minecraft.getInstance()
.getBlockRenderer()
.getBlockModel(blockstate), blockstate);
}
@Override
protected void renderModel(BlockRenderDispatcher blockRenderer, MultiBufferSource.BufferSource buffer,
PoseStack ms) {
if (blockState.getBlock() instanceof BaseFireBlock) {
Lighting.setupForFlatItems();
super.renderModel(blockRenderer, buffer, ms);
Lighting.setupFor3DItems();
return;
}
super.renderModel(blockRenderer, buffer, ms);
if (blockState.getFluidState()
.isEmpty())
return;
// FluidRenderer.renderFluidBox(new FluidStack(blockState.getFluidState()
// .getType(), 1000), 0, 0, 0, 1, 1, 1, buffer, ms, LightTexture.FULL_BRIGHT, false);
// buffer.endBatch();
}
}
public static class GuiBlockPartialRenderBuilder extends GuiBlockModelRenderBuilder {
public GuiBlockPartialRenderBuilder(PartialModel partial) {
super(partial.get(), null);
}
}
public static class GuiItemRenderBuilder extends GuiRenderBuilder {
private final ItemStack stack;
public GuiItemRenderBuilder(ItemStack stack) {
this.stack = stack;
}
public GuiItemRenderBuilder(ItemLike provider) {
this(new ItemStack(provider));
}
@Override
public void render(PoseStack matrixStack) {
prepareMatrix(matrixStack);
transformMatrix(matrixStack);
renderItemIntoGUI(matrixStack, stack, customLighting == null);
cleanUpMatrix(matrixStack);
}
public static void renderItemIntoGUI(PoseStack matrixStack, ItemStack stack, boolean useDefaultLighting) {
ItemRenderer renderer = Minecraft.getInstance().getItemRenderer();
BakedModel bakedModel = renderer.getModel(stack, null, null, 0);
renderer.textureManager.getTexture(InventoryMenu.BLOCK_ATLAS).setFilter(false, false);
RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS);
RenderSystem.enableBlend();
RenderSystem.enableCull();
RenderSystem.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
matrixStack.pushPose();
matrixStack.translate(0, 0, 100.0F);
matrixStack.translate(8.0F, -8.0F, 0.0F);
matrixStack.scale(16.0F, 16.0F, 16.0F);
MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();
boolean flatLighting = !bakedModel.usesBlockLight();
if (useDefaultLighting && flatLighting) {
Lighting.setupForFlatItems();
}
renderer.render(stack, ItemDisplayContext.GUI, false, matrixStack, buffer, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, bakedModel);
RenderSystem.disableDepthTest();
buffer.endBatch();
RenderSystem.enableDepthTest();
if (useDefaultLighting && flatLighting) {
Lighting.setupFor3DItems();
}
matrixStack.popPose();
}
}
}

View File

@@ -0,0 +1,89 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import com.mojang.blaze3d.vertex.PoseStack;
public abstract class RenderElement implements ScreenElement {
public static final RenderElement EMPTY = new RenderElement() {
@Override
public void render(PoseStack ms) {
}
};
public static RenderElement of(ScreenElement renderable) {
return new SimpleRenderElement(renderable);
}
protected int width = 16, height = 16;
protected float x = 0, y = 0, z = 0;
protected float alpha = 1f;
public <T extends RenderElement> T at(float x, float y) {
this.x = x;
this.y = y;
//noinspection unchecked
return (T) this;
}
public <T extends RenderElement> T at(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
//noinspection unchecked
return (T) this;
}
public <T extends RenderElement> T withBounds(int width, int height) {
this.width = width;
this.height = height;
//noinspection unchecked
return (T) this;
}
public <T extends RenderElement> T withAlpha(float alpha) {
this.alpha = alpha;
//noinspection unchecked
return (T) this;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getZ() {
return z;
}
public abstract void render(PoseStack ms);
@Override
public void render(PoseStack ms, int x, int y) {
this.at(x, y).render(ms);
}
public static class SimpleRenderElement extends RenderElement {
private ScreenElement renderable;
public SimpleRenderElement(ScreenElement renderable) {
this.renderable = renderable;
}
@Override
public void render(PoseStack ms) {
renderable.render(ms, (int) x, (int) y);
}
}
}

View File

@@ -0,0 +1,12 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public interface ScreenElement {
@OnlyIn(Dist.CLIENT)
void render(PoseStack poseStack, int x, int y);
}

View File

@@ -0,0 +1,52 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
public abstract class StencilElement extends RenderElement {
@Override
public void render(PoseStack ms) {
ms.pushPose();
transform(ms);
prepareStencil(ms);
renderStencil(ms);
prepareElement(ms);
renderElement(ms);
cleanUp(ms);
ms.popPose();
}
protected abstract void renderStencil(PoseStack graphics);
protected abstract void renderElement(PoseStack graphics);
protected void transform(PoseStack ms) {
ms.translate(x, y, z);
}
protected void prepareStencil(PoseStack ms) {
GL11.glDisable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(~0);
RenderSystem.clear(GL11.GL_STENCIL_BUFFER_BIT, Minecraft.ON_OSX);
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilOp(GL11.GL_REPLACE, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_NEVER, 1, 0xFF);
}
protected void prepareElement(PoseStack ms) {
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
}
protected void cleanUp(PoseStack ms) {
GL11.glDisable(GL11.GL_STENCIL_TEST);
}
}

View File

@@ -0,0 +1,77 @@
package nl.requios.effortlessbuilding.create.foundation.gui.element;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
public class TextStencilElement extends DelegatedStencilElement {
protected Font font;
protected MutableComponent component;
protected boolean centerVertically = false;
protected boolean centerHorizontally = false;
public TextStencilElement(Font font) {
super();
this.font = font;
height = 10;
}
public TextStencilElement(Font font, String text) {
this(font);
component = Components.literal(text);
}
public TextStencilElement(Font font, MutableComponent component) {
this(font);
this.component = component;
}
public TextStencilElement withText(String text) {
component = Components.literal(text);
return this;
}
public TextStencilElement withText(MutableComponent component) {
this.component = component;
return this;
}
public TextStencilElement centered(boolean vertical, boolean horizontal) {
this.centerVertically = vertical;
this.centerHorizontally = horizontal;
return this;
}
@Override
protected void renderStencil(PoseStack ms) {
float x = 0, y = 0;
if (centerHorizontally)
x = width / 2f - font.width(component) / 2f;
if (centerVertically)
y = height / 2f - (font.lineHeight - 1) / 2f;
font.draw(ms, component, Math.round(x), Math.round(y), 0xff_000000);
}
@Override
protected void renderElement(PoseStack ms) {
float x = 0, y = 0;
if (centerHorizontally)
x = width / 2f - font.width(component) / 2f;
if (centerVertically)
y = height / 2f - (font.lineHeight - 1) / 2f;
ms.pushPose();
ms.translate(x, y, 0);
element.render(ms, font.width(component), font.lineHeight + 2, alpha);
ms.popPose();
}
public MutableComponent getComponent() {
return component;
}
}

View File

@@ -0,0 +1,102 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner;
import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner;
import net.minecraft.network.chat.Component;
import nl.requios.effortlessbuilding.create.foundation.gui.TickableGuiEventListener;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import javax.annotation.Nonnull;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiConsumer;
public abstract class AbstractSimiWidget extends AbstractWidget implements TickableGuiEventListener {
public static final int HEADER_RGB = 0x5391E1;
public static final int HINT_RGB = 0x96B7E0;
protected float z;
protected boolean wasHovered = false;
protected List<Component> toolTip = new LinkedList<>();
protected BiConsumer<Integer, Integer> onClick = (_$, _$$) -> {};
public int lockedTooltipX = -1;
public int lockedTooltipY = -1;
protected AbstractSimiWidget(int x, int y) {
this(x, y, 16, 16);
}
protected AbstractSimiWidget(int x, int y, int width, int height) {
this(x, y, width, height, Components.immutableEmpty());
}
protected AbstractSimiWidget(int x, int y, int width, int height, Component message) {
super(x, y, width, height, message);
}
@Override
protected ClientTooltipPositioner createTooltipPositioner() {
return DefaultTooltipPositioner.INSTANCE;
}
public <T extends AbstractSimiWidget> T withCallback(BiConsumer<Integer, Integer> cb) {
this.onClick = cb;
//noinspection unchecked
return (T) this;
}
public <T extends AbstractSimiWidget> T withCallback(Runnable cb) {
return withCallback((_$, _$$) -> cb.run());
}
public <T extends AbstractSimiWidget> T atZLevel(float z) {
this.z = z;
//noinspection unchecked
return (T) this;
}
public List<Component> getToolTip() {
return toolTip;
}
@Override
public void tick() {}
@Override
public void renderWidget(@Nonnull PoseStack ms, int mouseX, int mouseY, float partialTicks) {
beforeRender(ms, mouseX, mouseY, partialTicks);
doRender(ms, mouseX, mouseY, partialTicks);
afterRender(ms, mouseX, mouseY, partialTicks);
wasHovered = isHoveredOrFocused();
}
protected void beforeRender(@Nonnull PoseStack ms, int mouseX, int mouseY, float partialTicks) {
ms.pushPose();
}
protected void doRender(@Nonnull PoseStack ms, int mouseX, int mouseY, float partialTicks) {
}
protected void afterRender(@Nonnull PoseStack ms, int mouseX, int mouseY, float partialTicks) {
ms.popPose();
}
public void runCallback(double mouseX, double mouseY) {
onClick.accept((int) mouseX, (int) mouseY);
}
@Override
public void onClick(double mouseX, double mouseY) {
runCallback(mouseX, mouseY);
}
@Override
public void updateWidgetNarration(NarrationElementOutput pNarrationElementOutput) {
defaultButtonNarrationText(pNarrationElementOutput);
}
}

View File

@@ -0,0 +1,226 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.mojang.blaze3d.vertex.PoseStack;
import nl.requios.effortlessbuilding.create.foundation.gui.Theme;
import nl.requios.effortlessbuilding.create.foundation.gui.UIRenderHelper;
import nl.requios.effortlessbuilding.create.foundation.gui.element.BoxElement;
import nl.requios.effortlessbuilding.create.foundation.gui.element.DelegatedStencilElement;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import nl.requios.effortlessbuilding.create.foundation.utility.Couple;
import nl.requios.effortlessbuilding.create.foundation.utility.animation.LerpedFloat;
import java.util.function.Function;
public class BoxWidget extends ElementWidget {
public static final Function<BoxWidget, DelegatedStencilElement.ElementRenderer> gradientFactory = (box) -> (ms, w, h, alpha) -> UIRenderHelper.angledGradient(ms, 90, w/2, -2, w + 4, h + 4, box.gradientColor1, box.gradientColor2);
protected BoxElement box;
protected Color customBorderTop;
protected Color customBorderBot;
protected Color customBackground;
protected boolean animateColors = true;
protected LerpedFloat colorAnimation = LerpedFloat.linear();
protected Color gradientColor1, gradientColor2;
private Color previousColor1, previousColor2;
private Color colorTarget1 = Theme.c(getIdleTheme(), true).copy();
private Color colorTarget2 = Theme.c(getIdleTheme(), false).copy();
public BoxWidget() {
this(0, 0);
}
public BoxWidget(int x, int y) {
this(x, y, 16, 16);
}
public BoxWidget(int x, int y, int width, int height) {
super(x, y, width, height);
box = new BoxElement()
.at(x, y)
.withBounds(width, height);
gradientColor1 = colorTarget1;
gradientColor2 = colorTarget2;
}
public <T extends BoxWidget> T withBounds(int width, int height) {
this.width = width;
this.height = height;
//noinspection unchecked
return (T) this;
}
public <T extends BoxWidget> T withBorderColors(Couple<Color> colors) {
this.customBorderTop = colors.getFirst();
this.customBorderBot = colors.getSecond();
updateColorsFromState();
//noinspection unchecked
return (T) this;
}
public <T extends BoxWidget> T withBorderColors(Color top, Color bot) {
this.customBorderTop = top;
this.customBorderBot = bot;
updateColorsFromState();
//noinspection unchecked
return (T) this;
}
public <T extends BoxWidget> T withCustomBackground(Color color) {
this.customBackground = color;
//noinspection unchecked
return (T) this;
}
public <T extends BoxWidget> T animateColors(boolean b) {
this.animateColors = b;
//noinspection unchecked
return (T) this;
}
@Override
public void tick() {
super.tick();
colorAnimation.tickChaser();
}
@Override
public void onClick(double x, double y) {
super.onClick(x, y);
gradientColor1 = Theme.c(getClickTheme(), true);
gradientColor2 = Theme.c(getClickTheme(), false);
startGradientAnimation(getColorForState(true), getColorForState(false), true, 0.15);
}
@Override
protected void beforeRender(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
super.beforeRender(ms, mouseX, mouseY, partialTicks);
if (isHovered != wasHovered) {
startGradientAnimation(
getColorForState(true),
getColorForState(false),
isHovered
);
}
if (colorAnimation.settled()) {
gradientColor1 = colorTarget1;
gradientColor2 = colorTarget2;
} else {
float animationValue = 1 - Math.abs(colorAnimation.getValue(partialTicks));
gradientColor1 = Color.mixColors(previousColor1, colorTarget1, animationValue);
gradientColor2 = Color.mixColors(previousColor2, colorTarget2, animationValue);
}
}
@Override
public void doRender(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
float fadeValue = fade.getValue(partialTicks);
if (fadeValue < .1f)
return;
box.withAlpha(fadeValue);
box.withBackground(customBackground != null ? customBackground : Theme.c(Theme.Key.PONDER_BACKGROUND_TRANSPARENT))
.gradientBorder(gradientColor1, gradientColor2)
.at(getX(), getY(), z)
.withBounds(width, height)
.render(ms);
super.doRender(ms, mouseX, mouseY, partialTicks);
wasHovered = isHovered;
}
@Override
public boolean isMouseOver(double mX, double mY) {
if (!active || !visible)
return false;
float padX = 2 + paddingX;
float padY = 2 + paddingY;
return getX() - padX <= mX && getY() - padY <= mY && mX < getX() + padX + width && mY < getY() + padY + height;
}
@Override
protected boolean clicked(double pMouseX, double pMouseY) {
if (!active || !visible)
return false;
return isMouseOver(pMouseX, pMouseY);
}
public BoxElement getBox() {
return box;
}
public void updateColorsFromState() {
colorTarget1 = getColorForState(true);
colorTarget2 = getColorForState(false);
}
public void animateGradientFromState() {
startGradientAnimation(
getColorForState(true),
getColorForState(false),
true
);
}
private void startGradientAnimation(Color c1, Color c2, boolean positive, double expSpeed) {
if (!animateColors)
return;
colorAnimation.startWithValue(positive ? 1 : -1);
colorAnimation.chase(0, expSpeed, LerpedFloat.Chaser.EXP);
colorAnimation.tickChaser();
previousColor1 = gradientColor1;
previousColor2 = gradientColor2;
colorTarget1 = c1;
colorTarget2 = c2;
}
private void startGradientAnimation(Color c1, Color c2, boolean positive) {
startGradientAnimation(c1, c2, positive, 0.6);
}
private Color getColorForState(boolean first) {
if (!active)
return Theme.p(getDisabledTheme()).get(first);
if (isHovered) {
if (first)
return customBorderTop != null ? customBorderTop.darker() : Theme.c(getHoverTheme(), true);
else
return customBorderBot != null ? customBorderBot.darker() : Theme.c(getHoverTheme(), false);
}
if (first)
return customBorderTop != null ? customBorderTop : Theme.c(getIdleTheme(), true);
else
return customBorderBot != null ? customBorderBot : Theme.c(getIdleTheme(), false);
}
public Theme.Key getDisabledTheme() {
return Theme.Key.BUTTON_DISABLE;
}
public Theme.Key getIdleTheme() {
return Theme.Key.BUTTON_IDLE;
}
public Theme.Key getHoverTheme() {
return Theme.Key.BUTTON_HOVER;
}
public Theme.Key getClickTheme() {
return Theme.Key.BUTTON_CLICK;
}
}

View File

@@ -0,0 +1,155 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.mojang.blaze3d.vertex.PoseStack;
import nl.requios.effortlessbuilding.create.foundation.gui.element.RenderElement;
import nl.requios.effortlessbuilding.create.foundation.gui.element.ScreenElement;
import nl.requios.effortlessbuilding.create.foundation.utility.animation.LerpedFloat;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
public class ElementWidget extends AbstractSimiWidget {
protected RenderElement element = RenderElement.EMPTY;
protected boolean usesFade = false;
protected int fadeModX;
protected int fadeModY;
protected LerpedFloat fade = LerpedFloat.linear().startWithValue(1);
protected boolean rescaleElement = false;
protected float rescaleSizeX;
protected float rescaleSizeY;
protected float paddingX = 0;
protected float paddingY = 0;
public ElementWidget(int x, int y) {
super(x, y);
}
public ElementWidget(int x, int y, int width, int height) {
super(x, y, width, height);
}
public <T extends ElementWidget> T showingElement(RenderElement element) {
this.element = element;
//noinspection unchecked
return (T) this;
}
public <T extends ElementWidget> T showing(ScreenElement renderable) {
return this.showingElement(RenderElement.of(renderable));
}
public <T extends ElementWidget> T modifyElement(Consumer<RenderElement> consumer) {
if (element != null)
consumer.accept(element);
//noinspection unchecked
return (T) this;
}
public <T extends ElementWidget> T mapElement(UnaryOperator<RenderElement> function) {
if (element != null)
element = function.apply(element);
//noinspection unchecked
return (T) this;
}
public <T extends ElementWidget> T withPadding(float paddingX, float paddingY) {
this.paddingX = paddingX;
this.paddingY = paddingY;
//noinspection unchecked
return (T) this;
}
public <T extends ElementWidget> T enableFade(int fadeModifierX, int fadeModifierY) {
this.fade.startWithValue(0);
this.usesFade = true;
this.fadeModX = fadeModifierX;
this.fadeModY = fadeModifierY;
//noinspection unchecked
return (T) this;
}
public <T extends ElementWidget> T disableFade() {
this.fade.startWithValue(1);
this.usesFade = false;
//noinspection unchecked
return (T) this;
}
public LerpedFloat fade() {
return fade;
}
public <T extends ElementWidget> T fade(float target) {
fade.chase(target, 0.1, LerpedFloat.Chaser.EXP);
//noinspection unchecked
return (T) this;
}
/**
* Rescaling and its effects aren't properly tested with most elements.
* Thought it should work fine when using a TextStencilElement.
* Check BaseConfigScreen's title for such an example.
*/
@Deprecated
public <T extends ElementWidget> T rescaleElement(float rescaleSizeX, float rescaleSizeY) {
this.rescaleElement = true;
this.rescaleSizeX = rescaleSizeX;
this.rescaleSizeY = rescaleSizeY;
//noinspection unchecked
return (T) this;
}
public <T extends ElementWidget> T disableRescale() {
this.rescaleElement = false;
//noinspection unchecked
return (T) this;
}
@Override
public void tick() {
super.tick();
fade.tickChaser();
}
@Override
protected void beforeRender(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
super.beforeRender(ms, mouseX, mouseY, partialTicks);
isHovered = isMouseOver(mouseX, mouseY);
float fadeValue = fade.getValue(partialTicks);
element.withAlpha(fadeValue);
if (fadeValue < 1) {
ms.translate((1 - fadeValue) * fadeModX, (1 - fadeValue) * fadeModY, 0);
}
}
@Override
public void doRender(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
ms.pushPose();
ms.translate(getX() + paddingX, getY() + paddingY, z);
float innerWidth = width - 2 * paddingX;
float innerHeight = height - 2 * paddingY;
float eX = element.getX(), eY = element.getY();
if (rescaleElement) {
float xScale = innerWidth / rescaleSizeX;
float yScale = innerHeight / rescaleSizeY;
ms.scale(xScale, yScale, 1);
element.at(eX / xScale, eY / yScale);
innerWidth /= xScale;
innerHeight /= yScale;
}
element.withBounds((int) innerWidth, (int) innerHeight).render(ms);
ms.popPose();
if (rescaleElement) {
element.at(eX, eY);
}
}
public RenderElement getRenderElement() {
return element;
}
}

View File

@@ -0,0 +1,49 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.network.chat.Component;
import nl.requios.effortlessbuilding.create.foundation.gui.AllGuiTextures;
import nl.requios.effortlessbuilding.create.foundation.gui.element.ScreenElement;
public class IconButton extends AbstractSimiWidget {
protected ScreenElement icon;
public IconButton(int x, int y, ScreenElement icon) {
this(x, y, 18, 18, icon);
}
public IconButton(int x, int y, int w, int h, ScreenElement icon) {
super(x, y, w, h);
this.icon = icon;
}
@Override
public void doRender(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
if (visible) {
isHovered = mouseX >= getX() && mouseY >= getY() && mouseX < getX() + width && mouseY < getY() + height;
AllGuiTextures button = !active ? AllGuiTextures.BUTTON_DOWN
: isMouseOver(mouseX, mouseY) ? AllGuiTextures.BUTTON_HOVER : AllGuiTextures.BUTTON;
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
drawBg(ms, button);
icon.render(ms, getX() + 1, getY() + 1);
}
}
protected void drawBg(PoseStack ms, AllGuiTextures button) {
AllGuiTextures.BUTTON.bind();
blit(ms, getX(), getY(), button.startX, button.startY, button.width, button.height);
}
public void setToolTip(Component text) {
toolTip.clear();
toolTip.add(text);
}
public void setIcon(ScreenElement icon) {
this.icon = icon;
}
}

View File

@@ -0,0 +1,39 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.network.chat.Component;
import nl.requios.effortlessbuilding.create.foundation.gui.AllGuiTextures;
public class Indicator extends AbstractSimiWidget {
public State state;
public Indicator(int x, int y, Component tooltip) {
super(x, y, AllGuiTextures.INDICATOR.width, AllGuiTextures.INDICATOR.height);
this.toolTip = toolTip.isEmpty() ? ImmutableList.of() : ImmutableList.of(tooltip);
this.state = State.OFF;
}
@Override
public void render(PoseStack ms, int mouseX, int mouseY, float partialTicks ) {
if (!visible)
return;
AllGuiTextures toDraw;
switch (state) {
case ON: toDraw = AllGuiTextures.INDICATOR_WHITE; break;
case OFF: toDraw = AllGuiTextures.INDICATOR; break;
case RED: toDraw = AllGuiTextures.INDICATOR_RED; break;
case YELLOW: toDraw = AllGuiTextures.INDICATOR_YELLOW; break;
case GREEN: toDraw = AllGuiTextures.INDICATOR_GREEN; break;
default: toDraw = AllGuiTextures.INDICATOR; break;
}
toDraw.render(ms, getX(), getY());
}
public enum State {
OFF, ON,
RED, YELLOW, GREEN;
}
}

View File

@@ -0,0 +1,88 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import javax.annotation.Nonnull;
public class Label extends AbstractSimiWidget {
public Component text;
public String suffix;
protected boolean hasShadow;
protected int color;
protected Font font;
public Label(int x, int y, Component text) {
super(x, y, Minecraft.getInstance().font.width(text), 10);
font = Minecraft.getInstance().font;
this.text = Components.literal("Label");
color = 0xFFFFFF;
hasShadow = false;
suffix = "";
}
public Label colored(int color) {
this.color = color;
return this;
}
public Label withShadow() {
this.hasShadow = true;
return this;
}
public Label withSuffix(String s) {
suffix = s;
return this;
}
public void setTextAndTrim(Component newText, boolean trimFront, int maxWidthPx) {
Font fontRenderer = Minecraft.getInstance().font;
if (fontRenderer.width(newText) <= maxWidthPx) {
text = newText;
return;
}
String trim = "...";
int trimWidth = fontRenderer.width(trim);
String raw = newText.getString();
StringBuilder builder = new StringBuilder(raw);
int startIndex = trimFront ? 0 : raw.length() - 1;
int endIndex = !trimFront ? 0 : raw.length() - 1;
int step = (int) Math.signum(endIndex - startIndex);
for (int i = startIndex; i != endIndex; i += step) {
String sub = builder.substring(trimFront ? i : startIndex, trimFront ? endIndex + 1 : i + 1);
if (fontRenderer.width(Components.literal(sub).setStyle(newText.getStyle())) + trimWidth <= maxWidthPx) {
text = Components.literal(trimFront ? trim + sub : sub + trim).setStyle(newText.getStyle());
return;
}
}
}
@Override
protected void doRender(@Nonnull PoseStack ms, int mouseX, int mouseY, float partialTicks) {
if (text == null || text.getString().isEmpty())
return;
RenderSystem.setShaderColor(1, 1, 1, 1);
MutableComponent copy = text.plainCopy();
if (suffix != null && !suffix.isEmpty())
copy.append(suffix);
if (hasShadow)
font.drawShadow(ms, copy, getX(), getY(), color);
else
font.draw(ms, copy, getX(), getY(), color);
}
}

View File

@@ -0,0 +1,196 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.AllKeys;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.create.foundation.utility.Lang;
import java.util.function.Consumer;
import java.util.function.Function;
public class ScrollInput extends AbstractSimiWidget {
protected Consumer<Integer> onScroll;
protected int state;
protected Component title = Lang.translateDirect("gui.scrollInput.defaultTitle");
protected final Component scrollToModify = Lang.translateDirect("gui.scrollInput.scrollToModify");
protected final Component shiftScrollsFaster = Lang.translateDirect("gui.scrollInput.shiftScrollsFaster");
protected Component hint = null;
protected Label displayLabel;
protected boolean inverted;
protected boolean soundPlayed;
protected Function<Integer, Component> formatter;
protected int min, max;
protected int shiftStep;
Function<StepContext, Integer> step;
public ScrollInput(int xIn, int yIn, int widthIn, int heightIn) {
super(xIn, yIn, widthIn, heightIn);
state = 0;
min = 0;
max = 1;
shiftStep = 5;
step = standardStep();
formatter = i -> Components.literal(String.valueOf(i));
soundPlayed = false;
}
public Function<StepContext, Integer> standardStep() {
return c -> c.shift ? shiftStep : 1;
}
public ScrollInput inverted() {
inverted = true;
return this;
}
public ScrollInput withRange(int min, int max) {
this.min = min;
this.max = max;
return this;
}
public ScrollInput calling(Consumer<Integer> onScroll) {
this.onScroll = onScroll;
return this;
}
public ScrollInput format(Function<Integer, Component> formatter) {
this.formatter = formatter;
return this;
}
public ScrollInput removeCallback() {
this.onScroll = null;
return this;
}
public ScrollInput titled(MutableComponent title) {
this.title = title;
updateTooltip();
return this;
}
public ScrollInput addHint(MutableComponent hint) {
this.hint = hint;
updateTooltip();
return this;
}
public ScrollInput withStepFunction(Function<StepContext, Integer> step) {
this.step = step;
return this;
}
public ScrollInput writingTo(Label label) {
this.displayLabel = label;
if (label != null)
writeToLabel();
return this;
}
@Override
public void tick() {
super.tick();
soundPlayed = false;
}
public int getState() {
return state;
}
public ScrollInput setState(int state) {
this.state = state;
clampState();
updateTooltip();
if (displayLabel != null)
writeToLabel();
return this;
}
public ScrollInput withShiftStep(int step) {
shiftStep = step;
return this;
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
if (!this.visible || !this.isHovered) return false; //Added
if (inverted)
delta *= -1;
StepContext context = new StepContext();
context.control = AllKeys.ctrlDown();
context.shift = AllKeys.shiftDown();
context.currentValue = state;
context.forward = delta > 0;
int priorState = state;
boolean shifted = AllKeys.shiftDown();
int step = (int) Math.signum(delta) * this.step.apply(context);
state += step;
if (shifted)
state -= state % shiftStep;
clampState();
if (priorState != state) {
// if (!soundPlayed)
// Minecraft.getInstance()
// .getSoundManager()
// .play(SimpleSoundInstance.forUI(AllSoundEvents.SCROLL_VALUE.getMainEvent(),
// 1.5f + 0.1f * (state - min) / (max - min)));
// soundPlayed = true;
onChanged();
}
// return priorState != state;
return true; //Changed
}
protected void clampState() {
if (state >= max)
state = max - 1;
if (state < min)
state = min;
}
public void onChanged() {
if (displayLabel != null)
writeToLabel();
if (onScroll != null)
onScroll.accept(state);
updateTooltip();
}
protected void writeToLabel() {
displayLabel.text = formatter.apply(state);
}
protected void updateTooltip() {
toolTip.clear();
if (title == null)
return;
toolTip.add(title.plainCopy()
.withStyle(s -> s.withColor(HEADER_RGB)));
if (hint != null)
toolTip.add(hint.plainCopy()
.withStyle(s -> s.withColor(HINT_RGB)));
toolTip.add(scrollToModify.plainCopy()
.withStyle(ChatFormatting.ITALIC, ChatFormatting.DARK_GRAY));
toolTip.add(shiftScrollsFaster.plainCopy()
.withStyle(ChatFormatting.ITALIC, ChatFormatting.DARK_GRAY));
}
public static class StepContext {
public int currentValue;
public boolean forward;
public boolean shift;
public boolean control;
}
}

View File

@@ -0,0 +1,72 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.create.foundation.utility.Lang;
import java.util.ArrayList;
import java.util.List;
public class SelectionScrollInput extends ScrollInput {
private final MutableComponent scrollToSelect = Lang.translateDirect("gui.scrollInput.scrollToSelect");
protected List<? extends Component> options;
public SelectionScrollInput(int xIn, int yIn, int widthIn, int heightIn) {
super(xIn, yIn, widthIn, heightIn);
options = new ArrayList<>();
inverted();
}
public ScrollInput forOptions(List<? extends Component> options) {
this.options = options;
this.max = options.size();
format(options::get);
updateTooltip();
return this;
}
@Override
protected void updateTooltip() {
toolTip.clear();
if (title == null)
return;
toolTip.add(title.plainCopy()
.withStyle(s -> s.withColor(HEADER_RGB)));
int min = Math.min(this.max - 16, state - 7);
int max = Math.max(this.min + 16, state + 8);
min = Math.max(min, this.min);
max = Math.min(max, this.max);
if (this.min + 1 == min)
min--;
if (min > this.min)
toolTip.add(Components.literal("> ...")
.withStyle(ChatFormatting.GRAY));
if (this.max - 1 == max)
max++;
for (int i = min; i < max; i++) {
if (i == state)
toolTip.add(Components.empty()
.append("-> ")
.append(options.get(i))
.withStyle(ChatFormatting.WHITE));
else
toolTip.add(Components.empty()
.append("> ")
.append(options.get(i))
.withStyle(ChatFormatting.GRAY));
}
if (max < this.max)
toolTip.add(Components.literal("> ...")
.withStyle(ChatFormatting.GRAY));
if (hint != null)
toolTip.add(hint.plainCopy()
.withStyle(s -> s.withColor(HINT_RGB)));
toolTip.add(scrollToSelect.plainCopy()
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC));
}
}

View File

@@ -0,0 +1,25 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import java.util.List;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.network.chat.Component;
public class TooltipArea extends AbstractSimiWidget {
public TooltipArea(int x, int y, int width, int height) {
super(x, y, width, height);
}
@Override
public void renderWidget(PoseStack graphics, int mouseX, int mouseY, float partialTicks) {
if (visible)
isHovered = mouseX >= getX() && mouseY >= getY() && mouseX < getX() + width && mouseY < getY() + height;
}
public TooltipArea withTooltip(List<Component> tooltip) {
this.toolTip = tooltip;
return this;
}
}

Some files were not shown because too many files have changed in this diff Show More