Compare commits

...

97 commits

Author SHA1 Message Date
RuyaSavascisi
5d4fe7454a
tr_tr.json Turkish Localization for the mod 2024-08-01 14:28:20 +02:00
31bfbdb7f1 fix: #76 Logic Issue with Placement 2023-06-01 14:34:33 +02:00
ce0012dc3c fix: 1-block previews on consecutive placements 2023-06-01 14:34:12 +02:00
dc21a8882f update translations 2023-06-01 14:34:04 +02:00
2497f85800 bump version to 2.9 2023-02-12 18:06:17 +01:00
FITFC
d006a24ab7 added pt_br.json 2023-02-12 18:06:00 +01:00
mango_buff
e83cae7ae2 Create zh_cn.json 2023-02-12 18:05:55 +01:00
gjeodnd12165
a9f703e28f Create ko_kr.json 2023-02-12 18:05:46 +01:00
d69901c0fa fix: wands not breaking when out of durability 2023-02-12 17:51:31 +01:00
aefb3e138b update Botania integration for MC1.18.1 2022-03-26 00:18:18 +01:00
710a6a7ba1 remove superfluous inventory checks 2022-03-26 00:17:52 +01:00
7a89175bf1 use DeferredRegister for registering items 2022-03-25 22:00:47 +01:00
210baba4cd update MC and Forge to 1.18.1 2021-12-12 11:26:46 +01:00
0a1c0f2926 removed unused block texture 2021-12-12 11:01:42 +01:00
030d5f72f2 fix #47 crash when pressing E inside wand gui 2021-12-12 10:47:22 +01:00
f86a429beb update pack.mcmeta version 2021-12-09 12:02:39 +01:00
84af7b7227 Merge branch 'upd/2.5' into 1.18 2021-12-09 10:52:56 +01:00
721d7543a9 update forge and jei 2021-12-09 10:45:30 +01:00
d944f21655 bump version 2.4 -> 2.5 2021-12-09 10:21:08 +01:00
138e66e4b7 update readme 2021-12-09 10:20:15 +01:00
A. Regnander
439783744c Update sv_se.json
Added new strings
2021-12-09 10:18:31 +01:00
bc0668a1b7 fix wand getting damaged in creative mode 2021-12-09 10:17:48 +01:00
0ca129122e add JEI ingame documentation 2021-12-09 10:17:18 +01:00
A. Regnander
3d62df6ac5 Create sv_se.json 2021-12-09 10:15:13 +01:00
5be6d5c788 Ported to 1.18 2021-12-08 09:15:35 +01:00
bb48292c6f Fix destruction behind permeable blocks 2021-10-22 17:42:54 +02:00
c60260ea5c Fix #40 wand replacing half slabs
Fix destruction wand removing blocks not facing the player
2021-10-22 16:25:24 +02:00
8fd5a09363 Update Forge 2021-09-28 20:49:31 +02:00
5cad07d34d Fixed crash caused by HandlerCapability 2021-09-16 19:56:54 +02:00
9b036cdc50 Fixed server crash when changing wand direction 2021-08-21 15:12:29 +02:00
45da4ad7a7 Add support for bundle
Fix crash when opening wand GUI
2021-08-21 14:27:35 +02:00
e2394ff05e Ported to 1.17 2021-08-02 23:48:18 +02:00
6b555d2077 Updated Gradle, Forge and MCP for MC 1.17 2021-08-02 09:34:26 +02:00
489e050a7f Updated version info 2021-04-22 07:26:32 +02:00
f2d99a8251 Fixed Bug #34 Crash on client startup 2021-04-22 07:12:28 +02:00
ddbe201155 Fixed Bug #33 Preview broken in locations beyond 100k 2021-03-31 21:09:03 +02:00
f60692f6d8 Small fix (supportingBlock != null) 2021-03-14 22:52:27 +01:00
cdba987f8d Fixed crash when placing many connectable blocks
(iron bars, glass panes, redstone)

The reason for this crash is that I precomputed the blockstates to be placed (which would be free standing poles when no blocks are present around them).
Placing all blocks at once then causes lots of block updates which may crash the game (especially in MC1.15).
So the solution is to calculate the blockstate directly before the placement.
2021-03-14 22:49:09 +01:00
5b29ecca80 Added mod name to pack.mcmeta 2021-03-14 20:45:27 +01:00
81d92aa494 Removed min undo limit 2021-03-14 20:33:55 +01:00
503019af9a Removed min undo limit 2021-03-14 20:29:39 +01:00
ba99d1e309 MaxRange set to 100 2021-03-14 20:02:45 +01:00
fff371abf6 DestroySnapshots can replace fluids on undo 2021-03-14 18:07:32 +01:00
77f1f03210 DestroySnapshots can replace fluids on undo 2021-03-14 16:51:24 +01:00
cd55ff3c09 Updated README
changed Destruction core crafting recipe
fixed block limits in creative mode
2021-03-14 16:24:24 +01:00
82f83898fe Block placement checks when undoing 2021-03-08 18:57:15 +01:00
39e02d8fe6 Added crafting recipes for wand cores
Wand core list in tooltip
Tooltips for wand cores
2021-03-08 01:33:19 +01:00
ebf3ae9518 Finally added DESTRUCTION ACTION
Config options for destruction core + upgradeable
Better block limit system
2021-03-07 23:43:31 +01:00
243ac0e56e Updated language files 2021-03-07 18:08:18 +01:00
6675412bd1 Code cleanup 2021-03-07 18:00:10 +01:00
28bd5a7c70 Wand Supplier/Action Refactoring
fixed inventory pickup order
undo now works within range of blocks
2021-03-07 17:50:25 +01:00
997aa5c105 Removed wand reservoirs
Forge update
2021-03-06 14:50:28 +01:00
5a7143aa82 Fixed angel mode placing blocks on the wrong side 2021-02-19 00:07:12 +01:00
a9c6a1c5be Better handling of generated (conjured) blocks
Removed petrogenesis core
2021-02-18 22:00:33 +01:00
a0317115b7 Completed conjured block behavior
Added all wand upgrades
2021-02-17 23:17:42 +01:00
6618784804 Added conjured block
Improved data generation
2021-02-17 00:33:10 +01:00
bb3d36fa56 BIG Refactoring.
Modular WandActions and WandSuppliers.
Wand Cores and Wand Reservoirs can be added to your wand, they determine which action and supplier gets used.
2021-02-14 02:20:53 +01:00
39dc7204cd Reformat & cleanup 2021-02-06 15:02:44 +01:00
b914deb7cf Reworked snapshot/undo system. Added DestroySnapshot 2021-02-06 00:37:03 +01:00
a90ced6395 OptionKey can be changed in config 2021-02-05 16:41:13 +01:00
1af803f8bf Merge remote-tracking branch 'origin/1.16.2' into 1.16.2 2020-12-06 18:34:14 +01:00
933fbea382 Added TE White/Blacklist
Now triggers onBlockPlacedBy()
Fixed SecurityCraft bug
2020-12-06 18:33:49 +01:00
a7dbdcec0d Added TE White/Blacklist
Now triggers onBlockPlacedBy()
Fixed SecurityCraft bug
2020-12-06 17:42:39 +01:00
ea20d44d61 Set MC version to 1.16.2 2020-11-06 17:01:12 +01:00
224c0350b3 Fixed typo 2020-11-06 16:02:38 +01:00
5714f77092 Updated mod to V1.6
Added MC 1.16.4 support
Better direction lock descriptions
Removed stray English words in German translation
2020-11-05 19:04:36 +01:00
94aa1a89e8
Added Hacktoberfest info to readme 2020-10-04 23:18:09 +02:00
8026f877c0
Merge pull request #24 from Theta-Dev/update_1.5
Update 1.5 -> 1.16.2
2020-09-29 22:44:12 +02:00
23eb77e180 Changed config desc 2020-09-29 22:39:27 +02:00
8a558da7ba Added placement range limit 2020-09-29 22:29:07 +02:00
797853232d Added Pools, CTRL+Shift to open GUI, refactored undo 2020-09-28 00:57:00 +02:00
b569e274de Fixed stack overflow when placing many connecting blocks (1.16) 2020-09-18 22:42:44 +02:00
d8b5d0cf2a Changed readme 2020-09-18 13:04:01 +02:00
eacb3138e9 Set direction default to TARGET 2020-09-17 23:18:26 +02:00
132ac1d18f
Merge pull request #20 from Theta-Dev/update1.3
Update1.3 -> 1.16.2
2020-09-17 23:00:58 +02:00
ac21bb14c6 Added 1.16.3 compat 2020-09-17 22:55:08 +02:00
733f3bf597 Cleaned up gradle 2020-09-17 22:35:04 +02:00
5ba260d544 Rewrite of WandOptions, fixed server crash when placing banners 2020-09-17 22:00:51 +02:00
fa7979f40a Better config, more wand options, removed keys, added GUI 2020-09-17 16:17:38 +02:00
c940aaac9f Added block randomization, ReplacementRegistry 2020-09-16 16:25:43 +02:00
a279d1f4dd
Merge pull request #16 from Theta-Dev/fix_collision
fix exception with no-collision blocks
2020-08-26 23:50:48 +02:00
ff47d1fcc1 fix exception with no-collision blocks 2020-08-26 18:17:10 +02:00
7ff0c2f011 placement dir improvements 2020-08-26 17:15:04 +02:00
4dfd0ff2b9 1.16.2 update done 2020-08-26 14:32:46 +02:00
eb3bfc2a93 Update 1.2 done 2020-08-26 14:13:07 +02:00
f58a1c1892 Merge branch '1.15_1.2' into 1.16 2020-08-26 01:25:58 +02:00
14fb5689d1 Update 1.2:
Added RecipeGenerator
Fixed placing blocks facing player
2020-08-26 01:04:53 +02:00
8ad6bd627c 1.16 fixes 2020-08-03 20:05:07 +02:00
4fef8d0ac5 Merge remote-tracking branch 'origin/1.16' into 1.16 2020-08-03 19:59:02 +02:00
c434cf7c2a Fixed tooltip not showing angel mode 2020-08-03 19:58:41 +02:00
afd73ff314
Merge pull request #11 from Theta-Dev/1.15_update1.1
CTRL+SHIFT to undo
2020-08-03 18:56:15 +02:00
ddfe2431aa Infinity wand wont burn 2020-08-03 11:59:36 +02:00
40b879e3f4
Merge pull request #9 from Theta-Dev/update1.1
Update 1.15 to mod version 1.1
2020-08-03 01:18:18 +02:00
504f90a186
Merge branch '1.16' into update1.1 2020-08-03 01:18:07 +02:00
4068c9aa77
Removed examplemod reference 2020-08-02 16:51:43 +02:00
e6f7a185b0 Port to 1.16 2020-08-02 16:31:20 +02:00
01410b9ae5 Update to 1.16 2020-08-02 14:30:03 +02:00
150 changed files with 4569 additions and 1986 deletions

5
.gitignore vendored
View file

@ -22,4 +22,7 @@ eclipse
run
# Files from Forge MDK
forge*changelog.txt
forge*changelog.txt
.cache
logs

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) 2021 ThetaDev
MIT LICENSE
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

135
README.md
View file

@ -1,51 +1,134 @@
# Construction Wand
With a Construction Wand you can place multiple blocks (up to 1024) at once, extending your build on the side you're facing.
Sneak+Right click to activate angel mode which allows you to place a block at the opposite side of the block facing you.
If you concentrate enough, you can even conjure a block in mid air!
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/wands.png)
With a Construction Wand you can place multiple blocks (up to 1024) at once, extending your build on the side you're
facing. If that's not enough: you can upgrade your wand with additional cores, allowing you to place a block behind the
block you are facing, conjure blocks in mid air or destroy lots of blocks very fast.
![](images/wands.png)
**Note:** These are the instructions for ConstructionWand version 2.0+, which introduced some new features.
If you are still using version 1.x, refer to [those](https://github.com/Theta-Dev/ConstructionWand/tree/1.16.2-1.7)
instructions.
## Wands
There are basic wands made from stone, iron and diamond and the Infinity wand.
| Wand | Durability | Max. Blocks | Angel distance |
|----------|-------------|-------------|----------------|
| Stone | 131 | 9 | No angel mode |
| Iron | 250 | 27 | 1 |
| Diamond | 1561 | 128 | 4 |
| Infinity | Unbreakable | 1024 | 8 |
There are basic wands made from stone, iron and diamond and the Infinity wand. Wands from higher tiers are more powerful
and last longer. These properties can be changed in the config.
| Wand | Durability | Max. Blocks | Upgradeable | Angel distance | Max. Blocks (Destroy) |
|----------|-------------|-------------|-------------|----------------|-----------------------|
| Stone | 131 | 9 | No | - | - |
| Iron | 250 | 27 | Yes | 1 | 9 |
| Diamond | 1561 | 128 | Yes | 4 | 25 |
| Infinity | Unbreakable | 1024 | Yes | 8 | 81 |
## Crafting
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/crafting1.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/crafting2.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/crafting3.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/crafting4.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting1.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting2.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting3.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting4.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting5.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting6.png)
## Modes
There are four wand tiers: Stone, Iron, Diamond and Infinity.
## Keybindings
**Default mode:** Extends your build on the side facing you. Maximum number of blocks depends on wand tier. SHIFT+scroll to change the placement mode (Horizontal, Vertical, North/South, East/West, No lock).
To change a wand's core and options or undo your placement you need to hold down the wand option key
(refered as OPTKEY). By default, this is CTRL, but it can be changed in the client config file. To prevent unwanted
changes to your options, you can configure that you have to hold down SNEAK as well to change options or open up the
GUI.
**Angel mode:** Places a block on the opposite side of the block (or row of blocks) you are facing. Maximum distance depends on wand tier. Right click empty space to place a block in midair (similar to angel blocks, hence the name). To do that, you'll need to have the block you want to place in your offhand. You can't place a block in midair if you've fallen more than 10 blocks deep (no easy rescue tool from falling into the void).
## Wand cores
Wand cores are magical gemstones that make your wand do its thing.
Every wand comes with a Construction core. You can make your wand even better by putting
aditional cores into it. Put your new core together with your wand in a
crafting grid to install it. To switch between cores, hold down OPTKEY and left click empty space
with your wand.
Stone wands can't be upgraded.
**Construction core**
Extend your build on the side facing you. Maximum number of blocks depends on wand tier.
Hold down OPTKEY and scroll to change placement restriction
(Horizontal, Vertical, North/South, East/West, No lock).
**Angel core**
Place a block on the opposite side of the block (or row of blocks) you are facing. Maximum distance depends
on wand tier. Right click empty space to place a block in midair (similar to angel blocks, hence the name).
To do that, you'll need to have the block you want to place in your offhand. You can't place a block in midair
if you've fallen more than 10 blocks deep (no easy rescue tool from falling into the void).
**Destruction core**
Destroy blocks (no tile entities) on the side facing you. Maximum number of blocks depends on wand tier.
Restrictions work just like with the Construction core. Destroyed blocks disappear into the void,
you can use the undo feature if you've made a mistake.
## Options
Every wand has two additional options that can be changed using keys. (Standard: N / SHIFT+N)
SNEAK+OPTKEY+Right clicking empty space opens the option screen of your wand.
**Placement direction:** If set to "player" the wand places blocks in the same direction as if they were placed by yourself. Target mode places the blocks in the same direction as their supporting block. See the picture below:
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/options.png)
**Restriction:** If restriction is enabled the wand will only place blocks in one row or column
(choose between North/South, East/West on a horizontal plane and Horizontal, Vertical on a vertical plane).
If the direction lock is switched off, the wand will extend the entire face of the building it's pointed at. This option
has no effect if the angel core is enabled.
**Direction:** If set to "Player" the wand places blocks in the same direction as if they were placed by yourself.
Target mode places the blocks in the same direction as their supporting block. See the picture below:
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/placedir.png)
**Fluid lock:** Enables/disables the replacement of fluid blocks (both source + flowing).
**Replacement:** Enables/disables the replacement of replaceable blocks (Fluids, snow, tallgrass).
**Matching:** Select which blocks are extended by the wand. If set to "EXACT" it will only extend blocks that
are exactly the same as the selected block.<br>
"SIMILAR" will treat similar blocks equally (e.g. extend dirt and grass blocks).<br>
"ANY" will extend any block on the face of the building you're looking at.
**Random:** If random mode is enabled, the wand places random blocks from your hotbar.<br>
~~Shamelessly stolen~~ Inspired by the Trowel from Quark.
## Undo
Holding down Sneak+OPTKEY while looking at a block will show you the last blocks you placed with a green border around
them. Sneak+OPTKEY+Right clicking any of them will undo the operation, giving you all the items back. If you used the
Destruction core, it will restore the blocks.
## Additional features
- If you have shulker boxes in your inventory filled with blocks, the wand can pull them out and place them
- Botania compatibility: The Black Hole Talisman can supply blocks just like shulker boxes can. Having a Rod of the Lands / Rod of the Depths in your inventory will provide you with infinite dirt/cobble at the cost of Mana.
- Shulker boxes, bundles (MC 1.17+) and many containers from other mods can provide building blocks for the wand
- Holding down SHIFT+CTRL while looking at a blocks will show you the last blocks you placed with a green border around them. SHIFT+CTRL+Right clickking any of them will undo the operation, giving you all the items back.
- Botania compatibility: The Black Hole Talisman can supply blocks just like shulker boxes can. Having a Rod of the
Lands / Rod of the Depths in your inventory will provide you with infinite dirt/cobble at the cost of Mana.
- Having blocks in your offhand will place them instead of the block you're looking at
- Look at your statisics to see how many blocks you have placed using your wand
- **1.16 only:** The Infinity Wand won't burn in lava just like netherite gear.
- **1.16+ only:** The Infinity Wand won't burn in lava just like netherite gear.
- Ingame documentation with Just Enough Items (JEI)
## TileEntity Blacklist
Some modded TileEntitys can cause issues when placed using a wand. They may turn into invisible and
unremovable ghost blocks, become unbreakable or cause other unwanted effects.
That's why I've included a Black/Whitelist system
for TileEntities in CW Version 1.7. Chisels&Bits blocks are blacklisted by default.
There are probably a few other tile entities from other mods out there which may cause issues as well.
If you find some of them you can tell me by creating
an issue, commenting on Curse or editing the default blacklist yourself
(it is located at https://github.com/Theta-Dev/ConstructionWand/blob/1.16.2/src/main/java/thetadev/constructionwand/basics/ConfigServer.java#L28)
and making a PR.
## Contributions and #Hacktoberfest
As #Hacktoberfest now requires repo owners to opt in, I added the tag to this repository.
I'd really appreciate translations. Currently, ConstructionWand is available in English, German and Swedish.
If you speak any other language you can help translate the mod and add a new language file
under `src/main/resources/assets/constructionwand/lang/`.

View file

@ -1,11 +1,10 @@
buildscript {
repositories {
maven { url = 'https://files.minecraftforge.net/maven' }
jcenter()
maven { url = 'https://maven.minecraftforge.net' }
mavenCentral()
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true
}
}
apply plugin: 'net.minecraftforge.gradle'
@ -13,28 +12,18 @@ apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
version = '1.15-1.1'
group = 'thetadev.constructionwand' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'constructionwand'
version = "${mcversion}-${version_major}.${version_minor}"
group = "${author}.${modid}"
archivesBaseName = "${modid}"
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
minecraft {
// The mappings can be changed at any time, and must be in the following format.
// snapshot_YYYYMMDD Snapshot are built nightly.
// stable_# Stables are built at the discretion of the MCP team.
// Use non-default mappings at your own risk. they may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
mappings channel: 'snapshot', version: '20200514-1.15.1'
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
mappings channel: project.mcp_channel, version: project.mcp_mappings
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
client {
workingDirectory project.file('run')
workingDirectory project.file("run/client").canonicalPath
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
@ -50,7 +39,7 @@ minecraft {
}
server {
workingDirectory project.file('run')
workingDirectory project.file("run/server").canonicalPath
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
@ -66,7 +55,7 @@ minecraft {
}
data {
workingDirectory project.file('run')
workingDirectory project.file("run/client").canonicalPath
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
@ -74,7 +63,10 @@ minecraft {
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
args '--mod', 'examplemod', '--all', '--output', file('src/generated/resources/')
args '--mod', archivesBaseName, '--all',
'--existing', '"' + file('src/main/resources/') + '"',
'--existing', '"' + file('src/generated/resources/') + '"',
'--output', '"' + file('src/generated/resources/') + '"'
mods {
constructionwand {
@ -85,6 +77,10 @@ minecraft {
}
}
sourceSets.main.resources {
srcDir 'src/generated/resources'
}
repositories {
maven {
url = "https://maven.blamejared.com"
@ -92,50 +88,39 @@ repositories {
maven {
url = "https://maven.theillusivec4.top/"
}
maven {
name = "JEI Maven"
url "https://dvs1.progwml6.com/files/maven"
}
}
dependencies {
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
minecraft 'net.minecraftforge:forge:1.15.2-31.2.31'
minecraft([
group : "net.minecraftforge",
name : "forge",
version: "${project.mcversion}-${project.forgeversion}"
])
runtimeOnly fg.deobf("vazkii.patchouli:Patchouli:1.15.2-1.2-32.160")
runtimeOnly fg.deobf("top.theillusivec4.curios:curios:FORGE-1.15.2-2.0.2.4")
compileOnly fg.deobf("vazkii.botania:Botania:r1.15-387.455:api")
runtimeOnly fg.deobf("vazkii.botania:Botania:r1.15-387.455")
// You may put jars on which you depend on in ./libs 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.
// provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// These dependencies get remapped to your current MCP mappings
// deobf '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
compileOnly fg.deobf("mezz.jei:${jei_version}:api")
runtimeOnly fg.deobf("mezz.jei:${jei_version}")
compileOnly fg.deobf([
group: "vazkii.botania",
name: "Botania",
version: "${project.botania}",
classifier: "api"
])
}
jar {
manifest {
attributes([
"Specification-Title": archivesBaseName,
"Specification-Vendor": "thetadev",
"Specification-Vendor": "${author}",
"Specification-Version": "1", // We are version 1 of ourselves
"Implementation-Title": archivesBaseName,
"Implementation-Version": "${version}",
"Implementation-Vendor" : "thetadev",
"Implementation-Vendor" :"${author}",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
}

View file

@ -1,4 +1,18 @@
# 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
org.gradle.daemon=false
author=thetadev
modid=constructionwand
mcversion=1.18.1
forgeversion=39.1.0
mcp_channel=official
mcp_mappings=1.18.1
# Source: https://maven.blamejared.com/vazkii/botania/Botania/
botania=1.18.1-429
# Source: https://dvs1.progwml6.com/files/maven/mezz/jei/
jei_version=jei-1.18.1:9.1.0.41
version_major=2
version_minor=11

Binary file not shown.

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip

55
gradlew vendored
View file

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or 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
@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# 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"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@ -56,7 +72,7 @@ case "`uname`" in
Darwin* )
darwin=true
;;
MINGW* )
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
@ -66,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@ -109,10 +126,11 @@ 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
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@ -138,19 +156,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $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" ;;
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
fi
@ -159,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

43
gradlew.bat vendored
View file

@ -1,3 +1,19 @@
@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
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
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=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_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=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@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
@rem End local scope for the variables with windows NT shell

BIN
images/crafting5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
images/crafting6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
images/options.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "constructionwand:item/core_angel"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "constructionwand:item/core_destruction"
}
}

View file

@ -0,0 +1,14 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/diamond_wand"
},
"overrides": [
{
"predicate": {
"constructionwand:using_core": 1.0
},
"model": "constructionwand:item/diamond_wand_core"
}
]
}

View file

@ -0,0 +1,7 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/diamond_wand",
"layer1": "constructionwand:item/overlay_core"
}
}

View file

@ -0,0 +1,14 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/infinity_wand"
},
"overrides": [
{
"predicate": {
"constructionwand:using_core": 1.0
},
"model": "constructionwand:item/infinity_wand_core"
}
]
}

View file

@ -0,0 +1,7 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/infinity_wand",
"layer1": "constructionwand:item/overlay_core"
}
}

View file

@ -0,0 +1,14 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/iron_wand"
},
"overrides": [
{
"predicate": {
"constructionwand:using_core": 1.0
},
"model": "constructionwand:item/iron_wand_core"
}
]
}

View file

@ -0,0 +1,7 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/iron_wand",
"layer1": "constructionwand:item/overlay_core"
}
}

View file

@ -0,0 +1,14 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/stone_wand"
},
"overrides": [
{
"predicate": {
"constructionwand:using_core": 1.0
},
"model": "constructionwand:item/stone_wand_core"
}
]
}

View file

@ -0,0 +1,7 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "constructionwand:item/stone_wand",
"layer1": "constructionwand:item/overlay_core"
}
}

View file

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"constructionwand:core_angel"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:feathers"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "constructionwand:core_angel"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View file

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"constructionwand:core_destruction"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:storage_blocks/diamond"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "constructionwand:core_destruction"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View file

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"constructionwand:diamond_wand"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:gems/diamond"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "constructionwand:diamond_wand"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View file

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"constructionwand:infinity_wand"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:nether_stars"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "constructionwand:infinity_wand"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View file

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"constructionwand:iron_wand"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:ingots/iron"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "constructionwand:iron_wand"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View file

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"constructionwand:stone_wand"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "minecraft:stone_tool_materials"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "constructionwand:stone_wand"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" #X",
"#O#",
"X# "
],
"key": {
"O": {
"tag": "forge:feathers"
},
"X": {
"tag": "forge:ingots/gold"
},
"#": {
"tag": "forge:glass_panes"
}
},
"result": {
"item": "constructionwand:core_angel"
}
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" #X",
"#O#",
"X# "
],
"key": {
"O": {
"tag": "forge:storage_blocks/diamond"
},
"X": {
"item": "minecraft:diamond_pickaxe"
},
"#": {
"tag": "forge:glass_panes"
}
},
"result": {
"item": "constructionwand:core_destruction"
}
}

View file

@ -1,24 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern":
[
"pattern": [
" X",
" # ",
"# "
],
"key":
{
"X":
{
"key": {
"X": {
"tag": "forge:gems/diamond"
},
"#":
{
"item": "minecraft:stick"
"#": {
"tag": "forge:rods/wooden"
}
},
"result":
{
"result": {
"item": "constructionwand:diamond_wand"
}
}

View file

@ -0,0 +1,3 @@
{
"type": "constructionwand:wand_upgrade"
}

View file

@ -1,24 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern":
[
"pattern": [
" X",
" # ",
"# "
],
"key":
{
"X":
{
"key": {
"X": {
"tag": "forge:nether_stars"
},
"#":
{
"item": "minecraft:stick"
"#": {
"tag": "forge:rods/wooden"
}
},
"result":
{
"result": {
"item": "constructionwand:infinity_wand"
}
}

View file

@ -1,24 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern":
[
"pattern": [
" X",
" # ",
"# "
],
"key":
{
"X":
{
"key": {
"X": {
"tag": "forge:ingots/iron"
},
"#":
{
"item": "minecraft:stick"
"#": {
"tag": "forge:rods/wooden"
}
},
"result":
{
"result": {
"item": "constructionwand:iron_wand"
}
}

View file

@ -1,24 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern":
[
"pattern": [
" X",
" # ",
"# "
],
"key":
{
"X":
{
"tag": "forge:cobblestone"
"key": {
"X": {
"tag": "minecraft:stone_tool_materials"
},
"#":
{
"item": "minecraft:stick"
"#": {
"tag": "forge:rods/wooden"
}
},
"result":
{
"result": {
"item": "constructionwand:stone_wand"
}
}

View file

@ -1,6 +1,6 @@
package thetadev.constructionwand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
@ -8,72 +8,89 @@ import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import thetadev.constructionwand.basics.ConfigHandler;
import thetadev.constructionwand.basics.ConfigClient;
import thetadev.constructionwand.basics.ConfigServer;
import thetadev.constructionwand.basics.ModStats;
import thetadev.constructionwand.client.KeyEvents;
import thetadev.constructionwand.basics.ReplacementRegistry;
import thetadev.constructionwand.client.ClientEvents;
import thetadev.constructionwand.client.RenderBlockPreview;
import thetadev.constructionwand.containers.ContainerManager;
import thetadev.constructionwand.containers.ContainerRegistrar;
import thetadev.constructionwand.job.JobHistory;
import thetadev.constructionwand.items.ModItems;
import thetadev.constructionwand.network.PacketQueryUndo;
import thetadev.constructionwand.network.PacketUndoBlocks;
import thetadev.constructionwand.network.PacketWandOption;
import thetadev.constructionwand.wand.undo.UndoHistory;
@Mod(ConstructionWand.MODID)
public class ConstructionWand
{
public static final String MODID = "constructionwand";
public static final String MODNAME = "ConstructionWand";
public static ConstructionWand instance;
public static final Logger LOGGER = LogManager.getLogger();
private static final String PROTOCOL_VERSION = "1";
public SimpleChannel HANDLER;
public ContainerManager containerManager;
public JobHistory jobHistory;
public UndoHistory undoHistory;
public RenderBlockPreview renderBlockPreview;
public ConstructionWand() {
instance = this;
containerManager = new ContainerManager();
jobHistory = new JobHistory();
undoHistory = new UndoHistory();
// Register setup methods for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientSetup);
MinecraftForge.EVENT_BUS.register(this);
// Register Item DeferredRegister
ModItems.ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
// Config setup
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ConfigHandler.SPEC, MODID + ".toml");
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ConfigServer.SPEC);
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigClient.SPEC);
}
private void commonSetup(final FMLCommonSetupEvent event)
{
private void commonSetup(final FMLCommonSetupEvent event) {
LOGGER.info("ConstructionWand says hello - may the odds be ever in your favor.");
// Register packets
HANDLER = NetworkRegistry.newSimpleChannel(new ResourceLocation(MODID, "main"), ()->PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals);
HANDLER = NetworkRegistry.newSimpleChannel(new ResourceLocation(MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals);
int packetIndex = 0;
HANDLER.registerMessage(packetIndex++, PacketUndoBlocks.class, PacketUndoBlocks::encode, PacketUndoBlocks::decode, PacketUndoBlocks.Handler::handle);
HANDLER.registerMessage(packetIndex++, PacketQueryUndo.class, PacketQueryUndo::encode, PacketQueryUndo::decode, PacketQueryUndo.Handler::handle);
HANDLER.registerMessage(packetIndex++, PacketWandOption.class, PacketWandOption::encode, PacketWandOption::decode, PacketWandOption.Handler::handle);
HANDLER.registerMessage(packetIndex, PacketWandOption.class, PacketWandOption::encode, PacketWandOption::decode, PacketWandOption.Handler::handle);
// Container registry
ContainerRegistrar.register();
//Replacement registry
ReplacementRegistry.init();
// Stats
ModStats.register();
}
private void clientSetup(final FMLClientSetupEvent event)
{
private void clientSetup(final FMLClientSetupEvent event) {
renderBlockPreview = new RenderBlockPreview();
MinecraftForge.EVENT_BUS.register(renderBlockPreview);
MinecraftForge.EVENT_BUS.register(new KeyEvents());
MinecraftForge.EVENT_BUS.register(new ClientEvents());
event.enqueueWork(ModItems::registerModelProperties);
event.enqueueWork(ModItems::registerItemColors);
}
public static ResourceLocation loc(String name) {
return new ResourceLocation(MODID, name);
}
}

View file

@ -1,14 +1,13 @@
package thetadev.constructionwand.api;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
/**
* Created by james on 28/12/16.
*/
public interface IContainerHandler
{
boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack);
int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack);
int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count);
boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack);
int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack);
int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count);
}

View file

@ -0,0 +1,24 @@
package thetadev.constructionwand.api;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.wand.undo.ISnapshot;
import javax.annotation.Nonnull;
import java.util.List;
public interface IWandAction
{
int getLimit(ItemStack wand);
@Nonnull
List<ISnapshot> getSnapshots(Level world, Player player, BlockHitResult rayTraceResult,
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit);
@Nonnull
List<ISnapshot> getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult,
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit);
}

View file

@ -0,0 +1,8 @@
package thetadev.constructionwand.api;
public interface IWandCore extends IWandUpgrade
{
int getColor();
IWandAction getWandAction();
}

View file

@ -0,0 +1,30 @@
package thetadev.constructionwand.api;
import net.minecraft.core.BlockPos;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import thetadev.constructionwand.wand.undo.PlaceSnapshot;
import javax.annotation.Nullable;
public interface IWandSupplier
{
void getSupply(@Nullable BlockItem target);
/**
* Tries to create a new PlaceSnapshot at the specified position.
* Returns null if there aren't any blocks available that can be placed
* in that position.
*/
@Nullable
PlaceSnapshot getPlaceSnapshot(Level world, BlockPos pos, BlockHitResult rayTraceResult,
@Nullable BlockState supportingBlock);
/**
* Consumes an item stack if the placement was successful
*/
int takeItemStack(ItemStack stack);
}

View file

@ -0,0 +1,8 @@
package thetadev.constructionwand.api;
import net.minecraft.resources.ResourceLocation;
public interface IWandUpgrade
{
ResourceLocation getRegistryName();
}

View file

@ -1,22 +1,18 @@
package thetadev.constructionwand.basics;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraftforge.client.event.InputUpdateEvent;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.network.PacketQueryUndo;
@Mod.EventBusSubscriber(modid = ConstructionWand.MODID)
public class CommonEvents
{
@SubscribeEvent
public static void logOut(PlayerEvent.PlayerLoggedOutEvent e) {
PlayerEntity player = e.getPlayer();
if(player.getEntityWorld().isRemote) return;
ConstructionWand.instance.jobHistory.removePlayer(player);
}
@SubscribeEvent
public static void logOut(PlayerEvent.PlayerLoggedOutEvent e) {
Player player = e.getPlayer();
if(player.level.isClientSide) return;
ConstructionWand.instance.undoHistory.removePlayer(player);
}
}

View file

@ -0,0 +1,32 @@
package thetadev.constructionwand.basics;
import net.minecraftforge.common.ForgeConfigSpec;
public class ConfigClient
{
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
public static final ForgeConfigSpec.IntValue OPT_KEY;
public static final ForgeConfigSpec.BooleanValue SHIFTOPT_MODE;
public static final ForgeConfigSpec.BooleanValue SHIFTOPT_GUI;
static {
BUILDER.comment("This is the Client config for ConstructionWand.",
"If you're not familiar with Forge's new split client/server config, let me explain:",
"Client config is stored in the /config folder and only contains client specific settings like graphics and keybinds.",
"Mod behavior is configured in the Server config, which is world-specific and thus located",
"in the /saves/myworld/serverconfig folder. If you want to change the serverconfig for all",
"new worlds, copy the config files in the /defaultconfigs folder.");
BUILDER.push("keys");
BUILDER.comment("Key code of OPTKEY (Default: Left Control). Look up key codes under https://www.glfw.org/docs/3.3/group__keys.html");
OPT_KEY = BUILDER.defineInRange("OptKey", 341, 0, 350);
BUILDER.comment("Press SNEAK+OPTKEY instead of SNEAK for changing wand mode/direction lock");
SHIFTOPT_MODE = BUILDER.define("ShiftOpt", false);
BUILDER.comment("Press SNEAK+OPTKEY instead of SNEAK for opening wand GUI");
SHIFTOPT_GUI = BUILDER.define("ShiftOptGUI", true);
BUILDER.pop();
}
public static final ForgeConfigSpec SPEC = BUILDER.build();
}

View file

@ -1,69 +0,0 @@
package thetadev.constructionwand.basics;
import net.minecraft.item.Item;
import net.minecraft.item.ItemTier;
import net.minecraft.item.Items;
import net.minecraftforge.common.ForgeConfigSpec;
import java.util.Arrays;
import java.util.List;
public class ConfigHandler
{
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
// Durability
public static final ForgeConfigSpec.IntValue DURABILITY_STONE;
public static final ForgeConfigSpec.IntValue DURABILITY_IRON;
public static final ForgeConfigSpec.IntValue DURABILITY_DIAMOND;
public static final ForgeConfigSpec.IntValue LIMIT_STONE;
public static final ForgeConfigSpec.IntValue LIMIT_IRON;
public static final ForgeConfigSpec.IntValue LIMIT_DIAMOND;
public static final ForgeConfigSpec.IntValue LIMIT_INFINITY;
public static final ForgeConfigSpec.IntValue LIMIT_CREATIVE;
public static final ForgeConfigSpec.IntValue ANGEL_STONE;
public static final ForgeConfigSpec.IntValue ANGEL_IRON;
public static final ForgeConfigSpec.IntValue ANGEL_DIAMOND;
public static final ForgeConfigSpec.IntValue ANGEL_INFINITY;
public static final ForgeConfigSpec.IntValue UNDO_HISTORY;
public static final ForgeConfigSpec.BooleanValue ANGEL_FALLING;
static {
BUILDER.comment("Wand durability");
BUILDER.push("durability");
DURABILITY_STONE = BUILDER.defineInRange("StoneWand", ItemTier.STONE.getMaxUses(), 1, Integer.MAX_VALUE);
DURABILITY_IRON = BUILDER.defineInRange("IronWand", ItemTier.IRON.getMaxUses(), 1, Integer.MAX_VALUE);
DURABILITY_DIAMOND = BUILDER.defineInRange("DiamondWand", ItemTier.DIAMOND.getMaxUses(), 1, Integer.MAX_VALUE);
BUILDER.pop();
BUILDER.comment("Wand block limit");
BUILDER.push("block_limit");
LIMIT_STONE = BUILDER.defineInRange("StoneWand", 9, 1, Integer.MAX_VALUE);
LIMIT_IRON = BUILDER.defineInRange("IronWand", 27, 1, Integer.MAX_VALUE);
LIMIT_DIAMOND = BUILDER.defineInRange("DiamondWand", 128, 1, Integer.MAX_VALUE);
LIMIT_INFINITY = BUILDER.defineInRange("InfinityWand", 1024, 1, Integer.MAX_VALUE);
BUILDER.comment("Infinity Wand used in creative mode");
LIMIT_CREATIVE = BUILDER.defineInRange("InfinityWandCreative", 2048, 1, Integer.MAX_VALUE);
BUILDER.pop();
BUILDER.comment("Max placement distance with angel mode (0 to disable angel mode)");
BUILDER.push("angel_distance");
ANGEL_STONE = BUILDER.defineInRange("StoneWand", 0, 0, Integer.MAX_VALUE);
ANGEL_IRON = BUILDER.defineInRange("IronWand", 1, 0, Integer.MAX_VALUE);
ANGEL_DIAMOND = BUILDER.defineInRange("DiamondWand", 4, 0, Integer.MAX_VALUE);
ANGEL_INFINITY = BUILDER.defineInRange("InfinityWand", 8, 0, Integer.MAX_VALUE);
BUILDER.pop();
BUILDER.push("misc");
BUILDER.comment("Number of operations that can be undone");
UNDO_HISTORY = BUILDER.defineInRange("UndoHistory", 3, 0, Integer.MAX_VALUE);
BUILDER.comment("Place blocks below you while falling > 10 blocks with angel mode (Can be used to save you from drops/the void)");
ANGEL_FALLING = BUILDER.define("AngelFalling", false);
BUILDER.pop();
}
public static final ForgeConfigSpec SPEC = BUILDER.build();
}

View file

@ -0,0 +1,138 @@
package thetadev.constructionwand.basics;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Tiers;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.registries.RegistryObject;
import thetadev.constructionwand.items.ModItems;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class ConfigServer
{
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
public static final ForgeConfigSpec.IntValue LIMIT_CREATIVE;
public static final ForgeConfigSpec.IntValue MAX_RANGE;
public static final ForgeConfigSpec.IntValue UNDO_HISTORY;
public static final ForgeConfigSpec.BooleanValue ANGEL_FALLING;
public static final ForgeConfigSpec.ConfigValue<List<?>> SIMILAR_BLOCKS;
private static final String[] SIMILAR_BLOCKS_DEFAULT = {
"minecraft:dirt;minecraft:grass_block;minecraft:coarse_dirt;minecraft:podzol;minecraft:mycelium;minecraft:farmland;minecraft:dirt_path;minecraft:rooted_dirt"
};
public static final ForgeConfigSpec.BooleanValue TE_WHITELIST;
public static final ForgeConfigSpec.ConfigValue<List<?>> TE_LIST;
private static final String[] TE_LIST_DEFAULT = {"chiselsandbits"};
private static final HashMap<ResourceLocation, WandProperties> wandProperties = new HashMap<>();
public static WandProperties getWandProperties(Item wand) {
return wandProperties.getOrDefault(wand.getRegistryName(), WandProperties.DEFAULT);
}
public static class WandProperties
{
public static final WandProperties DEFAULT = new WandProperties(null, null, null, null, null);
private final ForgeConfigSpec.IntValue durability;
private final ForgeConfigSpec.IntValue limit;
private final ForgeConfigSpec.IntValue angel;
private final ForgeConfigSpec.IntValue destruction;
private final ForgeConfigSpec.BooleanValue upgradeable;
private WandProperties(ForgeConfigSpec.IntValue durability, ForgeConfigSpec.IntValue limit,
ForgeConfigSpec.IntValue angel, ForgeConfigSpec.IntValue destruction,
ForgeConfigSpec.BooleanValue upgradeable) {
this.durability = durability;
this.limit = limit;
this.angel = angel;
this.destruction = destruction;
this.upgradeable = upgradeable;
}
public WandProperties(ForgeConfigSpec.Builder builder, RegistryObject<Item> wandSupplier, int defDurability, int defLimit,
int defAngel, int defDestruction, boolean defUpgradeable) {
ResourceLocation registryName = wandSupplier.getId();
builder.push(registryName.getPath());
if(defDurability > 0) {
builder.comment("Wand durability");
durability = builder.defineInRange("durability", defDurability, 1, Integer.MAX_VALUE);
}
else durability = null;
builder.comment("Wand block limit");
limit = builder.defineInRange("limit", defLimit, 1, Integer.MAX_VALUE);
builder.comment("Max placement distance with angel core (0 to disable angel core)");
angel = builder.defineInRange("angel", defAngel, 0, Integer.MAX_VALUE);
builder.comment("Wand destruction block limit (0 to disable destruction core)");
destruction = builder.defineInRange("destruction", defDestruction, 0, Integer.MAX_VALUE);
builder.comment("Allow wand upgrading by putting the wand together with a wand core in a crafting grid.");
upgradeable = builder.define("upgradeable", defUpgradeable);
builder.pop();
wandProperties.put(registryName, this);
}
public int getDurability() {
return durability == null ? -1 : durability.get();
}
public int getLimit() {
return limit == null ? 0 : limit.get();
}
public int getAngel() {
return angel == null ? 0 : angel.get();
}
public int getDestruction() {
return destruction == null ? 0 : destruction.get();
}
public boolean isUpgradeable() {
return upgradeable != null && upgradeable.get();
}
}
static {
BUILDER.comment("This is the Server config for ConstructionWand.",
"If you're not familiar with Forge's new split client/server config, let me explain:",
"Client config is stored in the /config folder and only contains client specific settings like graphics and keybinds.",
"Mod behavior is configured in the Server config, which is world-specific and thus located",
"in the /saves/myworld/serverconfig folder. If you want to change the serverconfig for all",
"new worlds, copy the config files in the /defaultconfigs folder.");
new WandProperties(BUILDER, ModItems.WAND_STONE, Tiers.STONE.getUses(), 9, 0, 0, false);
new WandProperties(BUILDER, ModItems.WAND_IRON, Tiers.IRON.getUses(), 27, 2, 9, true);
new WandProperties(BUILDER, ModItems.WAND_DIAMOND, Tiers.DIAMOND.getUses(), 128, 8, 25, true);
new WandProperties(BUILDER, ModItems.WAND_INFINITY, 0, 1024, 16, 81, true);
BUILDER.push("misc");
BUILDER.comment("Block limit for Infinity Wand used in creative mode");
LIMIT_CREATIVE = BUILDER.defineInRange("InfinityWandCreative", 2048, 1, Integer.MAX_VALUE);
BUILDER.comment("Maximum placement range (0: unlimited). Affects all wands and is meant for lag prevention, not game balancing.");
MAX_RANGE = BUILDER.defineInRange("MaxRange", 100, 0, Integer.MAX_VALUE);
BUILDER.comment("Number of operations that can be undone");
UNDO_HISTORY = BUILDER.defineInRange("UndoHistory", 3, 0, Integer.MAX_VALUE);
BUILDER.comment("Place blocks below you while falling > 10 blocks with angel core (Can be used to save you from drops/the void)");
ANGEL_FALLING = BUILDER.define("AngelFalling", false);
BUILDER.comment("Blocks to treat equally when in Similar mode. Enter block IDs seperated by ;");
SIMILAR_BLOCKS = BUILDER.defineList("SimilarBlocks", Arrays.asList(SIMILAR_BLOCKS_DEFAULT), obj -> true);
BUILDER.pop();
BUILDER.push("tileentity");
BUILDER.comment("White/Blacklist for Tile Entities. Allow/Prevent blocks with TEs from being placed by wand.",
"You can either add block ids like minecraft:chest or mod ids like minecraft");
TE_LIST = BUILDER.defineList("TEList", Arrays.asList(TE_LIST_DEFAULT), obj -> true);
BUILDER.comment("If set to TRUE, treat TEList as a whitelist, otherwise blacklist");
TE_WHITELIST = BUILDER.define("TEWhitelist", false);
BUILDER.pop();
}
public static final ForgeConfigSpec SPEC = BUILDER.build();
}

View file

@ -1,22 +1,22 @@
package thetadev.constructionwand.basics;
import net.minecraft.stats.IStatFormatter;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.stats.StatFormatter;
import net.minecraft.stats.Stats;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.registry.Registry;
import thetadev.constructionwand.ConstructionWand;
public class ModStats
{
public static final ResourceLocation USE_WAND = new ResourceLocation(ConstructionWand.MODID, "use_wand");
public static final ResourceLocation USE_WAND = new ResourceLocation(ConstructionWand.MODID, "use_wand");
public static void register() {
registerStat(USE_WAND);
}
public static void register() {
registerStat(USE_WAND);
}
private static void registerStat(ResourceLocation registryName) {
// Compare with net.minecraft.stats.Stats#registerCustom
Registry.register(Registry.CUSTOM_STAT, registryName.getPath(), registryName);
Stats.CUSTOM.get(registryName, IStatFormatter.DEFAULT);
}
private static void registerStat(ResourceLocation registryName) {
// Compare with net.minecraft.stats.Stats#registerCustom
Registry.register(Registry.CUSTOM_STAT, registryName.getPath(), registryName);
Stats.CUSTOM.get(registryName, StatFormatter.DEFAULT);
}
}

View file

@ -0,0 +1,54 @@
package thetadev.constructionwand.basics;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.registries.ForgeRegistries;
import thetadev.constructionwand.ConstructionWand;
import java.util.HashSet;
import java.util.Set;
public class ReplacementRegistry
{
private static final HashSet<HashSet<Item>> replacements = new HashSet<>();
public static void init() {
for(Object key : ConfigServer.SIMILAR_BLOCKS.get()) {
if(!(key instanceof String)) continue;
HashSet<Item> set = new HashSet<>();
for(String id : ((String) key).split(";")) {
Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(id));
if(item == null || item == Items.AIR) {
ConstructionWand.LOGGER.warn("Replacement Registry: Could not find item " + id);
continue;
}
set.add(item);
}
if(!set.isEmpty()) replacements.add(set);
}
}
public static Set<Item> getMatchingSet(Item item) {
HashSet<Item> res = new HashSet<>();
for(HashSet<Item> set : replacements) {
if(set.contains(item)) res.addAll(set);
}
res.remove(item);
return res;
}
public static boolean matchBlocks(Block b1, Block b2) {
if(b1 == b2) return true;
if(b1 == Blocks.AIR || b2 == Blocks.AIR) return false;
for(HashSet<Item> set : replacements) {
if(set.contains(b1.asItem()) && set.contains(b2.asItem())) return true;
}
return false;
}
}

View file

@ -1,40 +1,233 @@
package thetadev.constructionwand.basics;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.tags.ItemTags;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.World;
import thetadev.constructionwand.basics.options.EnumMode;
import thetadev.constructionwand.basics.options.IEnumOption;
import thetadev.constructionwand.basics.options.WandOptions;
import thetadev.constructionwand.items.ItemWand;
import thetadev.constructionwand.job.ConstructionJob;
import thetadev.constructionwand.job.TransductionJob;
import thetadev.constructionwand.job.WandJob;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.world.BlockEvent;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.containers.ContainerManager;
import thetadev.constructionwand.items.wand.ItemWand;
import thetadev.constructionwand.wand.WandItemUseContext;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class WandUtil
{
public static boolean stackEquals(ItemStack stackA, ItemStack stackB) {
return ItemStack.areItemsEqual(stackA, stackB) && ItemStack.areItemStackTagsEqual(stackA, stackB);
}
public static boolean stackEquals(ItemStack stackA, ItemStack stackB) {
return ItemStack.isSameItemSameTags(stackA, stackB);
}
public static boolean stackEquals(ItemStack stackA, Item item) {
ItemStack stackB = new ItemStack(item);
return stackEquals(stackA, stackB);
}
public static boolean stackEquals(ItemStack stackA, Item item) {
ItemStack stackB = new ItemStack(item);
return stackEquals(stackA, stackB);
}
public static ItemStack holdingWand(PlayerEntity player) {
if(player.getHeldItem(Hand.MAIN_HAND) != ItemStack.EMPTY && player.getHeldItem(Hand.MAIN_HAND).getItem() instanceof ItemWand) {
return player.getHeldItem(Hand.MAIN_HAND);
}
else if(player.getHeldItem(Hand.OFF_HAND) != ItemStack.EMPTY && player.getHeldItem(Hand.OFF_HAND).getItem() instanceof ItemWand) {
return player.getHeldItem(Hand.OFF_HAND);
}
return null;
}
public static ItemStack holdingWand(Player player) {
if(player.getItemInHand(InteractionHand.MAIN_HAND) != ItemStack.EMPTY && player.getItemInHand(InteractionHand.MAIN_HAND).getItem() instanceof ItemWand) {
return player.getItemInHand(InteractionHand.MAIN_HAND);
}
else if(player.getItemInHand(InteractionHand.OFF_HAND) != ItemStack.EMPTY && player.getItemInHand(InteractionHand.OFF_HAND).getItem() instanceof ItemWand) {
return player.getItemInHand(InteractionHand.OFF_HAND);
}
return null;
}
public static BlockPos playerPos(Player player) {
return new BlockPos(player.position());
}
public static Vec3 entityPositionVec(Entity entity) {
return new Vec3(entity.getX(), entity.getY() - entity.getMyRidingOffset() + entity.getBbHeight() / 2, entity.getZ());
}
public static Vec3 blockPosVec(BlockPos pos) {
return new Vec3(pos.getX(), pos.getY(), pos.getZ());
}
public static List<ItemStack> getHotbar(Player player) {
return player.getInventory().items.subList(0, 9);
}
public static List<ItemStack> getHotbarWithOffhand(Player player) {
ArrayList<ItemStack> inventory = new ArrayList<>(player.getInventory().items.subList(0, 9));
inventory.addAll(player.getInventory().offhand);
return inventory;
}
public static List<ItemStack> getMainInv(Player player) {
return player.getInventory().items.subList(9, player.getInventory().items.size());
}
public static List<ItemStack> getFullInv(Player player) {
ArrayList<ItemStack> inventory = new ArrayList<>(player.getInventory().offhand);
inventory.addAll(player.getInventory().items);
return inventory;
}
public static int blockDistance(BlockPos p1, BlockPos p2) {
return Math.max(Math.abs(p1.getX() - p2.getX()), Math.abs(p1.getZ() - p2.getZ()));
}
public static boolean isTEAllowed(BlockState state) {
if(!state.hasBlockEntity()) return true;
ResourceLocation name = state.getBlock().getRegistryName();
if(name == null) return false;
String fullId = name.toString();
String modId = name.getNamespace();
boolean inList = ConfigServer.TE_LIST.get().contains(fullId) || ConfigServer.TE_LIST.get().contains(modId);
boolean isWhitelist = ConfigServer.TE_WHITELIST.get();
return isWhitelist == inList;
}
public static boolean placeBlock(Level world, Player player, BlockState block, BlockPos pos, @Nullable BlockItem item) {
if(!world.setBlockAndUpdate(pos, block)) {
ConstructionWand.LOGGER.info("Block could not be placed");
return false;
}
// Remove block if placeEvent is canceled
BlockSnapshot snapshot = BlockSnapshot.create(world.dimension(), world, pos);
BlockEvent.EntityPlaceEvent placeEvent = new BlockEvent.EntityPlaceEvent(snapshot, block, player);
MinecraftForge.EVENT_BUS.post(placeEvent);
if(placeEvent.isCanceled()) {
world.removeBlock(pos, false);
return false;
}
ItemStack stack;
if(item == null) stack = new ItemStack(block.getBlock().asItem());
else {
stack = new ItemStack(item);
player.awardStat(Stats.ITEM_USED.get(item));
}
// Call OnBlockPlaced method
block.getBlock().setPlacedBy(world, pos, block, player, stack);
return true;
}
public static boolean removeBlock(Level world, Player player, @Nullable BlockState block, BlockPos pos) {
BlockState currentBlock = world.getBlockState(pos);
if(!world.mayInteract(player, pos)) return false;
if(!player.isCreative()) {
if(currentBlock.getDestroySpeed(world, pos) <= -1 || world.getBlockEntity(pos) != null) return false;
if(block != null)
if(!ReplacementRegistry.matchBlocks(currentBlock.getBlock(), block.getBlock())) return false;
}
BlockEvent.BreakEvent breakEvent = new BlockEvent.BreakEvent(world, pos, currentBlock, player);
MinecraftForge.EVENT_BUS.post(breakEvent);
if(breakEvent.isCanceled()) return false;
world.removeBlock(pos, false);
return true;
}
public static int countItem(Player player, Item item) {
if(player.getInventory().items == null) return 0;
if(player.isCreative()) return Integer.MAX_VALUE;
int total = 0;
ContainerManager containerManager = ConstructionWand.instance.containerManager;
List<ItemStack> inventory = WandUtil.getFullInv(player);
for(ItemStack stack : inventory) {
if(stack == null || stack.isEmpty()) continue;
if(WandUtil.stackEquals(stack, item)) {
total += stack.getCount();
}
else {
int amount = containerManager.countItems(player, new ItemStack(item), stack);
if(amount == Integer.MAX_VALUE) return Integer.MAX_VALUE;
total += amount;
}
}
return total;
}
private static boolean isPositionModifiable(Level world, Player player, BlockPos pos) {
// Is position out of world?
if(!world.isInWorldBounds(pos)) return false;
// Is block modifiable?
if(!world.mayInteract(player, pos)) return false;
// Limit range
if(ConfigServer.MAX_RANGE.get() > 0 &&
WandUtil.blockDistance(player.blockPosition(), pos) > ConfigServer.MAX_RANGE.get()) return false;
return true;
}
/**
* Tests if a wand can place a block at a certain position.
* This check is independent of the used block.
*/
public static boolean isPositionPlaceable(Level world, Player player, BlockPos pos, boolean replace) {
if(!isPositionModifiable(world, player, pos)) return false;
// If replace mode is off, target has to be air
if(world.isEmptyBlock(pos)) return true;
// Otherwise, check if the block can be replaced by a generic block
return replace && world.getBlockState(pos).canBeReplaced(
new WandItemUseContext(world, player,
new BlockHitResult(new Vec3(0, 0, 0), Direction.DOWN, pos, false),
pos, (BlockItem) Items.STONE));
}
public static boolean isBlockRemovable(Level world, Player player, BlockPos pos) {
if(!isPositionModifiable(world, player, pos)) return false;
if(!player.isCreative()) {
return !(world.getBlockState(pos).getDestroySpeed(world, pos) <= -1) && world.getBlockEntity(pos) == null;
}
return true;
}
public static boolean isBlockPermeable(Level world, BlockPos pos) {
return world.isEmptyBlock(pos) || world.getBlockState(pos).getCollisionShape(world, pos).isEmpty();
}
public static boolean entitiesCollidingWithBlock(Level world, BlockState blockState, BlockPos pos) {
VoxelShape shape = blockState.getCollisionShape(world, pos);
if(!shape.isEmpty()) {
AABB blockBB = shape.bounds().move(pos);
return !world.getEntitiesOfClass(LivingEntity.class, blockBB, Predicate.not(Entity::isSpectator)).isEmpty();
}
return false;
}
public static Direction fromVector(Vec3 vector) {
return Direction.getNearest(vector.x, vector.y, vector.z);
}
}

View file

@ -0,0 +1,36 @@
package thetadev.constructionwand.basics.option;
import thetadev.constructionwand.ConstructionWand;
public interface IOption<T>
{
String getKey();
String getValueString();
void setValueString(String val);
default String getKeyTranslation() {
return ConstructionWand.MODID + ".option." + getKey();
}
default String getValueTranslation() {
return ConstructionWand.MODID + ".option." + getKey() + "." + getValueString();
}
default String getDescTranslation() {
return ConstructionWand.MODID + ".option." + getKey() + "." + getValueString() + ".desc";
}
boolean isEnabled();
void set(T val);
T get();
T next(boolean dir);
default T next() {
return next(true);
}
}

View file

@ -0,0 +1,62 @@
package thetadev.constructionwand.basics.option;
import net.minecraft.nbt.CompoundTag;
public class OptionBoolean implements IOption<Boolean>
{
private final CompoundTag tag;
private final String key;
private final boolean enabled;
private boolean value;
public OptionBoolean(CompoundTag tag, String key, boolean dval, boolean enabled) {
this.tag = tag;
this.key = key;
this.enabled = enabled;
if(tag.contains(key)) value = tag.getBoolean(key);
else value = dval;
}
public OptionBoolean(CompoundTag tag, String key, boolean dval) {
this(tag, key, dval, true);
}
@Override
public String getKey() {
return key;
}
@Override
public String getValueString() {
return value ? "yes" : "no";
}
@Override
public void setValueString(String val) {
set(val.equals("yes"));
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void set(Boolean val) {
if(!enabled) return;
value = val;
tag.putBoolean(key, value);
}
@Override
public Boolean get() {
return value;
}
@Override
public Boolean next(boolean dir) {
set(!value);
return value;
}
}

View file

@ -0,0 +1,69 @@
package thetadev.constructionwand.basics.option;
import com.google.common.base.Enums;
import net.minecraft.nbt.CompoundTag;
public class OptionEnum<E extends Enum<E>> implements IOption<E>
{
private final CompoundTag tag;
private final String key;
private final Class<E> enumClass;
private final boolean enabled;
private final E dval;
private E value;
public OptionEnum(CompoundTag tag, String key, Class<E> enumClass, E dval, boolean enabled) {
this.tag = tag;
this.key = key;
this.enumClass = enumClass;
this.enabled = enabled;
this.dval = dval;
value = Enums.getIfPresent(enumClass, tag.getString(key).toUpperCase()).or(dval);
}
public OptionEnum(CompoundTag tag, String key, Class<E> enumClass, E dval) {
this(tag, key, enumClass, dval, true);
}
@Override
public String getKey() {
return key;
}
@Override
public String getValueString() {
return value.name().toLowerCase();
}
@Override
public void setValueString(String val) {
set(Enums.getIfPresent(enumClass, val.toUpperCase()).or(dval));
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void set(E val) {
if(!enabled) return;
value = val;
tag.putString(key, getValueString());
}
@Override
public E get() {
return value;
}
@Override
public E next(boolean dir) {
E[] enumValues = enumClass.getEnumConstants();
int i = value.ordinal() + (dir ? 1 : -1);
if(i < 0) i += enumValues.length;
set(enumValues[i % enumValues.length]);
return value;
}
}

View file

@ -0,0 +1,100 @@
package thetadev.constructionwand.basics.option;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import thetadev.constructionwand.api.IWandCore;
import thetadev.constructionwand.api.IWandUpgrade;
import thetadev.constructionwand.basics.ReplacementRegistry;
import thetadev.constructionwand.items.core.CoreDefault;
import javax.annotation.Nullable;
public class WandOptions
{
public final CompoundTag tag;
private static final String TAG_ROOT = "wand_options";
public enum LOCK
{
HORIZONTAL,
VERTICAL,
NORTHSOUTH,
EASTWEST,
NOLOCK
}
public enum DIRECTION
{
TARGET,
PLAYER
}
public enum MATCH
{
EXACT,
SIMILAR,
ANY
}
public final WandUpgradesSelectable<IWandCore> cores;
public final OptionEnum<LOCK> lock;
public final OptionEnum<DIRECTION> direction;
public final OptionBoolean replace;
public final OptionEnum<MATCH> match;
public final OptionBoolean random;
public final IOption<?>[] allOptions;
public WandOptions(ItemStack wandStack) {
tag = wandStack.getOrCreateTagElement(TAG_ROOT);
cores = new WandUpgradesSelectable<>(tag, "cores", new CoreDefault());
lock = new OptionEnum<>(tag, "lock", LOCK.class, LOCK.NOLOCK);
direction = new OptionEnum<>(tag, "direction", DIRECTION.class, DIRECTION.TARGET);
replace = new OptionBoolean(tag, "replace", true);
match = new OptionEnum<>(tag, "match", MATCH.class, MATCH.SIMILAR);
random = new OptionBoolean(tag, "random", false);
allOptions = new IOption[]{cores, lock, direction, replace, match, random};
}
@Nullable
public IOption<?> get(String key) {
for(IOption<?> option : allOptions) {
if(option.getKey().equals(key)) return option;
}
return null;
}
public boolean testLock(LOCK l) {
if(lock.get() == LOCK.NOLOCK) return true;
return lock.get() == l;
}
public boolean matchBlocks(Block b1, Block b2) {
switch(match.get()) {
case EXACT:
return b1 == b2;
case SIMILAR:
return ReplacementRegistry.matchBlocks(b1, b2);
case ANY:
return b1 != Blocks.AIR && b2 != Blocks.AIR;
}
return false;
}
public boolean hasUpgrade(IWandUpgrade upgrade) {
if(upgrade instanceof IWandCore) return cores.hasUpgrade((IWandCore) upgrade);
return false;
}
public boolean addUpgrade(IWandUpgrade upgrade) {
if(upgrade instanceof IWandCore) return cores.addUpgrade((IWandCore) upgrade);
return false;
}
}

View file

@ -0,0 +1,79 @@
package thetadev.constructionwand.basics.option;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraftforge.registries.ForgeRegistries;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.api.IWandUpgrade;
import java.util.ArrayList;
public class WandUpgrades<T extends IWandUpgrade>
{
protected final CompoundTag tag;
protected final String key;
protected final ArrayList<T> upgrades;
protected final T dval;
public WandUpgrades(CompoundTag tag, String key, T dval) {
this.tag = tag;
this.key = key;
this.dval = dval;
upgrades = new ArrayList<>();
if(dval != null) upgrades.add(0, dval);
deserialize();
}
protected void deserialize() {
ListTag listnbt = tag.getList(key, Tag.TAG_STRING);
boolean require_fix = false;
for(int i = 0; i < listnbt.size(); i++) {
String str = listnbt.getString(i);
Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(str));
T data;
try {
//noinspection unchecked
data = (T) item;
upgrades.add(data);
} catch(ClassCastException e) {
ConstructionWand.LOGGER.warn("Invalid wand upgrade: " + str);
require_fix = true;
}
}
if(require_fix) serialize();
}
protected void serialize() {
ListTag listnbt = new ListTag();
for(T item : upgrades) {
if(item == dval) continue;
listnbt.add(StringTag.valueOf(item.getRegistryName().toString()));
}
tag.put(key, listnbt);
}
public boolean addUpgrade(T upgrade) {
if(hasUpgrade(upgrade)) return false;
upgrades.add(upgrade);
serialize();
return true;
}
public boolean hasUpgrade(T upgrade) {
return upgrades.contains(upgrade);
}
public ArrayList<T> getUpgrades() {
return upgrades;
}
}

View file

@ -0,0 +1,83 @@
package thetadev.constructionwand.basics.option;
import net.minecraft.nbt.CompoundTag;
import thetadev.constructionwand.api.IWandUpgrade;
public class WandUpgradesSelectable<T extends IWandUpgrade> extends WandUpgrades<T> implements IOption<T>
{
private byte selector;
public WandUpgradesSelectable(CompoundTag tag, String key, T dval) {
super(tag, key, dval);
}
@Override
public String getKey() {
return key;
}
@Override
public String getValueString() {
return get().getRegistryName().toString();
}
@Override
public void setValueString(String val) {
for(byte i = 0; i < upgrades.size(); i++) {
if(upgrades.get(i).getRegistryName().toString().equals(val)) {
selector = i;
serializeSelector();
return;
}
}
}
@Override
public boolean isEnabled() {
return upgrades.size() > 1;
}
@Override
public void set(T val) {
selector = (byte) upgrades.indexOf(val);
fixSelector();
serializeSelector();
}
@Override
public T get() {
fixSelector();
return upgrades.get(selector);
}
@Override
public T next(boolean dir) {
selector++;
fixSelector();
serializeSelector();
return get();
}
private void fixSelector() {
if(selector < 0 || selector >= upgrades.size()) selector = 0;
}
@Override
protected void deserialize() {
super.deserialize();
selector = tag.getByte(key + "_sel");
fixSelector();
}
@Override
protected void serialize() {
super.serialize();
serializeSelector();
}
private void serializeSelector() {
tag.putByte(key + "_sel", selector);
}
}

View file

@ -1,38 +0,0 @@
package thetadev.constructionwand.basics.options;
import com.google.common.base.Enums;
public enum EnumDirection implements IEnumOption
{
TARGET,
PLAYER;
private static EnumDirection[] vals = values();
public IEnumOption fromName(String name) {
return Enums.getIfPresent(EnumDirection.class, name.toUpperCase()).or(this);
}
public EnumDirection next(boolean dir) {
int i = this.ordinal() + (dir ? 1:-1);
if(i < 0) i += vals.length;
return vals[i % vals.length];
}
public int getOrdinal() {
return ordinal();
}
public String getOptionKey() {
return "direction";
}
public String getValue() {
return this.name().toLowerCase();
}
public String getTranslationKey() {
return getOptionKey() + "." + getValue();
}
}

View file

@ -1,46 +0,0 @@
package thetadev.constructionwand.basics.options;
import com.google.common.base.Enums;
public enum EnumLock implements IEnumOption
{
HORIZONTAL,
VERTICAL,
NORTHSOUTH,
EASTWEST,
NOLOCK;
private static EnumLock[] vals = values();
public IEnumOption fromName(String name) {
return Enums.getIfPresent(EnumLock.class, name.toUpperCase()).or(this);
}
public EnumLock next(boolean dir) {
int i = this.ordinal() + (dir ? 1:-1);
if(i < 0) i += vals.length;
return vals[i % vals.length];
}
public int getOrdinal() {
return ordinal();
}
public String getOptionKey() {
return "lock";
}
public String getValue() {
return this.name().toLowerCase();
}
public String getTranslationKey() {
return getOptionKey() + "." + getValue();
}
public boolean test(EnumLock lock) {
if(this == NOLOCK) return true;
return this == lock;
}
}

View file

@ -1,38 +0,0 @@
package thetadev.constructionwand.basics.options;
import com.google.common.base.Enums;
public enum EnumMode implements IEnumOption
{
DEFAULT,
ANGEL;
private static EnumMode[] vals = values();
public IEnumOption fromName(String name) {
return Enums.getIfPresent(EnumMode.class, name.toUpperCase()).or(this);
}
public EnumMode next(boolean dir) {
int i = this.ordinal() + (dir ? 1:-1);
if(i < 0) i += vals.length;
return vals[i % vals.length];
}
public int getOrdinal() {
return ordinal();
}
public String getOptionKey() {
return "mode";
}
public String getValue() {
return this.name().toLowerCase();
}
public String getTranslationKey() {
return getOptionKey() + "." + getValue();
}
}

View file

@ -1,38 +0,0 @@
package thetadev.constructionwand.basics.options;
import com.google.common.base.Enums;
public enum EnumReplace implements IEnumOption
{
YES,
NO;
private static EnumReplace[] vals = values();
public IEnumOption fromName(String name) {
return Enums.getIfPresent(EnumReplace.class, name.toUpperCase()).or(this);
}
public EnumReplace next(boolean dir) {
int i = this.ordinal() + (dir ? 1:-1);
if(i < 0) i += vals.length;
return vals[i % vals.length];
}
public int getOrdinal() {
return ordinal();
}
public String getOptionKey() {
return "replace";
}
public String getValue() {
return this.name().toLowerCase();
}
public String getTranslationKey() {
return getOptionKey() + "." + getValue();
}
}

View file

@ -1,11 +0,0 @@
package thetadev.constructionwand.basics.options;
public interface IEnumOption
{
public int getOrdinal();
public String getOptionKey();
public String getValue();
public String getTranslationKey();
public IEnumOption next(boolean dir);
public IEnumOption fromName(String name);
}

View file

@ -1,44 +0,0 @@
package thetadev.constructionwand.basics.options;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import thetadev.constructionwand.items.ItemWand;
public class WandOptions
{
private ItemWand item;
private CompoundNBT tag;
private final String TAG_ROOT = "wand_options";
public static final IEnumOption[] options = {
EnumMode.DEFAULT,
EnumLock.NOLOCK,
EnumDirection.TARGET,
EnumReplace.YES
};
public WandOptions(ItemStack stack) {
this.item = (ItemWand) stack.getItem();
this.tag = stack.getOrCreateChildTag(TAG_ROOT);
}
public IEnumOption getOption(IEnumOption option) {
return option.fromName(tag.getString(option.getOptionKey()));
}
public void setOption(IEnumOption option) {
tag.putString(option.getOptionKey(), option.getValue());
}
public IEnumOption nextOption(IEnumOption option, boolean dir) {
IEnumOption nextOption = getOption(option).next(dir);
if(nextOption == EnumMode.ANGEL && item.angelDistance == 0) nextOption = EnumMode.DEFAULT;
setOption(nextOption);
return nextOption;
}
public IEnumOption nextOption(IEnumOption option) {
return nextOption(option, true);
}
}

View file

@ -0,0 +1,15 @@
package thetadev.constructionwand.basics.pool;
import javax.annotation.Nullable;
public interface IPool<T>
{
void add(T element);
void remove(T element);
@Nullable
T draw();
void reset();
}

View file

@ -0,0 +1,39 @@
package thetadev.constructionwand.basics.pool;
import javax.annotation.Nullable;
import java.util.ArrayList;
public class OrderedPool<T> implements IPool<T>
{
private final ArrayList<T> elements;
private int index;
public OrderedPool() {
elements = new ArrayList<>();
reset();
}
@Override
public void add(T element) {
elements.add(element);
}
@Override
public void remove(T element) {
elements.remove(element);
}
@Nullable
@Override
public T draw() {
if(index >= elements.size()) return null;
T e = elements.get(index);
index++;
return e;
}
@Override
public void reset() {
index = 0;
}
}

View file

@ -0,0 +1,60 @@
package thetadev.constructionwand.basics.pool;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
public class RandomPool<T> implements IPool<T>
{
private final Random rng;
private final HashMap<T, Integer> elements;
private HashSet<T> pool;
public RandomPool(Random rng) {
this.rng = rng;
elements = new HashMap<>();
reset();
}
@Override
public void add(T element) {
addWithWeight(element, 1);
}
@Override
public void remove(T element) {
elements.remove(element);
pool.remove(element);
}
public void addWithWeight(T element, int weight) {
if(weight < 1) return;
elements.merge(element, weight, Integer::sum);
pool.add(element);
}
@Nullable
@Override
public T draw() {
int allWeights = pool.stream().reduce(0, (partialRes, e) -> partialRes + elements.get(e), Integer::sum);
if(allWeights < 1) return null;
int random = rng.nextInt(allWeights);
int accWeight = 0;
for(T e : pool) {
accWeight += elements.get(e);
if(random < accWeight) {
pool.remove(e);
return e;
}
}
return null;
}
@Override
public void reset() {
pool = new HashSet<>(elements.keySet());
}
}

View file

@ -0,0 +1,105 @@
package thetadev.constructionwand.client;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.ConfigClient;
import thetadev.constructionwand.basics.WandUtil;
import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.items.wand.ItemWand;
import thetadev.constructionwand.network.PacketQueryUndo;
import thetadev.constructionwand.network.PacketWandOption;
public class ClientEvents
{
private boolean optPressed;
public ClientEvents() {
optPressed = false;
}
// Send state of OPT key to server
@SubscribeEvent
public void KeyEvent(InputEvent.KeyInputEvent event) {
Player player = Minecraft.getInstance().player;
if(player == null) return;
if(WandUtil.holdingWand(player) == null) return;
boolean optState = isOptKeyDown();
if(optPressed != optState) {
optPressed = optState;
PacketQueryUndo packet = new PacketQueryUndo(optPressed);
ConstructionWand.instance.HANDLER.sendToServer(packet);
//ConstructionWand.LOGGER.debug("OPT key update: " + optPressed);
}
}
// Sneak+(OPT)+Scroll to change direction lock
@SubscribeEvent(priority = EventPriority.HIGHEST)
public void MouseScrollEvent(InputEvent.MouseScrollEvent event) {
Player player = Minecraft.getInstance().player;
double scroll = event.getScrollDelta();
if(player == null || !modeKeyCombDown(player) || scroll == 0) return;
ItemStack wand = WandUtil.holdingWand(player);
if(wand == null) return;
WandOptions wandOptions = new WandOptions(wand);
wandOptions.lock.next(scroll < 0);
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.lock, true));
event.setCanceled(true);
}
// Sneak+(OPT)+Left click wand to change core
@SubscribeEvent
public void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) {
Player player = event.getPlayer();
if(player == null || !modeKeyCombDown(player)) return;
ItemStack wand = event.getItemStack();
if(!(wand.getItem() instanceof ItemWand)) return;
WandOptions wandOptions = new WandOptions(wand);
wandOptions.cores.next();
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.cores, true));
}
// Sneak+(OPT)+Right click wand to open GUI
@SubscribeEvent
public void onRightClickItem(PlayerInteractEvent.RightClickItem event) {
if(event.getSide().isServer()) return;
Player player = event.getPlayer();
if(player == null || !guiKeyCombDown(player)) return;
ItemStack wand = event.getItemStack();
if(!(wand.getItem() instanceof ItemWand)) return;
Minecraft.getInstance().setScreen(new ScreenWand(wand));
event.setCanceled(true);
}
private static boolean isKeyDown(int id) {
return InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), id);
}
public static boolean isOptKeyDown() {
return isKeyDown(ConfigClient.OPT_KEY.get());
}
public static boolean modeKeyCombDown(Player player) {
return player.isCrouching() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_MODE.get());
}
public static boolean guiKeyCombDown(Player player) {
return player.isCrouching() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_GUI.get());
}
}

View file

@ -1,97 +0,0 @@
package thetadev.constructionwand.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.util.InputMappings;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.client.event.InputUpdateEvent;
import net.minecraftforge.client.settings.KeyConflictContext;
import net.minecraftforge.client.settings.KeyModifier;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import org.lwjgl.glfw.GLFW;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.*;
import thetadev.constructionwand.basics.options.EnumDirection;
import thetadev.constructionwand.basics.options.EnumReplace;
import thetadev.constructionwand.basics.options.EnumLock;
import thetadev.constructionwand.basics.options.IEnumOption;
import thetadev.constructionwand.network.PacketQueryUndo;
import thetadev.constructionwand.network.PacketWandOption;
public class KeyEvents
{
private final String langPrefix = ConstructionWand.MODID + ".key.";
private final String langCategory = langPrefix + "category";
public final KeyBinding[] keys = {
new KeyBinding(langPrefix+"direction", KeyConflictContext.IN_GAME, InputMappings.getInputByCode(GLFW.GLFW_KEY_N, 0), langCategory),
new KeyBinding(langPrefix+"replace", KeyConflictContext.IN_GAME, KeyModifier.SHIFT, InputMappings.getInputByCode(GLFW.GLFW_KEY_N, 0), langCategory)
};
public static final IEnumOption[] keyOptions = {
EnumDirection.TARGET,
EnumReplace.YES
};
private boolean ctrlPressed;
public KeyEvents() {
for(KeyBinding key : keys) ClientRegistry.registerKeyBinding(key);
ctrlPressed = false;
}
@SubscribeEvent
public void KeyEvent(InputEvent.KeyInputEvent e) {
PlayerEntity player = Minecraft.getInstance().player;
if(player == null) return;
if(WandUtil.holdingWand(player) == null) return;
for(int i=0; i<keyOptions.length; i++) {
if(keys[i].isPressed()) {
PacketWandOption packet = new PacketWandOption(keyOptions[i], true);
ConstructionWand.instance.HANDLER.sendToServer(packet);
}
}
boolean ctrlState = Screen.hasControlDown();
if(ctrlPressed != ctrlState) {
ctrlPressed = ctrlState;
PacketQueryUndo packet = new PacketQueryUndo(ctrlPressed);
ConstructionWand.instance.HANDLER.sendToServer(packet);
//ConstructionWand.LOGGER.debug("CTRL key update: "+ctrlPressed);
}
}
@SubscribeEvent(priority = EventPriority.HIGHEST)
public void MouseScrollEvent(InputEvent.MouseScrollEvent e) {
Minecraft minecraft = Minecraft.getInstance();
PlayerEntity player = minecraft.player;
double scroll = e.getScrollDelta();
if(player == null || !player.isSneaking() || scroll == 0 || WandUtil.holdingWand(player) == null) return;
PacketWandOption packet = new PacketWandOption(EnumLock.NOLOCK, scroll<0);
ConstructionWand.instance.HANDLER.sendToServer(packet);
e.setCanceled(true);
}
// Send undo blocks to player sneaking with wand
/*
@SubscribeEvent
public void sneak(InputUpdateEvent e) {
if(e.getMovementInput().sneaking) {
PlayerEntity player = e.getPlayer();
if(WandUtil.holdingWand(player) == null) return;
PacketQueryUndo packet = new PacketQueryUndo();
ConstructionWand.instance.HANDLER.sendToServer(packet);
}
}*/
}

View file

@ -1,77 +1,78 @@
package thetadev.constructionwand.client;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraftforge.client.event.DrawHighlightEvent;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraftforge.client.event.DrawSelectionEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import thetadev.constructionwand.basics.WandUtil;
import thetadev.constructionwand.job.WandJob;
import thetadev.constructionwand.items.wand.ItemWand;
import thetadev.constructionwand.wand.WandJob;
import java.util.LinkedList;
import java.util.Set;
public class RenderBlockPreview
{
public WandJob wandJob;
public LinkedList<BlockPos> undoBlocks;
private WandJob wandJob;
public Set<BlockPos> undoBlocks;
@SubscribeEvent
public void renderBlockHighlight(DrawHighlightEvent event)
{
if(event.getTarget().getType() != RayTraceResult.Type.BLOCK) return;
@SubscribeEvent
public void renderBlockHighlight(DrawSelectionEvent.HighlightBlock event) {
if(event.getTarget().getType() != HitResult.Type.BLOCK) return;
BlockRayTraceResult rtr = (BlockRayTraceResult) event.getTarget();
Entity entity = event.getInfo().getRenderViewEntity();
if(!(entity instanceof PlayerEntity)) return;
PlayerEntity player = (PlayerEntity) entity;
LinkedList<BlockPos> blocks;
float colorR=0, colorG=0, colorB=0;
BlockHitResult rtr = event.getTarget();
Entity entity = event.getCamera().getEntity();
if(!(entity instanceof Player player)) return;
Set<BlockPos> blocks;
float colorR = 0, colorG = 0, colorB = 0;
ItemStack wand = WandUtil.holdingWand(player);
if(wand == null) return;
ItemStack wand = WandUtil.holdingWand(player);
if(wand == null) return;
if(!(player.isSneaking() && Screen.hasControlDown())) {
if(wandJob == null || !(wandJob.getRayTraceResult().equals(rtr)) || !(wandJob.getWand().equals(wand))) {
wandJob = WandJob.getJob(player, player.getEntityWorld(), rtr, wand);
}
if(!(player.isCrouching() && ClientEvents.isOptKeyDown())) {
// Use cached wandJob for previews of the same target pos/dir
// Exception: always update if blockCount < 2 to prevent 1-block previews when block updates
// from the last placement are lagging
if(wandJob == null || !compareRTR(wandJob.rayTraceResult, rtr) || !(wandJob.wand.equals(wand))
|| wandJob.blockCount() < 2) {
wandJob = ItemWand.getWandJob(player, player.level, rtr, wand);
}
blocks = wandJob.getBlockPositions();
}
else {
blocks = undoBlocks;
colorG = 1;
}
blocks = wandJob.getBlockPositions();
}
else {
blocks = undoBlocks;
colorG = 1;
}
if(blocks == null || blocks.isEmpty()) return;
if(blocks == null || blocks.isEmpty()) return;
PoseStack ms = event.getPoseStack();
MultiBufferSource buffer = event.getMultiBufferSource();
VertexConsumer lineBuilder = buffer.getBuffer(RenderType.LINES);
renderBlockList(blocks, event.getMatrix(), event.getBuffers(), colorR, colorG, colorB);
double partialTicks = event.getPartialTicks();
double d0 = player.xOld + (player.getX() - player.xOld) * partialTicks;
double d1 = player.yOld + player.getEyeHeight() + (player.getY() - player.yOld) * partialTicks;
double d2 = player.zOld + (player.getZ() - player.zOld) * partialTicks;
event.setCanceled(true);
}
for(BlockPos block : blocks) {
AABB aabb = new AABB(block).move(-d0, -d1, -d2);
LevelRenderer.renderLineBox(ms, lineBuilder, aabb, colorR, colorG, colorB, 0.4F);
}
private void renderBlockList(LinkedList<BlockPos> blocks, MatrixStack ms, IRenderTypeBuffer buffer, float red, float green, float blue) {
double renderPosX = Minecraft.getInstance().getRenderManager().info.getProjectedView().getX();
double renderPosY = Minecraft.getInstance().getRenderManager().info.getProjectedView().getY();
double renderPosZ = Minecraft.getInstance().getRenderManager().info.getProjectedView().getZ();
event.setCanceled(true);
}
ms.push();
ms.translate(-renderPosX, -renderPosY, -renderPosZ);
for(BlockPos block : blocks) {
AxisAlignedBB aabb = new AxisAlignedBB(block);
IVertexBuilder lineBuilder = buffer.getBuffer(RenderTypes.TRANSLUCENT_LINES);
WorldRenderer.drawBoundingBox(ms, lineBuilder, aabb, red, green, blue, 0.4F);
}
ms.pop();
}
private static boolean compareRTR(BlockHitResult rtr1, BlockHitResult rtr2) {
return rtr1.getBlockPos().equals(rtr2.getBlockPos()) && rtr1.getDirection().equals(rtr2.getDirection());
}
}

View file

@ -1,37 +0,0 @@
package thetadev.constructionwand.client;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import org.lwjgl.opengl.GL11;
import thetadev.constructionwand.ConstructionWand;
import java.util.OptionalDouble;
public class RenderTypes
{
public static final RenderType TRANSLUCENT_LINES;
protected static final RenderState.TransparencyState TRANSLUCENT_TRANSPARENCY = new RenderState.TransparencyState("translucent_transparency", () -> {
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
}, RenderSystem::disableBlend);
protected static final RenderState.DepthTestState DEPTH_ALWAYS = new RenderState.DepthTestState(GL11.GL_ALWAYS);
static {
RenderType.State translucentNoDepthState = RenderType.State.getBuilder().transparency(TRANSLUCENT_TRANSPARENCY)
.line(new RenderState.LineState(OptionalDouble.of(2)))
.texture(new RenderState.TextureState())
.depthTest(DEPTH_ALWAYS)
.build(false);
TRANSLUCENT_LINES = RenderType.makeType(
ConstructionWand.MODID+":translucent_lines",
DefaultVertexFormats.POSITION_COLOR,
GL11.GL_LINES,
256,
translucentNoDepthState
);
}
}

View file

@ -0,0 +1,95 @@
package thetadev.constructionwand.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.item.ItemStack;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.option.IOption;
import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.network.PacketWandOption;
import javax.annotation.Nonnull;
public class ScreenWand extends Screen
{
private final ItemStack wand;
private final WandOptions wandOptions;
private static final int BUTTON_WIDTH = 160;
private static final int BUTTON_HEIGHT = 20;
private static final int SPACING_WIDTH = 50;
private static final int SPACING_HEIGHT = 30;
private static final int N_COLS = 2;
private static final int N_ROWS = 3;
private static final int FIELD_WIDTH = N_COLS * (BUTTON_WIDTH + SPACING_WIDTH) - SPACING_WIDTH;
private static final int FIELD_HEIGHT = N_ROWS * (BUTTON_HEIGHT + SPACING_HEIGHT) - SPACING_HEIGHT;
public ScreenWand(ItemStack wand) {
super(new TextComponent("ScreenWand"));
this.wand = wand;
wandOptions = new WandOptions(wand);
}
@Override
protected void init() {
createButton(0, 0, wandOptions.cores);
createButton(0, 1, wandOptions.lock);
createButton(0, 2, wandOptions.direction);
createButton(1, 0, wandOptions.replace);
createButton(1, 1, wandOptions.match);
createButton(1, 2, wandOptions.random);
}
@Override
public void render(@Nonnull PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
renderBackground(matrixStack);
drawCenteredString(matrixStack, font, wand.getDisplayName(), width / 2, height / 2 - FIELD_HEIGHT / 2 - SPACING_HEIGHT, 16777215);
super.render(matrixStack, mouseX, mouseY, partialTicks);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (Minecraft.getInstance().options.keyInventory.matches(keyCode, scanCode)) {
this.onClose();
return true;
} else {
return super.keyPressed(keyCode, scanCode, modifiers);
}
}
private void createButton(int cx, int cy, IOption<?> option) {
Button button = new Button(getX(cx), getY(cy), BUTTON_WIDTH, BUTTON_HEIGHT, getButtonLabel(option), bt -> clickButton(bt, option), (bt, ms, x, y) -> drawTooltip(ms, x, y, option));
button.active = option.isEnabled();
addRenderableWidget(button);
}
private void clickButton(Button button, IOption<?> option) {
option.next();
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(option, false));
button.setMessage(getButtonLabel(option));
}
private void drawTooltip(PoseStack matrixStack, int mouseX, int mouseY, IOption<?> option) {
if(isMouseOver(mouseX, mouseY)) {
renderTooltip(matrixStack, new TranslatableComponent(option.getDescTranslation()), mouseX, mouseY);
}
}
private int getX(int n) {
return width / 2 - FIELD_WIDTH / 2 + n * (BUTTON_WIDTH + SPACING_WIDTH);
}
private int getY(int n) {
return height / 2 - FIELD_HEIGHT / 2 + n * (BUTTON_HEIGHT + SPACING_HEIGHT);
}
private Component getButtonLabel(IOption<?> option) {
return new TranslatableComponent(option.getKeyTranslation()).append(new TranslatableComponent(option.getValueTranslation()));
}
}

View file

@ -1,37 +1,38 @@
package thetadev.constructionwand.containers;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import thetadev.constructionwand.api.IContainerHandler;
import java.util.ArrayList;
public class ContainerManager {
private ArrayList<IContainerHandler> handlers;
public class ContainerManager
{
private final ArrayList<IContainerHandler> handlers;
public ContainerManager() {
handlers = new ArrayList<IContainerHandler>();
}
public ContainerManager() {
handlers = new ArrayList<IContainerHandler>();
}
public boolean register(IContainerHandler handler) {
return handlers.add(handler);
}
public boolean register(IContainerHandler handler) {
return handlers.add(handler);
}
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
for(IContainerHandler handler : handlers) {
if(handler.matches(player, itemStack, inventoryStack)) {
return handler.countItems(player,itemStack, inventoryStack);
}
}
return 0;
}
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
for(IContainerHandler handler : handlers) {
if(handler.matches(player, itemStack, inventoryStack)) {
return handler.countItems(player, itemStack, inventoryStack);
}
}
return 0;
}
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) {
for(IContainerHandler handler : handlers) {
if(handler.matches(player, itemStack, inventoryStack)) {
return handler.useItems(player, itemStack, inventoryStack, count);
}
}
return count;
}
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
for(IContainerHandler handler : handlers) {
if(handler.matches(player, itemStack, inventoryStack)) {
return handler.useItems(player, itemStack, inventoryStack, count);
}
}
return count;
}
}

View file

@ -2,17 +2,21 @@ package thetadev.constructionwand.containers;
import net.minecraftforge.fml.ModList;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.containers.handlers.*;
import thetadev.constructionwand.containers.handlers.HandlerBotania;
import thetadev.constructionwand.containers.handlers.HandlerBundle;
import thetadev.constructionwand.containers.handlers.HandlerCapability;
import thetadev.constructionwand.containers.handlers.HandlerShulkerbox;
public class ContainerRegistrar
{
public static void register() {
ConstructionWand.instance.containerManager.register(new HandlerCapability());
ConstructionWand.instance.containerManager.register(new HandlerShulkerbox());
public static void register() {
ConstructionWand.instance.containerManager.register(new HandlerCapability());
ConstructionWand.instance.containerManager.register(new HandlerShulkerbox());
ConstructionWand.instance.containerManager.register(new HandlerBundle());
if(ModList.get().isLoaded("botania")) {
ConstructionWand.instance.containerManager.register(new HandlerBotania());
ConstructionWand.LOGGER.info("Botania integration added");
}
}
if(ModList.get().isLoaded("botania")) {
ConstructionWand.instance.containerManager.register(new HandlerBotania());
ConstructionWand.LOGGER.info("Botania integration added");
}
}
}

View file

@ -1,35 +1,41 @@
package thetadev.constructionwand.containers.handlers;
import net.minecraft.block.Block;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import thetadev.constructionwand.api.IContainerHandler;
import vazkii.botania.api.BotaniaForgeCapabilities;
import vazkii.botania.api.item.IBlockProvider;
/**
* Created by james on 28/12/16.
*/
import java.util.Optional;
public class HandlerBotania implements IContainerHandler
{
@Override
public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
return inventoryStack != null && inventoryStack.getCount() == 1 && inventoryStack.getItem() instanceof IBlockProvider;
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
return inventoryStack != null && inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).isPresent();
}
@Override
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
IBlockProvider prov = (IBlockProvider) inventoryStack.getItem();
int provCount = prov.getBlockCount(player, itemStack, inventoryStack, Block.getBlockFromItem(itemStack.getItem()));
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
Optional<IBlockProvider> provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve();
if(provOptional.isEmpty()) return 0;
IBlockProvider prov = provOptional.get();
int provCount = prov.getBlockCount(player, inventoryStack, Block.byItem(itemStack.getItem()));
if(provCount == -1)
return Integer.MAX_VALUE;
return provCount;
}
@Override
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) {
IBlockProvider prov = (IBlockProvider) inventoryStack.getItem();
if(prov.provideBlock(player, itemStack, inventoryStack, Block.getBlockFromItem(itemStack.getItem()), true))
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
Optional<IBlockProvider> provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve();
if(provOptional.isEmpty()) return 0;
IBlockProvider prov = provOptional.get();
if(prov.provideBlock(player, inventoryStack, Block.byItem(itemStack.getItem()), true))
return 0;
return count;
}
}
}

View file

@ -0,0 +1,68 @@
package thetadev.constructionwand.containers.handlers;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import thetadev.constructionwand.api.IContainerHandler;
import thetadev.constructionwand.basics.WandUtil;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
public class HandlerBundle implements IContainerHandler
{
@Override
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
return inventoryStack != null && inventoryStack.getCount() == 1 && inventoryStack.getItem() == Items.BUNDLE;
}
@Override
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
return getContents(inventoryStack).filter((stack) -> WandUtil.stackEquals(stack, itemStack))
.map(ItemStack::getCount).reduce(0, Integer::sum);
}
@Override
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
AtomicInteger newCount = new AtomicInteger(count);
List<ItemStack> itemStacks = getContents(inventoryStack).filter((stack -> {
if(WandUtil.stackEquals(stack, itemStack)) {
int toTake = Math.min(newCount.get(), stack.getCount());
stack.shrink(toTake);
newCount.set(newCount.get() - toTake);
}
return !stack.isEmpty();
})).toList();
setItemList(inventoryStack, itemStacks);
return newCount.get();
}
private Stream<ItemStack> getContents(ItemStack bundleStack) {
CompoundTag compoundtag = bundleStack.getTag();
if(compoundtag == null) {
return Stream.empty();
}
else {
ListTag listtag = compoundtag.getList("Items", 10);
return listtag.stream().map(CompoundTag.class::cast).map(ItemStack::of);
}
}
private void setItemList(ItemStack itemStack, List<ItemStack> itemStacks) {
CompoundTag rootTag = itemStack.getOrCreateTag();
ListTag listTag = new ListTag();
rootTag.put("Items", listTag);
for(ItemStack stack : itemStacks) {
CompoundTag itemTag = new CompoundTag();
stack.save(itemTag);
listTag.add(itemTag);
}
}
}

View file

@ -1,61 +1,52 @@
package thetadev.constructionwand.containers.handlers;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import thetadev.constructionwand.api.IContainerHandler;
import thetadev.constructionwand.basics.WandUtil;
import java.util.Optional;
/**
* Created by james on 28/12/16.
*/
public class HandlerCapability implements IContainerHandler
{
@Override
public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
return inventoryStack != null && inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).isPresent();
}
@Override
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
LazyOptional<IItemHandler> itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
if(!itemHandlerLazyOptional.isPresent()) return 0;
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve();
if(itemHandlerOptional.isEmpty()) return 0;
int total = 0;
IItemHandler itemHandler = itemHandlerLazyOptional.orElse(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.getDefaultInstance());
IItemHandler itemHandler = itemHandlerOptional.get();
for (int i = 0; i < itemHandler.getSlots(); i++) {
for(int i = 0; i < itemHandler.getSlots(); i++) {
ItemStack containerStack = itemHandler.getStackInSlot(i);
if (containerStack != null && itemStack.isItemEqual(containerStack)) {
if(WandUtil.stackEquals(itemStack, containerStack)) {
total += Math.max(0, containerStack.getCount());
}
// Already in a container. Don't inception this thing.
}
return total;
}
@Override
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) {
int toUse = itemStack.getCount();
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve();
if(itemHandlerOptional.isEmpty()) return 0;
LazyOptional<IItemHandler> itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
if(!itemHandlerLazyOptional.isPresent()) return 0;
IItemHandler itemHandler = itemHandlerOptional.get();
IItemHandler itemHandler = itemHandlerLazyOptional.orElse(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.getDefaultInstance());
for (int i = 0; i < itemHandler.getSlots(); i++) {
for(int i = 0; i < itemHandler.getSlots(); i++) {
ItemStack handlerStack = itemHandler.getStackInSlot(i);
if(handlerStack != null && handlerStack.isItemEqual(itemStack)) {
if(WandUtil.stackEquals(itemStack, handlerStack)) {
ItemStack extracted = itemHandler.extractItem(i, count, false);
if(extracted != null) {
count -= extracted.getCount();
}
if(count <= 0) {
break;
}
count -= extracted.getCount();
if(count <= 0) break;
}
}
return count;

View file

@ -1,78 +1,75 @@
package thetadev.constructionwand.containers.handlers;
import net.minecraft.block.Block;
import net.minecraft.block.ShulkerBoxBlock;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.ItemStackHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.NonNullList;
import net.minecraftforge.common.util.Constants;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.ShulkerBoxBlock;
import thetadev.constructionwand.api.IContainerHandler;
import thetadev.constructionwand.basics.WandUtil;
public class HandlerShulkerbox implements IContainerHandler
{
private final int SLOTS = 27;
private final int SLOTS = 27;
@Override
public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack)
{
return inventoryStack != null && inventoryStack.getCount() == 1 && Block.getBlockFromItem(inventoryStack.getItem()) instanceof ShulkerBoxBlock;
}
@Override
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
return inventoryStack != null && inventoryStack.getCount() == 1 && Block.byItem(inventoryStack.getItem()) instanceof ShulkerBoxBlock;
}
@Override
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack)
{
int count = 0;
@Override
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
int count = 0;
for(ItemStack stack : getItemList(inventoryStack)) {
if(WandUtil.stackEquals(stack, itemStack)) count += stack.getCount();
}
for(ItemStack stack : getItemList(inventoryStack)) {
if(WandUtil.stackEquals(stack, itemStack)) count += stack.getCount();
}
return count;
}
return count;
}
@Override
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count)
{
NonNullList<ItemStack> itemList = getItemList(inventoryStack);
boolean changed = false;
@Override
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
NonNullList<ItemStack> itemList = getItemList(inventoryStack);
boolean changed = false;
for(ItemStack stack : itemList) {
if(WandUtil.stackEquals(stack, itemStack)) {
int toTake = Math.min(count, stack.getCount());
stack.shrink(toTake);
count -= toTake;
changed = true;
if(count == 0) break;
}
}
if(changed) {
setItemList(inventoryStack, itemList);
player.inventory.markDirty();
}
for(ItemStack stack : itemList) {
if(WandUtil.stackEquals(stack, itemStack)) {
int toTake = Math.min(count, stack.getCount());
stack.shrink(toTake);
count -= toTake;
changed = true;
if(count == 0) break;
}
}
if(changed) {
setItemList(inventoryStack, itemList);
player.getInventory().setChanged();
}
return count;
}
return count;
}
private NonNullList<ItemStack> getItemList(ItemStack itemStack) {
NonNullList<ItemStack> itemStacks = NonNullList.withSize(SLOTS, ItemStack.EMPTY);
CompoundNBT rootTag = itemStack.getTag();
if (rootTag != null && rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) {
CompoundNBT entityTag = rootTag.getCompound("BlockEntityTag");
if (entityTag.contains("Items", Constants.NBT.TAG_LIST)) {
ItemStackHelper.loadAllItems(entityTag, itemStacks);
}
}
return itemStacks;
}
private NonNullList<ItemStack> getItemList(ItemStack itemStack) {
NonNullList<ItemStack> itemStacks = NonNullList.withSize(SLOTS, ItemStack.EMPTY);
CompoundTag rootTag = itemStack.getTag();
if(rootTag != null && rootTag.contains("BlockEntityTag", Tag.TAG_COMPOUND)) {
CompoundTag entityTag = rootTag.getCompound("BlockEntityTag");
if(entityTag.contains("Items", Tag.TAG_LIST)) {
ContainerHelper.loadAllItems(entityTag, itemStacks);
}
}
return itemStacks;
}
private void setItemList(ItemStack itemStack, NonNullList<ItemStack> itemStacks) {
CompoundNBT rootTag = itemStack.getOrCreateTag();
if (!rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) {
rootTag.put("BlockEntityTag", new CompoundNBT());
}
ItemStackHelper.saveAllItems(rootTag.getCompound("BlockEntityTag"), itemStacks);
}
private void setItemList(ItemStack itemStack, NonNullList<ItemStack> itemStacks) {
CompoundTag rootTag = itemStack.getOrCreateTag();
if(!rootTag.contains("BlockEntityTag", Tag.TAG_COMPOUND)) {
rootTag.put("BlockEntityTag", new CompoundTag());
}
ContainerHelper.saveAllItems(rootTag.getCompound("BlockEntityTag"), itemStacks);
}
}

View file

@ -0,0 +1,75 @@
package thetadev.constructionwand.crafting;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraft.world.level.Level;
import thetadev.constructionwand.api.IWandUpgrade;
import thetadev.constructionwand.basics.ConfigServer;
import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.items.wand.ItemWand;
import javax.annotation.Nonnull;
public class RecipeWandUpgrade extends CustomRecipe
{
public static final SimpleRecipeSerializer<RecipeWandUpgrade> SERIALIZER = new SimpleRecipeSerializer<>(RecipeWandUpgrade::new);
public RecipeWandUpgrade(ResourceLocation resourceLocation) {
super(resourceLocation);
}
@Override
public boolean matches(@Nonnull CraftingContainer inv, @Nonnull Level worldIn) {
ItemStack wand = null;
IWandUpgrade upgrade = null;
for(int i = 0; i < inv.getContainerSize(); i++) {
ItemStack stack = inv.getItem(i);
if(!stack.isEmpty()) {
if(wand == null && stack.getItem() instanceof ItemWand) wand = stack;
else if(upgrade == null && stack.getItem() instanceof IWandUpgrade)
upgrade = (IWandUpgrade) stack.getItem();
else return false;
}
}
if(wand == null || upgrade == null) return false;
return !new WandOptions(wand).hasUpgrade(upgrade) && ConfigServer.getWandProperties(wand.getItem()).isUpgradeable();
}
@Nonnull
@Override
public ItemStack assemble(@Nonnull CraftingContainer inv) {
ItemStack wand = null;
IWandUpgrade upgrade = null;
for(int i = 0; i < inv.getContainerSize(); i++) {
ItemStack stack = inv.getItem(i);
if(!stack.isEmpty()) {
if(stack.getItem() instanceof ItemWand) wand = stack;
else if(stack.getItem() instanceof IWandUpgrade) upgrade = (IWandUpgrade) stack.getItem();
}
}
if(wand == null || upgrade == null) return ItemStack.EMPTY;
ItemStack newWand = wand.copy();
new WandOptions(newWand).addUpgrade(upgrade);
return newWand;
}
@Override
public boolean canCraftInDimensions(int width, int height) {
return width * height >= 2;
}
@Nonnull
@Override
public RecipeSerializer<?> getSerializer() {
return SERIALIZER;
}
}

View file

@ -0,0 +1,6 @@
package thetadev.constructionwand.data;
public interface ICustomItemModel
{
void generateCustomItemModel(ItemModelGenerator generator, String name);
}

View file

@ -0,0 +1,5 @@
package thetadev.constructionwand.data;
public interface INoItemBlock
{
}

View file

@ -0,0 +1,28 @@
package thetadev.constructionwand.data;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.tags.Tag;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.ItemLike;
public class Inp
{
public final String name;
public final Ingredient ingredient;
public final ItemPredicate predicate;
public Inp(String name, Ingredient ingredient, ItemPredicate predicate) {
this.name = name;
this.ingredient = ingredient;
this.predicate = predicate;
}
public static Inp fromItem(ItemLike in) {
return new Inp(in.asItem().getRegistryName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build());
}
public static Inp fromTag(Tag.Named<Item> in) {
return new Inp(in.getName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build());
}
}

View file

@ -0,0 +1,39 @@
package thetadev.constructionwand.data;
import net.minecraft.data.DataGenerator;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.RegistryObject;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.items.ModItems;
import javax.annotation.Nonnull;
public class ItemModelGenerator extends ItemModelProvider
{
public ItemModelGenerator(DataGenerator generator, ExistingFileHelper existingFileHelper) {
super(generator, ConstructionWand.MODID, existingFileHelper);
}
@Override
protected void registerModels() {
for(RegistryObject<Item> itemObject : ModItems.ITEMS.getEntries()) {
Item item = itemObject.get();
String name = item.getRegistryName().getPath();
if(item instanceof ICustomItemModel)
((ICustomItemModel) item).generateCustomItemModel(this, name);
else if(item instanceof BlockItem)
withExistingParent(name, modLoc("block/" + name));
else withExistingParent(name, "item/generated").texture("layer0", "item/" + name);
}
}
@Nonnull
@Override
public String getName() {
return ConstructionWand.MODNAME + " item models";
}
}

View file

@ -0,0 +1,25 @@
package thetadev.constructionwand.data;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.forge.event.lifecycle.GatherDataEvent;
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public class ModData
{
@SubscribeEvent
public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator();
ExistingFileHelper fileHelper = event.getExistingFileHelper();
if(event.includeServer()) {
generator.addProvider(new RecipeGenerator(generator));
}
if(event.includeClient()) {
generator.addProvider(new ItemModelGenerator(generator, fileHelper));
}
}
}

View file

@ -0,0 +1,74 @@
package thetadev.constructionwand.data;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.recipes.FinishedRecipe;
import net.minecraft.data.recipes.RecipeProvider;
import net.minecraft.data.recipes.ShapedRecipeBuilder;
import net.minecraft.data.recipes.SpecialRecipeBuilder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraft.world.level.ItemLike;
import net.minecraftforge.common.Tags;
import net.minecraftforge.registries.ForgeRegistries;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.crafting.RecipeWandUpgrade;
import thetadev.constructionwand.items.ModItems;
import javax.annotation.Nonnull;
import java.util.function.Consumer;
public class RecipeGenerator extends RecipeProvider
{
public RecipeGenerator(DataGenerator generatorIn) {
super(generatorIn);
}
@Override
protected void buildCraftingRecipes(@Nonnull Consumer<FinishedRecipe> consumer) {
wandRecipe(consumer, ModItems.WAND_STONE.get(), Inp.fromTag(ItemTags.STONE_TOOL_MATERIALS));
wandRecipe(consumer, ModItems.WAND_IRON.get(), Inp.fromTag(Tags.Items.INGOTS_IRON));
wandRecipe(consumer, ModItems.WAND_DIAMOND.get(), Inp.fromTag(Tags.Items.GEMS_DIAMOND));
wandRecipe(consumer, ModItems.WAND_INFINITY.get(), Inp.fromTag(Tags.Items.NETHER_STARS));
coreRecipe(consumer, ModItems.CORE_ANGEL.get(), Inp.fromTag(Tags.Items.FEATHERS), Inp.fromTag(Tags.Items.INGOTS_GOLD));
coreRecipe(consumer, ModItems.CORE_DESTRUCTION.get(), Inp.fromTag(Tags.Items.STORAGE_BLOCKS_DIAMOND), Inp.fromItem(Items.DIAMOND_PICKAXE));
specialRecipe(consumer, RecipeWandUpgrade.SERIALIZER);
}
private void wandRecipe(Consumer<FinishedRecipe> consumer, ItemLike wand, Inp material) {
ShapedRecipeBuilder.shaped(wand)
.define('X', material.ingredient)
.define('#', Tags.Items.RODS_WOODEN)
.pattern(" X")
.pattern(" # ")
.pattern("# ")
.unlockedBy("has_item", inventoryTrigger(material.predicate))
.save(consumer);
}
private void coreRecipe(Consumer<FinishedRecipe> consumer, ItemLike core, Inp item1, Inp item2) {
ShapedRecipeBuilder.shaped(core)
.define('O', item1.ingredient)
.define('X', item2.ingredient)
.define('#', Tags.Items.GLASS_PANES)
.pattern(" #X")
.pattern("#O#")
.pattern("X# ")
.unlockedBy("has_item", inventoryTrigger(item1.predicate))
.save(consumer);
}
private void specialRecipe(Consumer<FinishedRecipe> consumer, SimpleRecipeSerializer<?> serializer) {
ResourceLocation name = ForgeRegistries.RECIPE_SERIALIZERS.getKey(serializer);
SpecialRecipeBuilder.special(serializer).save(consumer, ConstructionWand.loc("dynamic/" + name.getPath()).toString());
}
@Nonnull
@Override
public String getName() {
return ConstructionWand.MODID + " crafting recipes";
}
}

View file

@ -0,0 +1,70 @@
package thetadev.constructionwand.integrations.jei;
import com.mojang.blaze3d.platform.InputConstants;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.registration.IRecipeRegistration;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.RegistryObject;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.ConfigClient;
import thetadev.constructionwand.basics.ConfigServer;
import thetadev.constructionwand.items.ModItems;
import javax.annotation.Nonnull;
@JeiPlugin
public class ConstructionWandJeiPlugin implements IModPlugin
{
private static final ResourceLocation pluginId = new ResourceLocation(ConstructionWand.MODID, ConstructionWand.MODID);
private static final String baseKey = ConstructionWand.MODID + ".description.";
private static final String baseKeyItem = "item." + ConstructionWand.MODID + ".";
@Nonnull
@Override
public ResourceLocation getPluginUid() {
return pluginId;
}
private Component keyComboComponent(boolean shiftOpt, Component optkeyComponent) {
String key = shiftOpt ? "sneak_opt" : "sneak";
return new TranslatableComponent(baseKey + "key." + key, optkeyComponent).withStyle(ChatFormatting.BLUE);
}
@Override
public void registerRecipes(IRecipeRegistration registration) {
Component optkeyComponent = new TranslatableComponent(InputConstants.getKey(ConfigClient.OPT_KEY.get(), -1).getName())
.withStyle(ChatFormatting.BLUE);
Component wandModeComponent = keyComboComponent(ConfigClient.SHIFTOPT_MODE.get(), optkeyComponent);
Component wandGuiComponent = keyComboComponent(ConfigClient.SHIFTOPT_GUI.get(), optkeyComponent);
for(RegistryObject<Item> wandSupplier : ModItems.WANDS) {
Item wand = wandSupplier.get();
ConfigServer.WandProperties wandProperties = ConfigServer.getWandProperties(wand);
String durabilityKey = wand == ModItems.WAND_INFINITY.get() ? "unlimited" : "limited";
Component durabilityComponent = new TranslatableComponent(baseKey + "durability." + durabilityKey, wandProperties.getDurability());
registration.addIngredientInfo(new ItemStack(wand), VanillaTypes.ITEM,
new TranslatableComponent(baseKey + "wand",
new TranslatableComponent(baseKeyItem + wand.getRegistryName().getPath()),
wandProperties.getLimit(), durabilityComponent,
optkeyComponent, wandModeComponent, wandGuiComponent)
);
}
for(RegistryObject<Item> coreSupplier : ModItems.CORES) {
Item core = coreSupplier.get();
registration.addIngredientInfo(new ItemStack(core), VanillaTypes.ITEM,
new TranslatableComponent(baseKey + core.getRegistryName().getPath()),
new TranslatableComponent(baseKey + "core", wandModeComponent)
);
}
}
}

View file

@ -1,154 +0,0 @@
package thetadev.constructionwand.items;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.ActionResult;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.*;
import thetadev.constructionwand.basics.options.EnumLock;
import thetadev.constructionwand.basics.options.EnumMode;
import thetadev.constructionwand.basics.options.IEnumOption;
import thetadev.constructionwand.basics.options.WandOptions;
import thetadev.constructionwand.job.AngelJob;
import thetadev.constructionwand.job.JobHistory;
import thetadev.constructionwand.job.WandJob;
import java.util.List;
public abstract class ItemWand extends Item
{
public final int maxBlocks;
public final int angelDistance;
public ItemWand(Item.Properties properties, int maxBlocks, int angelDistance) {
super(properties.group(ItemGroup.TOOLS));
this.maxBlocks = maxBlocks;
this.angelDistance = angelDistance;
addPropertyOverride(new ResourceLocation(ConstructionWand.MODID, "wand_mode"),
(stack, worldIn, entityIn) -> getWandMode(stack));
}
@Override
public ActionResultType onItemUse(ItemUseContext context)
{
PlayerEntity player = context.getPlayer();
Hand hand = context.getHand();
World world = context.getWorld();
if(world.isRemote || player == null) return ActionResultType.FAIL;
ItemStack stack = player.getHeldItem(hand);
if(player.isSneaking() && ConstructionWand.instance.jobHistory.isUndoActive(player)) {
WandJob job = ConstructionWand.instance.jobHistory.getForUndo(player, world, context.getPos());
if(job == null) return ActionResultType.FAIL;
//ConstructionWand.LOGGER.debug("Starting Undo");
return job.undo() ? ActionResultType.SUCCESS : ActionResultType.FAIL;
}
else {
WandJob job = WandJob.getJob(player, world, new BlockRayTraceResult(context.getHitVec(), context.getFace(), context.getPos(), false), stack);
return job.doIt() ? ActionResultType.SUCCESS : ActionResultType.FAIL;
}
}
@Override
public ActionResult<ItemStack> onItemRightClick(World world, PlayerEntity player, Hand hand) {
ItemStack stack = player.getHeldItem(hand);
if(world.isRemote) return ActionResult.resultFail(stack);
if(player.isSneaking()) {
// SHIFT + Right click: Change wand mode
WandOptions options = new WandOptions(stack);
IEnumOption opt = EnumMode.DEFAULT;
opt = options.nextOption(opt);
//ConstructionWand.LOGGER.debug("Wand mode: " + options.getOption(EnumLock.NOLOCK));
optionMessage(player, opt);
player.inventory.markDirty();
return ActionResult.resultSuccess(stack);
}
else {
// Right click: Place angel block
//ConstructionWand.LOGGER.debug("Place angel block");
WandJob job = new AngelJob(player, world, stack);
return job.doIt() ? ActionResult.resultSuccess(stack) : ActionResult.resultFail(stack);
}
}
@Override
public boolean canHarvestBlock(BlockState blockIn) {
return false;
}
@Override
public boolean getIsRepairable(ItemStack toRepair, ItemStack repair) {
return false;
}
public int getLimit(PlayerEntity player, ItemStack stack) {
return maxBlocks;
}
public static int getWandMode(ItemStack stack) {
WandOptions options = new WandOptions(stack);
return options.getOption(EnumMode.DEFAULT).getOrdinal();
}
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack itemstack, World worldIn, List<ITextComponent> lines, ITooltipFlag extraInfo) {
ItemWand wand = (ItemWand) itemstack.getItem();
WandOptions options = new WandOptions(itemstack);
String langPrefix = ConstructionWand.MODID + ".option.";
String langTooltip = ConstructionWand.MODID + ".tooltip.";
if(Screen.hasShiftDown()) {
for(int i=1; i<WandOptions.options.length; i++) {
IEnumOption opt = WandOptions.options[i];
lines.add(new TranslationTextComponent(langPrefix + opt.getOptionKey()).applyTextStyle(TextFormatting.AQUA)
.appendSibling(new TranslationTextComponent(langPrefix + options.getOption(opt).getTranslationKey()).applyTextStyle(TextFormatting.GRAY))
);
}
}
else {
IEnumOption opt = WandOptions.options[0];
lines.add(new TranslationTextComponent(langTooltip + "blocks", wand.maxBlocks).applyTextStyle(TextFormatting.GRAY));
lines.add(new TranslationTextComponent(langPrefix+opt.getOptionKey()).applyTextStyle(TextFormatting.AQUA)
.appendSibling(new TranslationTextComponent(langPrefix+options.getOption(opt).getTranslationKey()).applyTextStyle(TextFormatting.WHITE)));
lines.add(new TranslationTextComponent(langTooltip + "shift").applyTextStyle(TextFormatting.AQUA));
}
}
public static void optionMessage(PlayerEntity player, IEnumOption option) {
String langPrefix = ConstructionWand.MODID + ".option.";
player.sendStatusMessage(
new TranslationTextComponent(langPrefix+option.getOptionKey()).applyTextStyle(TextFormatting.AQUA)
.appendSibling(new TranslationTextComponent(langPrefix+option.getTranslationKey()).applyTextStyle(TextFormatting.WHITE))
.appendSibling(new StringTextComponent(" - ").applyTextStyle(TextFormatting.GRAY))
.appendSibling(new TranslationTextComponent(langPrefix+option.getTranslationKey()+".desc").applyTextStyle(TextFormatting.WHITE))
, true);
}
}

View file

@ -1,28 +0,0 @@
package thetadev.constructionwand.items;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.IItemTier;
import net.minecraft.item.Item;
import net.minecraft.item.Item.Properties;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
public class ItemWandBasic extends ItemWand
{
private final IItemTier tier;
public ItemWandBasic(IItemTier tier, int durability, int maxBlocks, int angelDistance) {
super(new Properties().maxDamage(durability), maxBlocks, angelDistance);
this.tier = tier;
}
@Override
public int getLimit(PlayerEntity player, ItemStack stack) {
return Math.min(stack.getMaxDamage() - stack.getDamage(), maxBlocks);
}
@Override
public boolean getIsRepairable(ItemStack toRepair, ItemStack repair) {
return this.tier.getRepairMaterial().test(repair);
}
}

View file

@ -1,20 +0,0 @@
package thetadev.constructionwand.items;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
import thetadev.constructionwand.basics.ConfigHandler;
public class ItemWandInfinity extends ItemWand
{
public ItemWandInfinity(int maxBlocks, int angelDistance)
{
super(new Item.Properties().maxStackSize(1), maxBlocks, angelDistance);
}
@Override
public int getLimit(PlayerEntity player, ItemStack stack) {
return player.isCreative() ? ConfigHandler.LIMIT_CREATIVE.get() : maxBlocks;
}
}

View file

@ -1,40 +1,84 @@
package thetadev.constructionwand.items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemTier;
import net.minecraft.util.ResourceLocation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.item.ItemColors;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Tiers;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.IForgeRegistryEntry;
import thetadev.constructionwand.basics.ConfigHandler;
import net.minecraftforge.registries.*;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.crafting.RecipeWandUpgrade;
import thetadev.constructionwand.items.core.ItemCoreAngel;
import thetadev.constructionwand.items.core.ItemCoreDestruction;
import thetadev.constructionwand.items.wand.ItemWand;
import thetadev.constructionwand.items.wand.ItemWandBasic;
import thetadev.constructionwand.items.wand.ItemWandInfinity;
@Mod.EventBusSubscriber(modid = ConstructionWand.MODID, bus = Mod.EventBusSubscriber.Bus.MOD)
public class ModItems
{
public static final Item WAND_STONE = new ItemWandBasic(ItemTier.STONE, ConfigHandler.DURABILITY_STONE.get(), ConfigHandler.LIMIT_STONE.get(), ConfigHandler.ANGEL_STONE.get());
public static final Item WAND_IRON = new ItemWandBasic(ItemTier.IRON, ConfigHandler.DURABILITY_IRON.get(), ConfigHandler.LIMIT_IRON.get(), ConfigHandler.ANGEL_IRON.get());
public static final Item WAND_DIAMOND = new ItemWandBasic(ItemTier.DIAMOND, ConfigHandler.DURABILITY_DIAMOND.get(), ConfigHandler.LIMIT_DIAMOND.get(), ConfigHandler.ANGEL_DIAMOND.get());
public static final Item WAND_INFINITY = new ItemWandInfinity(ConfigHandler.LIMIT_INFINITY.get(), ConfigHandler.ANGEL_INFINITY.get());
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, ConstructionWand.MODID);
@SubscribeEvent
public static void onRegisterItems(RegistryEvent.Register<Item> event)
{
event.getRegistry().registerAll(
register(WAND_STONE, "stone_wand"),
register(WAND_IRON, "iron_wand"),
register(WAND_DIAMOND, "diamond_wand"),
register(WAND_INFINITY, "infinity_wand")
);
}
// Wands
public static final RegistryObject<Item> WAND_STONE = ITEMS.register("stone_wand", () -> new ItemWandBasic(propWand(), Tiers.STONE));
public static final RegistryObject<Item> WAND_IRON = ITEMS.register("iron_wand", () -> new ItemWandBasic(propWand(), Tiers.IRON));
public static final RegistryObject<Item> WAND_DIAMOND = ITEMS.register("diamond_wand", () -> new ItemWandBasic(propWand(), Tiers.DIAMOND));
public static final RegistryObject<Item> WAND_INFINITY = ITEMS.register("infinity_wand", () -> new ItemWandInfinity(propWand()));
public static <T extends IForgeRegistryEntry<T>> T register(final T entry, final String name) {
return register(entry, new ResourceLocation(ConstructionWand.MODID, name));
}
// Cores
public static final RegistryObject<Item> CORE_ANGEL = ITEMS.register("core_angel", () -> new ItemCoreAngel(propUpgrade()));
public static final RegistryObject<Item> CORE_DESTRUCTION = ITEMS.register("core_destruction", () -> new ItemCoreDestruction(propUpgrade()));
public static <T extends IForgeRegistryEntry<T>> T register(final T entry, final ResourceLocation registryName) {
entry.setRegistryName(registryName);
return entry;
}
// Collections
public static final RegistryObject<Item>[] WANDS = new RegistryObject[] {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY};
public static final RegistryObject<Item>[] CORES = new RegistryObject[] {CORE_ANGEL, CORE_DESTRUCTION};
public static Item.Properties propWand() {
return new Item.Properties().tab(CreativeModeTab.TAB_TOOLS);
}
private static Item.Properties propUpgrade() {
return new Item.Properties().tab(CreativeModeTab.TAB_MISC).stacksTo(1);
}
@SubscribeEvent
public static void registerRecipeSerializers(RegistryEvent.Register<RecipeSerializer<?>> event) {
IForgeRegistry<RecipeSerializer<?>> r = event.getRegistry();
register(r, "wand_upgrade", RecipeWandUpgrade.SERIALIZER);
}
@OnlyIn(Dist.CLIENT)
public static void registerModelProperties() {
for(RegistryObject<Item> itemSupplier : WANDS) {
Item item = itemSupplier.get();
ItemProperties.register(
item, ConstructionWand.loc("using_core"),
(stack, world, entity, n) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 :
new WandOptions(stack).cores.get().getColor() > -1 ? 1 : 0
);
}
}
@OnlyIn(Dist.CLIENT)
public static void registerItemColors() {
ItemColors colors = Minecraft.getInstance().getItemColors();
for(RegistryObject<Item> itemSupplier : WANDS) {
Item item = itemSupplier.get();
colors.register((stack, layer) -> (layer == 1 && stack.getItem() instanceof ItemWand) ?
new WandOptions(stack).cores.get().getColor() : -1, item);
}
}
private static <V extends IForgeRegistryEntry<V>> void register(IForgeRegistry<V> reg, String name, IForgeRegistryEntry<V> thing) {
reg.register(thing.setRegistryName(ConstructionWand.loc(name)));
}
}

View file

@ -0,0 +1,25 @@
package thetadev.constructionwand.items.core;
import net.minecraft.resources.ResourceLocation;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.api.IWandAction;
import thetadev.constructionwand.api.IWandCore;
import thetadev.constructionwand.wand.action.ActionConstruction;
public class CoreDefault implements IWandCore
{
@Override
public int getColor() {
return -1;
}
@Override
public IWandAction getWandAction() {
return new ActionConstruction();
}
@Override
public ResourceLocation getRegistryName() {
return ConstructionWand.loc("default");
}
}

View file

@ -0,0 +1,31 @@
package thetadev.constructionwand.items.core;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.api.IWandCore;
import javax.annotation.Nonnull;
import java.util.List;
public abstract class ItemCore extends Item implements IWandCore
{
public ItemCore(Properties properties) {
super(properties);
}
@OnlyIn(Dist.CLIENT)
public void appendHoverText(@Nonnull ItemStack itemstack, Level worldIn, @Nonnull List<Component> lines, @Nonnull TooltipFlag extraInfo) {
lines.add(new TranslatableComponent(ConstructionWand.MODID + ".option.cores." + getRegistryName().toString() + ".desc")
.withStyle(ChatFormatting.GRAY));
lines.add(new TranslatableComponent(ConstructionWand.MODID + ".tooltip.core_tip")
.withStyle(ChatFormatting.AQUA));
}
}

View file

@ -0,0 +1,21 @@
package thetadev.constructionwand.items.core;
import thetadev.constructionwand.api.IWandAction;
import thetadev.constructionwand.wand.action.ActionAngel;
public class ItemCoreAngel extends ItemCore
{
public ItemCoreAngel(Properties properties) {
super(properties);
}
@Override
public int getColor() {
return 0xE9B115;
}
@Override
public IWandAction getWandAction() {
return new ActionAngel();
}
}

View file

@ -0,0 +1,21 @@
package thetadev.constructionwand.items.core;
import thetadev.constructionwand.api.IWandAction;
import thetadev.constructionwand.wand.action.ActionDestruction;
public class ItemCoreDestruction extends ItemCore
{
public ItemCoreDestruction(Properties properties) {
super(properties);
}
@Override
public int getColor() {
return 0xFF0000;
}
@Override
public IWandAction getWandAction() {
return new ActionDestruction();
}
}

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