diff --git a/src/generated/resources/.cache/2f572324d4ae438eed6ef1c4961027be07f7d2fe b/src/generated/resources/.cache/2f572324d4ae438eed6ef1c4961027be07f7d2fe index 757a464c..eec7eab9 100644 --- a/src/generated/resources/.cache/2f572324d4ae438eed6ef1c4961027be07f7d2fe +++ b/src/generated/resources/.cache/2f572324d4ae438eed6ef1c4961027be07f7d2fe @@ -1,4 +1,4 @@ -// 1.21.1 2024-09-10T19:56:11.5948361 JustDireThings Tags +// 1.21.1 2024-09-12T11:15:53.8960728 JustDireThings Tags 3f1af1854f73f0ca55ec9d76439efe025d53baaf data/c/tags/block/budding_blocks.json f9398184c0216b0a1898a13ab326b459417ec790 data/c/tags/block/buds.json 691444261459e5b538067e8cd03db2fbe1b3371d data/c/tags/block/clusters.json @@ -13,5 +13,5 @@ fd8f34ea7c3f46b4031a8323009cfaffab88474e data/justdirethings/tags/block/phase_de b73bf681aa4023a1ac69623485bbec13846a282a data/justdirethings/tags/block/swapper_deny.json 35133e95f1c8fdd7a1c21afcc231fc0bffefb9a8 data/justdirethings/tags/block/tick_speed_deny.json 704d4386b02b3ad86131bd6cf1e36461baa4169f data/minecraft/tags/block/bamboo_plantable_on.json -0562efff705455227cd6342808df3c8f082d9cbc data/minecraft/tags/block/mineable/pickaxe.json +206c5b35dd25f55c68f989b006c5c5acce7822af data/minecraft/tags/block/mineable/pickaxe.json 8882a0a52d428036a957d24fb046777a8795e588 data/minecraft/tags/block/mineable/shovel.json diff --git a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d index 0cbdbf3e..fdefeedd 100644 --- a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d +++ b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d @@ -1,4 +1,4 @@ -// 1.21.1 2024-09-07T12:06:42.2583603 Loot Tables +// 1.21.1 2024-09-12T11:15:53.9060812 Loot Tables 16a0ab61513717ab1cb8c1df9718722a643d9728 data/justdirethings/loot_table/blocks/blazegold_block.json b2953e2dcd4960e7f275bb42e4ad626219213fe1 data/justdirethings/loot_table/blocks/blockbreakert1.json bb65fdba3c1b14526a629aacb462661b5853008b data/justdirethings/loot_table/blocks/blockbreakert2.json @@ -18,6 +18,7 @@ d6bade76be1cda5393e15c24c1fbe30e95f88d76 data/justdirethings/loot_table/blocks/d 33bae530c3d366588774b7fd2be61e8eae4d2f59 data/justdirethings/loot_table/blocks/eclipsealloy_block.json 86da68130cd736a04252f0ce688aaaa22a824f70 data/justdirethings/loot_table/blocks/eclipsegateblock.json ae552784cf952a0f440297360735d3a06f2219ad data/justdirethings/loot_table/blocks/energytransmitter.json +bbec1ad75f93301b82c08e7ce57318522028e967 data/justdirethings/loot_table/blocks/experienceholder.json 1374ed53b2d6c1462b64a99f3ca1b290388d36c5 data/justdirethings/loot_table/blocks/ferricore_block.json 3716bc4ec75b66e189c71ea06c09dc08fd0132f5 data/justdirethings/loot_table/blocks/fluidcollectort1.json 6878247c1338944b23fd82861b5e098763334d6d data/justdirethings/loot_table/blocks/fluidcollectort2.json diff --git a/src/generated/resources/.cache/69ee5e809dff011efe049b68501078221ca37cb0 b/src/generated/resources/.cache/69ee5e809dff011efe049b68501078221ca37cb0 index f528b0ad..d5964c86 100644 --- a/src/generated/resources/.cache/69ee5e809dff011efe049b68501078221ca37cb0 +++ b/src/generated/resources/.cache/69ee5e809dff011efe049b68501078221ca37cb0 @@ -1,4 +1,4 @@ -// 1.21.1 2024-09-07T12:06:42.2473509 Item Models: justdirethings +// 1.21.1 2024-09-13T13:42:08.1493472 Item Models: justdirethings c197a1b2a5ae31fcfabfe6cab9ab95a9dc0e1af6 assets/justdirethings/models/item/blazegold_axe.json dddf215922d40d92c2d8bce4229f2e2af8b3c7ba assets/justdirethings/models/item/blazegold_axe_active.json 6396fb6e503760080534bc26337236d4d09a4cb8 assets/justdirethings/models/item/blazegold_block.json @@ -209,6 +209,7 @@ d3f356b071deedc9372ec13aa79b224a46327716 assets/justdirethings/models/item/eclip 2f3ef44480cc3c82ab7af95cfee7d174dcfd1d30 assets/justdirethings/models/item/eclipsealloy_sword_active.json f60564d23035abd9c1d690f4622ea258afa92db1 assets/justdirethings/models/item/eclipsegate_wand.json acf1922ba0cd70df4e7ccf30684508a47ac1a14b assets/justdirethings/models/item/energytransmitter.json +c6e8a590cc9e752378ffd172eadd9fcf6f6bb1ed assets/justdirethings/models/item/experienceholder.json eb052d000da5f8d7999360f7bbfe773aa23ce70d assets/justdirethings/models/item/ferricore_axe.json 62a8f615d9bd8a95b2e7e21b8f6658454008fb54 assets/justdirethings/models/item/ferricore_axe_active.json 6c85011e764ec697b9be64a2f7fc12ddacf56f74 assets/justdirethings/models/item/ferricore_block.json @@ -367,3 +368,4 @@ a3679c6341273d0028d1f6882e467e290189eb35 assets/justdirethings/models/item/upgra 0af186d271e80543d4bc2b38307337807c61af8d assets/justdirethings/models/item/upgrade_treefeller.json 24c722112f9d85c53ea12020657451e156b57e1c assets/justdirethings/models/item/upgrade_walkspeed.json b713dac946a919bd3c78bdd84396c681905676bb assets/justdirethings/models/item/voidshift_wand.json +a7b09b6500bbaed0170e4ebbf38cac862e0b2f20 assets/justdirethings/models/item/xp_fluid_bucket.json diff --git a/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb b/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb index e39ee550..04fab44a 100644 --- a/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb +++ b/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb @@ -1,2 +1,2 @@ -// 1.21.1 2024-09-08T10:48:40.1210526 Languages: en_us for mod: justdirethings -d5878f400337646026ccd4b5a26fd0191197b510 assets/justdirethings/lang/en_us.json +// 1.21.1 2024-09-13T13:53:01.5331943 Languages: en_us for mod: justdirethings +50598341120df2f850d5821435699dff7b3dcdb3 assets/justdirethings/lang/en_us.json diff --git a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e index 1d9c3a70..6c971aa6 100644 --- a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e +++ b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -1,4 +1,4 @@ -// 1.21.1 2024-09-07T12:06:42.2598615 Recipes +// 1.21.1 2024-09-12T11:15:53.9075825 Recipes 39cd44c838de7f56aeac73aec8b3bfb64ad7fec2 data/justdirethings/advancement/recipes/building_blocks/blazegold_ingot_9x9.json 2f2a0f948f272df9713010dbcb75beb81c328b01 data/justdirethings/advancement/recipes/building_blocks/celestigem_9x9.json 94b1a5fa365caea5ec90b525346091c5babe3068 data/justdirethings/advancement/recipes/building_blocks/coal_t1_9x9.json @@ -244,8 +244,10 @@ e8627cdd4f0fb2a6c839f4f9634298a090d5311e data/justdirethings/advancement/recipes e7739ae5a223a405cd2d8a63f1fffe9aae24ee77 data/justdirethings/advancement/recipes/misc/eclipsealloy_sword-templateupgrade.json 6f8cbc17d75d54ec3630af834f37a475d9aeb23a data/justdirethings/advancement/recipes/misc/eclipsealloy_sword.json c4027f6d2dfede7c4b83887d84307ecb3fd3d194 data/justdirethings/advancement/recipes/misc/eclipsegate_wand.json -99d92b1bc189067bd87bd462a3d9dcdc80b97214 data/justdirethings/advancement/recipes/misc/energytransmitter.json +6f7bbe549ead5d8d6e55a047489e60fd15ed6ed9 data/justdirethings/advancement/recipes/misc/energytransmitter.json 1c708ed2037839bce715e7bbb2b4735f10332ebf data/justdirethings/advancement/recipes/misc/energytransmitter_nbtclear.json +95ed0743947a37f994102e9fa297703842817dd9 data/justdirethings/advancement/recipes/misc/experienceholder.json +e4c5cc919880147cb39143d2756b4cd3987a1809 data/justdirethings/advancement/recipes/misc/experienceholder_nbtclear.json 7e588d8128a4c11f6f2dc30c548188f5d05e476b data/justdirethings/advancement/recipes/misc/ferricore_axe-leafbreaker.json cb728c7c3e212df2c679818a0c43ead0f775862e data/justdirethings/advancement/recipes/misc/ferricore_axe-treefeller.json 53378d92dc5e2456f22bef4f2e68f88ab35ed25c data/justdirethings/advancement/recipes/misc/ferricore_axe.json @@ -626,6 +628,8 @@ da733be1557a9be15498275f8c6178ed326ac6a9 data/justdirethings/recipe/eclipsealloy 2f2859d569c0b8c60550dfb94a5bda2cd7c44aab data/justdirethings/recipe/eclipsegate_wand.json b05351e6bb42bb9d25dd71b1c866ed8faa5676b9 data/justdirethings/recipe/energytransmitter.json 96973172b8a0d257d8b381579a452986ba57d599 data/justdirethings/recipe/energytransmitter_nbtclear.json +27ac72b113eb35d4b4e95866a54e70a0844bfa13 data/justdirethings/recipe/experienceholder.json +d7a84f58bdf776d2037d2a688104493093707109 data/justdirethings/recipe/experienceholder_nbtclear.json 04206220684c529ae065b7eac581f3b8f88c18e9 data/justdirethings/recipe/ferricore_axe-leafbreaker.json 89fda77982a9d37bbdb23d336a8c93e94a540496 data/justdirethings/recipe/ferricore_axe-treefeller.json 86d392a3bb7b5daf9cf25a03c114aab9a04ba849 data/justdirethings/recipe/ferricore_axe.json diff --git a/src/generated/resources/.cache/a1a2ec2da1e0c65903879d2c88015afc97528c8f b/src/generated/resources/.cache/a1a2ec2da1e0c65903879d2c88015afc97528c8f index 97644322..a01c4afd 100644 --- a/src/generated/resources/.cache/a1a2ec2da1e0c65903879d2c88015afc97528c8f +++ b/src/generated/resources/.cache/a1a2ec2da1e0c65903879d2c88015afc97528c8f @@ -1,4 +1,4 @@ -// 1.21.1 2024-09-07T12:06:42.2548566 Block States: justdirethings +// 1.21.1 2024-09-13T13:42:08.1573543 Block States: justdirethings e5976480ed9485e0bae43d4edde0c8aecaabe8a5 assets/justdirethings/blockstates/blazegold_block.json 6580258682ab076313135ecb0c16dc2595a1a0df assets/justdirethings/blockstates/blockbreakert1.json 0db1a53fd80ee5aed746843da439bd30b131ac55 assets/justdirethings/blockstates/blockbreakert2.json @@ -52,6 +52,7 @@ f49a72ecbf07e06b65c649abe5868d3d4ef1f311 assets/justdirethings/blockstates/unref 08ba81f3e2ae52bf1c45b64295a773b42f47d7d5 assets/justdirethings/blockstates/unrefined_t3_fluid_block.json ee8299d78cf13542216f38b90fa292fb0d634f81 assets/justdirethings/blockstates/unrefined_t4_fluid_block.json db57ae5afcebace0cb6847909f30fe9458feef8a assets/justdirethings/blockstates/unstable_portal_fluid_block.json +b6f1951b73ff9f551bfc1407975e4451ad9abe62 assets/justdirethings/blockstates/xp_fluid_block.json 9bd3946cfa107459786529b2af21eb125a901fb7 assets/justdirethings/models/block/blazegold_block.json 9659e3f59756a5bb95fdd0504c1bf43fe30d2724 assets/justdirethings/models/block/blockbreakert1.json bb4a6cb7aaa1b611b9e5d16b1b02f1ec51a6c645 assets/justdirethings/models/block/blockbreakert1_active.json @@ -128,3 +129,4 @@ d8845a6da5ed66f910e014c97071f8ea754df1c8 assets/justdirethings/models/block/time 4cbb3c8e0147ca797cb922394a58a3950e00ffef assets/justdirethings/models/block/unrefined_t3_fluid_block.json 4cbb3c8e0147ca797cb922394a58a3950e00ffef assets/justdirethings/models/block/unrefined_t4_fluid_block.json 4cbb3c8e0147ca797cb922394a58a3950e00ffef assets/justdirethings/models/block/unstable_portal_fluid_block.json +4cbb3c8e0147ca797cb922394a58a3950e00ffef assets/justdirethings/models/block/xp_fluid_block.json diff --git a/src/generated/resources/assets/justdirethings/blockstates/xp_fluid_block.json b/src/generated/resources/assets/justdirethings/blockstates/xp_fluid_block.json new file mode 100644 index 00000000..09e82537 --- /dev/null +++ b/src/generated/resources/assets/justdirethings/blockstates/xp_fluid_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "justdirethings:block/xp_fluid_block" + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/justdirethings/lang/en_us.json b/src/generated/resources/assets/justdirethings/lang/en_us.json index 47123ed3..506be149 100644 --- a/src/generated/resources/assets/justdirethings/lang/en_us.json +++ b/src/generated/resources/assets/justdirethings/lang/en_us.json @@ -22,6 +22,7 @@ "block.justdirethings.eclipsealloy_block": "Eclipse Alloy Block", "block.justdirethings.eclipsegateblock": "Eclipse Gate", "block.justdirethings.energytransmitter": "Energy Transmitter", + "block.justdirethings.experienceholder": "Experience Holder", "block.justdirethings.ferricore_block": "Ferricore Block", "block.justdirethings.fluidcollectort1": "Simple Fluid Collector", "block.justdirethings.fluidcollectort2": "Advanced Fluid Collector", @@ -67,6 +68,7 @@ "block.justdirethings.unrefined_t3_fluid_block": "Unrefined Voidflame Fuel", "block.justdirethings.unrefined_t4_fluid_block": "Unrefined Eclipse Ember Fuel", "block.justdirethings.unstable_portal_fluid_block": "Unstable Portal Fluid", + "block.justdirethings.xp_fluid_block": "XP Fluid", "entity.justdirethings.decoy_entity": "Decoy Entity", "entity.justdirethings.paradox_entity": "Paradox", "entity.justdirethings.time_wand_entity": "Time Wand Entity", @@ -80,6 +82,7 @@ "fluid_type.justdirethings.unrefined_t3_fluid_type": "Unrefined Voidflame Fuel", "fluid_type.justdirethings.unrefined_t4_fluid_type": "Unrefined Eclipse Ember Fuel", "fluid_type.justdirethings.unstable_portal_fluid_type": "Unstable Portal Fluid", + "fluid_type.justdirethings.xp_fluid_type": "XP Fluid", "item.justdirethings.blazegold_axe": "Blazegold Axe", "item.justdirethings.blazegold_boots": "Blazegold Boots", "item.justdirethings.blazegold_chestplate": "Blazegold Chestplate", @@ -207,6 +210,7 @@ "item.justdirethings.upgrade_treefeller": "Upgrade: Treefeller", "item.justdirethings.upgrade_walkspeed": "Upgrade: Walk Speed", "item.justdirethings.voidshift_wand": "Voidshift Wand", + "item.justdirethings.xp_fluid_bucket": "XP Fluid Bucket", "itemGroup.DeferredHolder{ResourceKey[minecraft:creative_mode_tab / justdirethings:justdirethings]}": "Just Dire Things", "justdirethings.ability": "Ability: %s - %s", "justdirethings.ability.airburst": "Air Burst", @@ -356,6 +360,7 @@ "justdirethings.screen.click-hold-for": "Hold Click For (ticks)", "justdirethings.screen.click-left": "Left Click", "justdirethings.screen.click-right": "Right Click", + "justdirethings.screen.collectexp": "Collect Experience", "justdirethings.screen.comparecounts": "Compare Stack Sizes", "justdirethings.screen.comparenbt": "Compare NBT", "justdirethings.screen.copy_area": "Copy Area", @@ -393,20 +398,24 @@ "justdirethings.screen.low": "Low", "justdirethings.screen.no_fuel": "Fuel source empty", "justdirethings.screen.notrequireequipped": "Activate from Inventory", + "justdirethings.screen.owneronly": "Owner Only", "justdirethings.screen.paradoxall": "Revert Blocks and Entities", "justdirethings.screen.paradoxblock": "Revert Blocks", "justdirethings.screen.paradoxenergycost": "Energy Cost: %s FE", "justdirethings.screen.paradoxentity": "Revert Entities", "justdirethings.screen.paradoxfluidcost": "Fluid Cost: %s mb", "justdirethings.screen.pickupdelay": "Pickup Delay (Ticks)", + "justdirethings.screen.pullfluids": "Pull Fluids", "justdirethings.screen.pullitems": "Pull Items", "justdirethings.screen.pulse": "Pulse", + "justdirethings.screen.pushfluids": "Push Fluids", "justdirethings.screen.redstone-strong": "Strong Signal", "justdirethings.screen.redstone-weak": "Weak Signal", "justdirethings.screen.remove_favorite": "Remove Favorite", "justdirethings.screen.renderarea": "Render Area", "justdirethings.screen.renderparadox": "Render Paradox", "justdirethings.screen.requireequipped": "Activate if Equipped", + "justdirethings.screen.retrieveexp": "Retrieve Level", "justdirethings.screen.rightclicksettings": "Right Click for Settings", "justdirethings.screen.save_close": "Save and Close", "justdirethings.screen.senditems": "Push Items", @@ -418,6 +427,7 @@ "justdirethings.screen.snapshotarea": "Snapshot Area", "justdirethings.screen.sneak-click": "Sneak Click", "justdirethings.screen.stay_open": "Stay Open", + "justdirethings.screen.storeexp": "Store Level", "justdirethings.screen.swapitems": "Swap Items", "justdirethings.screen.target-adult": "Target Adult", "justdirethings.screen.target-air": "Target Air", @@ -429,6 +439,7 @@ "justdirethings.screen.target-noblock": "Ignore Blocks", "justdirethings.screen.target-passive": "Target Passive", "justdirethings.screen.target-player": "Target Player", + "justdirethings.screen.targetexp": "Target Level", "justdirethings.screen.tickspeed": "Speed (Ticks)", "justdirethings.settingscopied": "Settings Copied", "justdirethings.settingspasted": "Settings Pasted", diff --git a/src/generated/resources/assets/justdirethings/models/block/xp_fluid_block.json b/src/generated/resources/assets/justdirethings/models/block/xp_fluid_block.json new file mode 100644 index 00000000..270155ce --- /dev/null +++ b/src/generated/resources/assets/justdirethings/models/block/xp_fluid_block.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "justdirethings:block/fluid_source" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/justdirethings/models/item/experienceholder.json b/src/generated/resources/assets/justdirethings/models/item/experienceholder.json new file mode 100644 index 00000000..56eed91e --- /dev/null +++ b/src/generated/resources/assets/justdirethings/models/item/experienceholder.json @@ -0,0 +1,3 @@ +{ + "parent": "justdirethings:block/experienceholder" +} \ No newline at end of file diff --git a/src/generated/resources/assets/justdirethings/models/item/xp_fluid_bucket.json b/src/generated/resources/assets/justdirethings/models/item/xp_fluid_bucket.json new file mode 100644 index 00000000..25bcf3de --- /dev/null +++ b/src/generated/resources/assets/justdirethings/models/item/xp_fluid_bucket.json @@ -0,0 +1,5 @@ +{ + "parent": "neoforge:item/bucket", + "fluid": "justdirethings:xp_fluid_source", + "loader": "neoforge:fluid_container" +} \ No newline at end of file diff --git a/src/generated/resources/data/justdirethings/advancement/recipes/misc/energytransmitter.json b/src/generated/resources/data/justdirethings/advancement/recipes/misc/energytransmitter.json index 32ccf170..a823feae 100644 --- a/src/generated/resources/data/justdirethings/advancement/recipes/misc/energytransmitter.json +++ b/src/generated/resources/data/justdirethings/advancement/recipes/misc/energytransmitter.json @@ -1,11 +1,11 @@ { "parent": "minecraft:recipes/root", "criteria": { - "has_ferricore_ingot": { + "has_blazegold_ingot": { "conditions": { "items": [ { - "items": "justdirethings:ferricore_ingot" + "items": "justdirethings:blazegold_ingot" } ] }, @@ -21,7 +21,7 @@ "requirements": [ [ "has_the_recipe", - "has_ferricore_ingot" + "has_blazegold_ingot" ] ], "rewards": { diff --git a/src/generated/resources/data/justdirethings/advancement/recipes/misc/experienceholder.json b/src/generated/resources/data/justdirethings/advancement/recipes/misc/experienceholder.json new file mode 100644 index 00000000..180b3ce7 --- /dev/null +++ b/src/generated/resources/data/justdirethings/advancement/recipes/misc/experienceholder.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_blazegold_ingot": { + "conditions": { + "items": [ + { + "items": "justdirethings:blazegold_ingot" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "justdirethings:experienceholder" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_blazegold_ingot" + ] + ], + "rewards": { + "recipes": [ + "justdirethings:experienceholder" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/justdirethings/advancement/recipes/misc/experienceholder_nbtclear.json b/src/generated/resources/data/justdirethings/advancement/recipes/misc/experienceholder_nbtclear.json new file mode 100644 index 00000000..03f76af5 --- /dev/null +++ b/src/generated/resources/data/justdirethings/advancement/recipes/misc/experienceholder_nbtclear.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_experienceholder": { + "conditions": { + "items": [ + { + "items": "justdirethings:experienceholder" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "justdirethings:experienceholder_nbtclear" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_experienceholder" + ] + ], + "rewards": { + "recipes": [ + "justdirethings:experienceholder_nbtclear" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/justdirethings/loot_table/blocks/experienceholder.json b/src/generated/resources/data/justdirethings/loot_table/blocks/experienceholder.json new file mode 100644 index 00000000..e24d367c --- /dev/null +++ b/src/generated/resources/data/justdirethings/loot_table/blocks/experienceholder.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "justdirethings:experienceholder" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "justdirethings:blocks/experienceholder" +} \ No newline at end of file diff --git a/src/generated/resources/data/justdirethings/recipe/experienceholder.json b/src/generated/resources/data/justdirethings/recipe/experienceholder.json new file mode 100644 index 00000000..d8e72ea5 --- /dev/null +++ b/src/generated/resources/data/justdirethings/recipe/experienceholder.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "justdirethings", + "key": { + "d": { + "item": "minecraft:emerald" + }, + "e": { + "item": "minecraft:ender_pearl" + }, + "f": { + "item": "justdirethings:blazegold_ingot" + }, + "h": { + "item": "minecraft:book" + } + }, + "pattern": [ + " d ", + "heh", + "fff" + ], + "result": { + "count": 1, + "id": "justdirethings:experienceholder" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/justdirethings/recipe/experienceholder_nbtclear.json b/src/generated/resources/data/justdirethings/recipe/experienceholder_nbtclear.json new file mode 100644 index 00000000..64cfe372 --- /dev/null +++ b/src/generated/resources/data/justdirethings/recipe/experienceholder_nbtclear.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:crafting_shapeless", + "category": "misc", + "group": "justdirethings", + "ingredients": [ + { + "item": "justdirethings:experienceholder" + } + ], + "result": { + "count": 1, + "id": "justdirethings:experienceholder" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json index 6b145466..9a720811 100644 --- a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json @@ -40,6 +40,7 @@ "justdirethings:time_crystal_cluster", "justdirethings:time_crystal_block", "justdirethings:paradoxmachine", - "justdirethings:inventory_holder" + "justdirethings:inventory_holder", + "justdirethings:experienceholder" ] } \ No newline at end of file diff --git a/src/main/java/com/direwolf20/justdirethings/JustDireThings.java b/src/main/java/com/direwolf20/justdirethings/JustDireThings.java index 0f823d33..94f6530f 100644 --- a/src/main/java/com/direwolf20/justdirethings/JustDireThings.java +++ b/src/main/java/com/direwolf20/justdirethings/JustDireThings.java @@ -1,16 +1,15 @@ package com.direwolf20.justdirethings; -import com.direwolf20.justdirethings.common.blockentities.EnergyTransmitterBE; -import com.direwolf20.justdirethings.common.blockentities.InventoryHolderBE; -import com.direwolf20.justdirethings.common.blockentities.ParadoxMachineBE; -import com.direwolf20.justdirethings.common.blockentities.PlayerAccessorBE; +import com.direwolf20.justdirethings.common.blockentities.*; import com.direwolf20.justdirethings.common.blockentities.basebe.BaseMachineBE; import com.direwolf20.justdirethings.common.blockentities.basebe.FluidMachineBE; import com.direwolf20.justdirethings.common.blockentities.basebe.PoweredMachineBE; import com.direwolf20.justdirethings.common.capabilities.EnergyStorageItemStackNoReceive; import com.direwolf20.justdirethings.common.capabilities.EnergyStorageItemstack; +import com.direwolf20.justdirethings.common.capabilities.ExperienceHolderFluidTank; import com.direwolf20.justdirethings.common.containers.handlers.PotionCanisterHandler; import com.direwolf20.justdirethings.common.entities.DecoyEntity; +import com.direwolf20.justdirethings.common.fluids.xpfluid.XPFluid; import com.direwolf20.justdirethings.common.items.FluidCanister; import com.direwolf20.justdirethings.common.items.PortalGunV2; import com.direwolf20.justdirethings.common.items.TimeWand; @@ -301,5 +300,14 @@ public boolean canFillFluidType(FluidStack fluid) { }, Registration.ParadoxMachine.get() ); + event.registerBlock(Capabilities.FluidHandler.BLOCK, + (level, pos, state, be, side) -> { + if (be instanceof ExperienceHolderBE experienceHolderBE) { + return new ExperienceHolderFluidTank(experienceHolderBE, fluidstack -> fluidstack.getFluid() instanceof XPFluid); //TODO Tags? + } + return null; + }, + Registration.ExperienceHolder.get() + ); } } diff --git a/src/main/java/com/direwolf20/justdirethings/client/blockentityrenders/ExperienceHolderBER.java b/src/main/java/com/direwolf20/justdirethings/client/blockentityrenders/ExperienceHolderBER.java new file mode 100644 index 00000000..93dd4f69 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/client/blockentityrenders/ExperienceHolderBER.java @@ -0,0 +1,49 @@ +package com.direwolf20.justdirethings.client.blockentityrenders; + +import com.direwolf20.justdirethings.client.blockentityrenders.baseber.AreaAffectingBER; +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; + +public class ExperienceHolderBER extends AreaAffectingBER { + public static final ItemStack itemStack = new ItemStack(Items.EXPERIENCE_BOTTLE); + + public ExperienceHolderBER(BlockEntityRendererProvider.Context context) { + + } + + @Override + public void render(BlockEntity blockentity, float partialTicks, PoseStack matrixStackIn, MultiBufferSource bufferIn, int combinedLightsIn, int combinedOverlayIn) { + super.render(blockentity, partialTicks, matrixStackIn, bufferIn, combinedLightsIn, combinedOverlayIn); + if (blockentity instanceof ExperienceHolderBE experienceHolderBE) + this.renderItemStack(experienceHolderBE, matrixStackIn, bufferIn, combinedOverlayIn); + } + + private void renderItemStack(ExperienceHolderBE blockEntity, PoseStack poseStack, MultiBufferSource bufferIn, int combinedOverlayIn) { + ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer(); + Direction direction = blockEntity.getBlockState().getValue(BlockStateProperties.FACING).getOpposite(); + long millis = System.currentTimeMillis(); + + poseStack.pushPose(); + poseStack.translate(0.5f + (direction.getStepX() * 0.3f), 0.5f + (direction.getStepY() * 0.3f), 0.5f + (direction.getStepZ() * 0.3f)); + poseStack.mulPose(Axis.XP.rotationDegrees(direction.getStepZ() * -90)); + poseStack.mulPose(Axis.ZP.rotationDegrees(direction.getStepX() * 90)); + poseStack.mulPose(Axis.XP.rotationDegrees(direction.getStepY() == 1 ? 0 : 180)); + float angle = ((millis / 15) % 360); + poseStack.mulPose(Axis.YP.rotationDegrees(angle)); + poseStack.scale(.15f, .15f, .15f); + itemRenderer.renderStatic(itemStack, ItemDisplayContext.FIXED, LightTexture.FULL_BRIGHT, combinedOverlayIn, poseStack, bufferIn, Minecraft.getInstance().level, 0); + poseStack.popPose(); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/client/screens/ExperienceHolderScreen.java b/src/main/java/com/direwolf20/justdirethings/client/screens/ExperienceHolderScreen.java new file mode 100644 index 00000000..20fd4150 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/client/screens/ExperienceHolderScreen.java @@ -0,0 +1,105 @@ +package com.direwolf20.justdirethings.client.screens; + +import com.direwolf20.justdirethings.client.screens.basescreens.BaseMachineScreen; +import com.direwolf20.justdirethings.client.screens.standardbuttons.ToggleButtonFactory; +import com.direwolf20.justdirethings.client.screens.widgets.GrayscaleButton; +import com.direwolf20.justdirethings.client.screens.widgets.NumberButton; +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; +import com.direwolf20.justdirethings.common.containers.ExperienceHolderContainer; +import com.direwolf20.justdirethings.common.network.data.ExperienceHolderPayload; +import com.direwolf20.justdirethings.common.network.data.ExperienceHolderSettingsPayload; +import com.direwolf20.justdirethings.util.ExperienceUtils; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.neoforged.neoforge.network.PacketDistributor; + +public class ExperienceHolderScreen extends BaseMachineScreen { + private ExperienceHolderBE experienceHolderBE; + private int exp; + private int targetExp; + private boolean ownerOnly; + private boolean collectExp; + private static final ResourceLocation EXPERIENCE_BAR_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace("hud/experience_bar_background"); + private static final ResourceLocation EXPERIENCE_BAR_PROGRESS_SPRITE = ResourceLocation.withDefaultNamespace("hud/experience_bar_progress"); + + public ExperienceHolderScreen(ExperienceHolderContainer container, Inventory inv, Component name) { + super(container, inv, name); + if (container.baseMachineBE instanceof ExperienceHolderBE experienceHolderBE) { + this.experienceHolderBE = experienceHolderBE; + this.exp = experienceHolderBE.exp; + this.targetExp = experienceHolderBE.targetExp; + this.ownerOnly = experienceHolderBE.ownerOnly; + this.collectExp = experienceHolderBE.collectExp; + } + } + + @Override + public void init() { + super.init(); + addRenderableWidget(ToggleButtonFactory.STOREEXPBUTTON(topSectionLeft + (topSectionWidth / 2) + 15, topSectionTop + 62, true, b -> { + int amt = 1; + if (Screen.hasControlDown()) + amt = -1; + else if (Screen.hasShiftDown()) + amt = amt * 10; + PacketDistributor.sendToServer(new ExperienceHolderPayload(true, amt)); + })); + addRenderableWidget(ToggleButtonFactory.EXTRACTEXPBUTTON(topSectionLeft + (topSectionWidth / 2) - 15 - 18, topSectionTop + 62, true, b -> { + int amt = 1; + if (Screen.hasControlDown()) + amt = -1; + else if (Screen.hasShiftDown()) + amt = amt * 10; + PacketDistributor.sendToServer(new ExperienceHolderPayload(false, amt)); + })); + addRenderableWidget(ToggleButtonFactory.TARGETEXPBUTTON(topSectionLeft + (topSectionWidth / 2) - 15 - 42, topSectionTop + 64, targetExp, b -> { + targetExp = ((NumberButton) b).getValue(); //The value is updated in the mouseClicked method below + saveSettings(); + })); + addRenderableWidget(ToggleButtonFactory.OWNERONLYBUTTON(topSectionLeft + (topSectionWidth / 2) - 15 - 60, topSectionTop + 62, ownerOnly, b -> { + ownerOnly = !ownerOnly; + ((GrayscaleButton) b).toggleActive(); + saveSettings(); + })); + addRenderableWidget(ToggleButtonFactory.COLLECTEXPBUTTON(topSectionLeft + (topSectionWidth / 2) + 15, topSectionTop + 42, collectExp, b -> { + collectExp = !collectExp; + ((GrayscaleButton) b).toggleActive(); + saveSettings(); + })); + } + + @Override + protected void renderBg(GuiGraphics guiGraphics, float partialTicks, int mouseX, int mouseY) { + super.renderBg(guiGraphics, partialTicks, mouseX, mouseY); + renderXPBar(guiGraphics, partialTicks, mouseX, mouseY); + } + + public void renderXPBar(GuiGraphics guiGraphics, float partialTicks, int mouseX, int mouseY) { + int barX = topSectionLeft + (topSectionWidth / 2) - (182 / 2); // Position for the XP bar in your GUI + int barY = topSectionTop + topSectionHeight - 15; // Y position for the XP bar + + // Bind the vanilla experience bar texture (this is the same texture used by the player XP bar) + guiGraphics.blitSprite(EXPERIENCE_BAR_BACKGROUND_SPRITE, barX, barY, 182, 5); + int partialAmount = (int) (ExperienceUtils.getProgressToNextLevel(experienceHolderBE.exp) * 183.0F); + if (partialAmount > 0) { + guiGraphics.blitSprite(EXPERIENCE_BAR_PROGRESS_SPRITE, 182, 5, 0, 0, barX, barY, partialAmount, 5); + } + String s = String.valueOf(ExperienceUtils.getLevelFromTotalExperience(experienceHolderBE.exp)); + int j = topSectionLeft + (topSectionWidth / 2) - font.width(s) / 2; + int k = topSectionTop + 62 + (font.lineHeight / 2); + guiGraphics.drawString(font, s, j + 1, k, 0, false); + guiGraphics.drawString(font, s, j - 1, k, 0, false); + guiGraphics.drawString(font, s, j, k + 1, 0, false); + guiGraphics.drawString(font, s, j, k - 1, 0, false); + guiGraphics.drawString(font, s, j, k, 8453920, false); + } + + @Override + public void saveSettings() { + super.saveSettings(); + PacketDistributor.sendToServer(new ExperienceHolderSettingsPayload(targetExp, ownerOnly, collectExp)); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java b/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java index 7d7ceb5a..6987c8c9 100644 --- a/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java +++ b/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java @@ -49,6 +49,43 @@ public static ToggleButton ALLOWLISTBUTTON(int x, int y, boolean startingValue, return new ToggleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, ALLOW_LIST_TEXTURES, startingValue, onPress); } + private static final List PUSH_PULL_FLUIDS_TEXTURES = List.of( + new TextureLocalization(ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/pullfluids.png"), Component.translatable("justdirethings.screen.pullfluids")), + new TextureLocalization(ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/pushfluids.png"), Component.translatable("justdirethings.screen.pushfluids")) + ); + + public static ToggleButton PUSHPULLFLUIDSBUTTON(int x, int y, boolean startingValue, Button.OnPress onPress) { + return new ToggleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, PUSH_PULL_FLUIDS_TEXTURES, startingValue, onPress); + } + + private static final ResourceLocation STORE_EXP_BUTTON = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/add.png"); + private static final Component STORE_EXP_BUTTON_LOCALIZATION = Component.translatable("justdirethings.screen.storeexp"); + + public static GrayscaleButton STOREEXPBUTTON(int x, int y, boolean startingValue, Button.OnPress onPress) { + return new GrayscaleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, STORE_EXP_BUTTON, STORE_EXP_BUTTON_LOCALIZATION, startingValue, onPress); + } + + private static final ResourceLocation OWNER_ONLY_BUTTON = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/player.png"); + private static final Component OWNER_ONLY_BUTTON_LOCALIZATION = Component.translatable("justdirethings.screen.owneronly"); + + public static GrayscaleButton OWNERONLYBUTTON(int x, int y, boolean startingValue, Button.OnPress onPress) { + return new GrayscaleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, OWNER_ONLY_BUTTON, OWNER_ONLY_BUTTON_LOCALIZATION, startingValue, onPress); + } + + private static final ResourceLocation COLLECT_EXP_BUTTON = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/mindfog.png"); + private static final Component COLLECT_EXP_BUTTON_LOCALIZATION = Component.translatable("justdirethings.screen.collectexp"); + + public static GrayscaleButton COLLECTEXPBUTTON(int x, int y, boolean startingValue, Button.OnPress onPress) { + return new GrayscaleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, COLLECT_EXP_BUTTON, COLLECT_EXP_BUTTON_LOCALIZATION, startingValue, onPress); + } + + private static final ResourceLocation EXTRACT_EXP_BUTTON = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/remove.png"); + private static final Component EXTRACT_EXP_BUTTON_LOCALIZATION = Component.translatable("justdirethings.screen.retrieveexp"); + + public static GrayscaleButton EXTRACTEXPBUTTON(int x, int y, boolean startingValue, Button.OnPress onPress) { + return new GrayscaleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, EXTRACT_EXP_BUTTON, EXTRACT_EXP_BUTTON_LOCALIZATION, startingValue, onPress); + } + private static final ResourceLocation FILTER_ONLY = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/allowlisttrue.png"); private static final Component FILTER_ONLY_LOCALIZATION = Component.translatable("justdirethings.screen.filteronlytrue"); @@ -145,6 +182,12 @@ public static NumberButton TICKSPEEDBUTTON(int x, int y, int value, int min, But return new NumberButton(x, y, 24, 12, value, min, 1200, ticksButtonLocalization, onPress); } + private static Component targetExpLocalization = Component.translatable("justdirethings.screen.targetexp"); + + public static NumberButton TARGETEXPBUTTON(int x, int y, int value, Button.OnPress onPress) { + return new NumberButton(x, y, 24, 12, value, 0, 1000, targetExpLocalization, onPress); + } + private static Component pickupDelayButtonLocalization = Component.translatable("justdirethings.screen.pickupdelay"); public static NumberButton PICKUPDELAYBUTTON(int x, int y, int value, Button.OnPress onPress) { diff --git a/src/main/java/com/direwolf20/justdirethings/common/blockentities/ExperienceHolderBE.java b/src/main/java/com/direwolf20/justdirethings/common/blockentities/ExperienceHolderBE.java new file mode 100644 index 00000000..a6608894 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/blockentities/ExperienceHolderBE.java @@ -0,0 +1,309 @@ +package com.direwolf20.justdirethings.common.blockentities; + +import com.direwolf20.justdirethings.client.particles.itemparticle.ItemFlowParticleData; +import com.direwolf20.justdirethings.common.blockentities.basebe.AreaAffectingBE; +import com.direwolf20.justdirethings.common.blockentities.basebe.BaseMachineBE; +import com.direwolf20.justdirethings.common.blockentities.basebe.RedstoneControlledBE; +import com.direwolf20.justdirethings.setup.Registration; +import com.direwolf20.justdirethings.util.ExperienceUtils; +import com.direwolf20.justdirethings.util.MiscHelpers; +import com.direwolf20.justdirethings.util.interfacehelpers.AreaAffectingData; +import com.direwolf20.justdirethings.util.interfacehelpers.FilterData; +import com.direwolf20.justdirethings.util.interfacehelpers.RedstoneControlData; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ExperienceOrb; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.capabilities.BlockCapabilityCache; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; + +import java.util.List; + +public class ExperienceHolderBE extends BaseMachineBE implements AreaAffectingBE, RedstoneControlledBE { + protected BlockCapabilityCache attachedTank; + public FilterData filterData = new FilterData(); + public AreaAffectingData areaAffectingData = new AreaAffectingData(getBlockState().getValue(BlockStateProperties.FACING).getOpposite()); + public RedstoneControlData redstoneControlData = getDefaultRedstoneData(); + public int exp; + public int targetExp; + private Player currentPlayer; + public boolean collectExp; + public boolean ownerOnly; + + public ExperienceHolderBE(BlockPos pPos, BlockState pBlockState) { + super(Registration.ExperienceHolderBE.get(), pPos, pBlockState); + } + + @Override + public BlockEntity getBlockEntity() { + return this; + } + + @Override + public RedstoneControlData getRedstoneControlData() { + return redstoneControlData; + } + + @Override + public AreaAffectingData getAreaAffectingData() { + return areaAffectingData; + } + + public void changeSettings(Player player, int targetExp, boolean ownerOnly, boolean collectExp) { + if (this.ownerOnly && !player.getUUID().equals(placedByUUID)) return; + this.targetExp = targetExp; + this.ownerOnly = ownerOnly; + this.collectExp = collectExp; + markDirtyClient(); + } + + public int addExp(int addition) { + if (this.exp > Integer.MAX_VALUE - addition) { + // Prevent overflow by capping the experience at Integer.MAX_VALUE + int remainingExp = addition - (Integer.MAX_VALUE - this.exp); + this.exp = Integer.MAX_VALUE; + return remainingExp; //Return Remaining + } else { + // Safe to add the experience + this.exp += addition; + return 0; + } + } + + public int subExp(int subtraction) { + int amtToRemove = Math.min(exp, subtraction); + this.exp = this.exp - amtToRemove; + return subtraction - amtToRemove; + } + + public void storeExp(Player player, int levelChange) { + if (ownerOnly && !player.getUUID().equals(placedByUUID)) return; + if (levelChange == -1) { + // Move all experience from player + int totalExp = ExperienceUtils.getPlayerTotalExperience(player); + int remaining = addExp(totalExp); + player.giveExperiencePoints(-totalExp); // Removes all levels + player.giveExperienceLevels(-1); //Handles dangling Floating Point Math (RAGE!) Consider it a tax on storing exp :) + if (remaining > 0) + player.giveExperiencePoints(remaining); + } else if (levelChange > 0) { + // Handle fractional progress first, if the player is in the middle of a level + int expInCurrentLevel = (int) (player.experienceProgress * player.getXpNeededForNextLevel()); + + // If the player has partial progress within the current level, remove that first + if (player.experienceProgress > 0.0f) { + int expRemoved = ExperienceUtils.removePoints(player, expInCurrentLevel); + int remaining = addExp(expRemoved); + levelChange--; // We've already removed part of a level + player.experienceProgress = 0f; //Clear the player's partial exp, to handle super low floating point values + if (remaining > 0) + player.giveExperiencePoints(remaining); + } + + if (levelChange > 0) { + // Now remove the specified number of full levels + int expRemoved = ExperienceUtils.removeLevels(player, levelChange); + int remaining = addExp(expRemoved); + if (remaining > 0) + player.giveExperiencePoints(remaining); + } + } + + markDirtyClient(); + } + + public void extractExp(Player player, int levelChange) { + if (exp == 0) return; // No experience in the block, exit early + if (ownerOnly && !player.getUUID().equals(placedByUUID)) return; + + if (levelChange == -1) { + // Move all experience from block to player + int expToGive = exp; + player.giveExperiencePoints(expToGive); + this.exp = 0; // Remove all experience from the block + } else if (levelChange > 0) { + // Handle fractional progress first, if the player is in the middle of a level + if (roundUpToNextLevel(player)) + levelChange--; + + if (levelChange > 0 && this.exp > 0) { + // Give full levels based on the remaining levels requested + int expForNextLevels = ExperienceUtils.getTotalExperienceForLevel(player.experienceLevel + levelChange) - ExperienceUtils.getPlayerTotalExperience(player); + int expToGive = Math.min(this.exp, expForNextLevels); + player.giveExperiencePoints(expToGive); + this.exp -= expToGive; + roundUpToNextLevel(player); //Thanks Floating point math!! + } + } + + markDirtyClient(); + } + + public boolean roundUpToNextLevel(Player player) { + if (this.exp <= 0) return false; // No experience available to round up + int expInCurrentLevel = (int) (player.experienceProgress * player.getXpNeededForNextLevel()); + if (expInCurrentLevel > 0) { + int expToGive = Math.min(exp, ExperienceUtils.getExpNeededForNextLevel(player)); + player.giveExperiencePoints(expToGive); + this.exp -= expToGive; + return true; + } + return false; + } + + public void tickClient() { + } + + public void tickServer() { + super.tickServer(); + if (collectExp) + collectExp(); + handleExperience(); + } + + public void doParticles(ItemStack itemStack, Vec3 sourcePos, boolean toBlock) { + Direction direction = getBlockState().getValue(BlockStateProperties.FACING); + BlockPos blockPos = getBlockPos(); + Vec3 baubleSpot = new Vec3(blockPos.getX() + 0.5f - (0.3 * direction.getStepX()), blockPos.getY() + 0.5f - (0.3 * direction.getStepY()), blockPos.getZ() + 0.5f - (0.3 * direction.getStepZ())); + double d0 = sourcePos.x(); + double d1 = sourcePos.y(); + double d2 = sourcePos.z(); + if (toBlock) { + ItemFlowParticleData data = new ItemFlowParticleData(itemStack, baubleSpot.x, baubleSpot.y, baubleSpot.z, 1); + ((ServerLevel) level).sendParticles(data, d0, d1, d2, 10, 0, 0, 0, 0); + } else { + ItemFlowParticleData data = new ItemFlowParticleData(itemStack, d0, d1, d2, 1); + ((ServerLevel) level).sendParticles(data, baubleSpot.x, baubleSpot.y, baubleSpot.z, 10, 0, 0, 0, 0); + } + } + + private void handleExperience() { + //if (!isActiveRedstone() || !canRun()) return; + assert level != null; + if (isActiveRedstone() && canRun() && currentPlayer == null) + findTargetPlayer(); + + if (currentPlayer == null) return; + + int currentLevel = currentPlayer.experienceLevel; + if (currentLevel < targetExp && exp > 0) { + extractExp(currentPlayer, 1); + doParticles(new ItemStack(Items.EXPERIENCE_BOTTLE), currentPlayer.getEyePosition().subtract(0, 0.25f, 0), false); + if (exp == 0) + currentPlayer = null; //Clear current target if we run out of exp + } else if (currentLevel > targetExp || (currentLevel == targetExp && currentPlayer.experienceProgress > 0.01f)) { + storeExp(currentPlayer, 1); + doParticles(new ItemStack(Items.EXPERIENCE_BOTTLE), currentPlayer.getEyePosition().subtract(0, 0.25f, 0), true); + } else + currentPlayer = null; + } + + private void collectExp() { + if (operationTicks != 0) + return; //Run at the tick speed of the machine, but ignore redstone signal (For now at least) + assert level != null; + AABB searchArea = getAABB(getBlockPos()); + + List entityList = level.getEntitiesOfClass(ExperienceOrb.class, searchArea, entity -> true) + .stream().toList(); + + if (entityList.isEmpty()) return; + + for (ExperienceOrb experienceOrb : entityList) { + int orbValue = experienceOrb.getValue(); + addExp(orbValue); + doParticles(new ItemStack(Items.EXPERIENCE_BOTTLE), experienceOrb.position(), true); + experienceOrb.discard(); + } + markDirtyClient(); + } + + private void findTargetPlayer() { + assert level != null; + AABB searchArea = getAABB(getBlockPos()); + + List entityList = level.getEntitiesOfClass(Player.class, searchArea, entity -> true) + .stream().toList(); + + if (entityList.isEmpty()) return; + + for (Player player : entityList) { + if (ownerOnly && !player.getUUID().equals(placedByUUID)) + continue; + if (player.experienceLevel != targetExp || player.experienceProgress > 0.01f) { + this.currentPlayer = player; + return; + } + } + } + + private IFluidHandler getAttachedTank() { + if (attachedTank == null) { + assert this.level != null; + BlockState state = level.getBlockState(getBlockPos()); + Direction facing = state.getValue(BlockStateProperties.FACING); + BlockPos inventoryPos = getBlockPos().relative(facing); + attachedTank = BlockCapabilityCache.create( + Capabilities.FluidHandler.BLOCK, // capability to cache + (ServerLevel) this.level, // level + inventoryPos, // target position + facing.getOpposite() // context (The side of the block we're trying to pull/push from?) + ); + } + return attachedTank.getCapability(); + } + + @Override + public void saveAdditional(CompoundTag tag, HolderLookup.Provider provider) { + super.saveAdditional(tag, provider); + tag.putInt("exp", exp); + tag.putInt("targetExp", targetExp); + tag.putBoolean("collectExp", collectExp); + tag.putBoolean("ownerOnly", ownerOnly); + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider provider) { + super.loadAdditional(tag, provider); + exp = tag.getInt("exp"); + targetExp = tag.getInt("targetExp"); + collectExp = tag.getBoolean("collectExp"); + ownerOnly = tag.getBoolean("ownerOnly"); + } + + @Override + public AreaAffectingData getDefaultAreaData(AreaAffectingBE areaAffectingBE) { + return areaAffectingBE.getDefaultAreaData(getBlockState().getValue(BlockStateProperties.FACING).getOpposite()); + } + + @Override + public RedstoneControlData getDefaultRedstoneData() { + return new RedstoneControlData(MiscHelpers.RedstoneMode.PULSE); + } + + @Override + public boolean isDefaultSettings() { + if (!super.isDefaultSettings()) + return false; + if (exp != 0) + return false; + if (targetExp != 0) + return false; + if (collectExp) + return false; + if (ownerOnly) + return false; + return true; + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/blockentities/ItemCollectorBE.java b/src/main/java/com/direwolf20/justdirethings/common/blockentities/ItemCollectorBE.java index 49b9543f..63d167f3 100644 --- a/src/main/java/com/direwolf20/justdirethings/common/blockentities/ItemCollectorBE.java +++ b/src/main/java/com/direwolf20/justdirethings/common/blockentities/ItemCollectorBE.java @@ -32,7 +32,7 @@ public class ItemCollectorBE extends BaseMachineBE implements FilterableBE, AreaAffectingBE, RedstoneControlledBE { protected BlockCapabilityCache attachedInventory; public FilterData filterData = new FilterData(); - public AreaAffectingData areaAffectingData = new AreaAffectingData(); + public AreaAffectingData areaAffectingData = new AreaAffectingData(getBlockState().getValue(BlockStateProperties.FACING).getOpposite()); public RedstoneControlData redstoneControlData = new RedstoneControlData(); public ItemCollectorBE(BlockPos pPos, BlockState pBlockState) { @@ -126,4 +126,9 @@ private IItemHandler getAttachedInventory() { } return attachedInventory.getCapability(); } + + @Override + public AreaAffectingData getDefaultAreaData(AreaAffectingBE areaAffectingBE) { + return areaAffectingBE.getDefaultAreaData(getBlockState().getValue(BlockStateProperties.FACING).getOpposite()); + } } diff --git a/src/main/java/com/direwolf20/justdirethings/common/blockentities/basebe/BaseMachineBE.java b/src/main/java/com/direwolf20/justdirethings/common/blockentities/basebe/BaseMachineBE.java index 6d660b3c..1b7fc9fd 100644 --- a/src/main/java/com/direwolf20/justdirethings/common/blockentities/basebe/BaseMachineBE.java +++ b/src/main/java/com/direwolf20/justdirethings/common/blockentities/basebe/BaseMachineBE.java @@ -4,6 +4,7 @@ import com.direwolf20.justdirethings.setup.Registration; import com.direwolf20.justdirethings.util.MiscHelpers; import com.direwolf20.justdirethings.util.UsefulFakePlayer; +import com.direwolf20.justdirethings.util.interfacehelpers.AreaAffectingData; import com.direwolf20.justdirethings.util.interfacehelpers.FilterData; import com.direwolf20.justdirethings.util.interfacehelpers.RedstoneControlData; import com.mojang.authlib.GameProfile; @@ -211,12 +212,16 @@ public RedstoneControlData getDefaultRedstoneData() { return new RedstoneControlData(); } + public AreaAffectingData getDefaultAreaData(AreaAffectingBE areaAffectingBE) { + return areaAffectingBE.getDefaultAreaData(getBlockState().getValue(BlockStateProperties.FACING)); + } + public boolean isDefaultSettings() { if (tickSpeed != 20) return false; if (direction != 0) return false; - if (this instanceof AreaAffectingBE areaAffectingBE && !areaAffectingBE.getAreaAffectingData().equals(areaAffectingBE.getDefaultAreaData(getBlockState().getValue(BlockStateProperties.FACING)))) + if (this instanceof AreaAffectingBE areaAffectingBE && !areaAffectingBE.getAreaAffectingData().equals(getDefaultAreaData(areaAffectingBE))) return false; if (this instanceof FilterableBE filterableBE && !filterableBE.getFilterData().equals(getDefaultFilterData())) return false; diff --git a/src/main/java/com/direwolf20/justdirethings/common/blocks/ExperienceHolder.java b/src/main/java/com/direwolf20/justdirethings/common/blocks/ExperienceHolder.java new file mode 100644 index 00000000..cf15e603 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/blocks/ExperienceHolder.java @@ -0,0 +1,203 @@ +package com.direwolf20.justdirethings.common.blocks; + +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; +import com.direwolf20.justdirethings.common.blocks.baseblocks.BaseMachineBlock; +import com.direwolf20.justdirethings.common.containers.ExperienceHolderContainer; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem; + +import javax.annotation.Nullable; +import java.util.stream.Stream; + +public class ExperienceHolder extends BaseMachineBlock { + protected static final VoxelShape[] shapes = new VoxelShape[]{ + Stream.of( + Block.box(4, 0, 4, 12, 1, 12), + Block.box(3, 1, 3, 13, 2, 13), + Block.box(6, 2, 11, 10, 8, 12), + Block.box(5, 2, 5, 11, 9, 11), + Block.box(7, 9, 7, 9, 11, 9), + Block.box(6, 2, 4, 10, 8, 5), + Block.box(11, 2, 6, 12, 8, 10), + Block.box(4, 2, 6, 5, 8, 10) + ).reduce((v1, v2) -> Shapes.join(v1, v2, BooleanOp.OR)).get(), //UP + Stream.of( + Block.box(4, 15, 4, 12, 16, 12), + Block.box(3, 14, 3, 13, 15, 13), + Block.box(6, 8, 11, 10, 14, 12), + Block.box(5, 7, 5, 11, 14, 11), + Block.box(7, 5, 7, 9, 7, 9), + Block.box(6, 8, 4, 10, 14, 5), + Block.box(11, 8, 6, 12, 14, 10), + Block.box(4, 8, 6, 5, 14, 10) + ).reduce((v1, v2) -> Shapes.join(v1, v2, BooleanOp.OR)).get(), //DOWN + Stream.of( + Block.box(4, 4, 0, 12, 12, 1), + Block.box(3, 3, 1, 13, 13, 2), + Block.box(6, 4, 2, 10, 5, 8), + Block.box(5, 5, 2, 11, 11, 9), + Block.box(7, 7, 9, 9, 9, 11), + Block.box(6, 11, 2, 10, 12, 8), + Block.box(4, 6, 2, 5, 10, 8), + Block.box(11, 6, 2, 12, 10, 8) + ).reduce((v1, v2) -> Shapes.join(v1, v2, BooleanOp.OR)).get(), //SOUTH + Stream.of( + Block.box(4, 4, 15, 12, 12, 16), + Block.box(3, 3, 14, 13, 13, 15), + Block.box(6, 4, 8, 10, 5, 14), + Block.box(5, 5, 7, 11, 11, 14), + Block.box(7, 7, 5, 9, 9, 7), + Block.box(6, 11, 8, 10, 12, 14), + Block.box(11, 6, 8, 12, 10, 14), + Block.box(4, 6, 8, 5, 10, 14) + ).reduce((v1, v2) -> Shapes.join(v1, v2, BooleanOp.OR)).get(), //NORTH + Stream.of( + Block.box(0, 4, 4, 1, 12, 12), + Block.box(1, 3, 3, 2, 13, 13), + Block.box(2, 4, 6, 8, 5, 10), + Block.box(2, 5, 5, 9, 11, 11), + Block.box(9, 7, 7, 11, 9, 9), + Block.box(2, 11, 6, 8, 12, 10), + Block.box(2, 6, 11, 8, 10, 12), + Block.box(2, 6, 4, 8, 10, 5) + ).reduce((v1, v2) -> Shapes.join(v1, v2, BooleanOp.OR)).get(), //EAST + Stream.of( + Block.box(15, 4, 4, 16, 12, 12), + Block.box(14, 3, 3, 15, 13, 13), + Block.box(8, 4, 6, 14, 5, 10), + Block.box(7, 5, 5, 14, 11, 11), + Block.box(5, 7, 7, 7, 9, 9), + Block.box(8, 11, 6, 14, 12, 10), + Block.box(8, 6, 4, 14, 10, 5), + Block.box(8, 6, 11, 14, 10, 12) + ).reduce((v1, v2) -> Shapes.join(v1, v2, BooleanOp.OR)).get() //WEST + }; + + public static final DirectionProperty FACING = BlockStateProperties.FACING; + + public ExperienceHolder() { + super(Properties.of() + .sound(SoundType.METAL) + .strength(2.0f) + .noOcclusion() + .forceSolidOn() + .isRedstoneConductor(BaseMachineBlock::never) + ); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new ExperienceHolderBE(pos, state); + } + + @Override + public void openMenu(Player player, BlockPos blockPos) { + player.openMenu(new SimpleMenuProvider( + (windowId, playerInventory, playerEntity) -> new ExperienceHolderContainer(windowId, playerInventory, blockPos), Component.translatable("")), (buf -> { + buf.writeBlockPos(blockPos); + })); + } + + @Override + protected ItemInteractionResult useItemOn(ItemStack itemStack, BlockState blockState, Level level, BlockPos blockPos, Player player, InteractionHand hand, BlockHitResult blockHitResult) { + if (level.isClientSide) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + IFluidHandlerItem fluidHandlerItem = itemStack.getCapability(Capabilities.FluidHandler.ITEM); + if (fluidHandlerItem != null) { + IFluidHandler cap = level.getCapability(Capabilities.FluidHandler.BLOCK, blockPos, blockHitResult.getDirection()); + if (cap == null) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + if (fluidHandlerItem.getFluidInTank(0).getAmount() < fluidHandlerItem.getTankCapacity(0) && !cap.getFluidInTank(0).isEmpty()) { + FluidStack testStack = cap.drain(fluidHandlerItem.getTankCapacity(0), IFluidHandler.FluidAction.SIMULATE); + if (testStack.getAmount() > 0) { + int amtFit = fluidHandlerItem.fill(testStack, IFluidHandler.FluidAction.SIMULATE); + if (amtFit > 0) { + FluidStack extractedStack = cap.drain(amtFit, IFluidHandler.FluidAction.EXECUTE); + fluidHandlerItem.fill(extractedStack, IFluidHandler.FluidAction.EXECUTE); + if (itemStack.getItem() instanceof BucketItem) + player.setItemSlot(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, fluidHandlerItem.getContainer()); + level.playSound(null, blockPos, SoundEvents.BUCKET_FILL, SoundSource.BLOCKS, 1F, 1.0F); + return ItemInteractionResult.SUCCESS; + } + } + } else { + FluidStack fluidStack = fluidHandlerItem.getFluidInTank(0); + int insertAmt = cap.fill(fluidStack, IFluidHandler.FluidAction.SIMULATE); + if (insertAmt > 0) { + FluidStack extractedStack = fluidHandlerItem.drain(insertAmt, IFluidHandler.FluidAction.EXECUTE); + if (!extractedStack.isEmpty()) { + cap.fill(extractedStack, IFluidHandler.FluidAction.EXECUTE); + if (itemStack.getItem() instanceof BucketItem) + player.setItemSlot(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, fluidHandlerItem.getContainer()); + level.playSound(null, blockPos, SoundEvents.BUCKET_EMPTY, SoundSource.BLOCKS, 1F, 1.0F); + return ItemInteractionResult.SUCCESS; + } + } + } + } + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + @Override + public boolean isValidBE(BlockEntity blockEntity) { + return blockEntity instanceof ExperienceHolderBE; + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter getter, BlockPos pos, CollisionContext context) { + return shapes[state.getValue(FACING).get3DDataValue()]; + } + + @SuppressWarnings("deprecation") + @Override + public VoxelShape getOcclusionShape(BlockState state, BlockGetter reader, BlockPos pos) { + return shapes[state.getValue(FACING).get3DDataValue()]; + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState().setValue(BlockStateProperties.FACING, context.getClickedFace().getOpposite()); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(BlockStateProperties.FACING); + } + + @Override + public float getShadeBrightness(BlockState p_48731_, BlockGetter p_48732_, BlockPos p_48733_) { + return 1.0F; + } + + @Override + public boolean propagatesSkylightDown(BlockState p_48740_, BlockGetter p_48741_, BlockPos p_48742_) { + return true; + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/capabilities/ExperienceHolderFluidTank.java b/src/main/java/com/direwolf20/justdirethings/common/capabilities/ExperienceHolderFluidTank.java new file mode 100644 index 00000000..e5344a71 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/capabilities/ExperienceHolderFluidTank.java @@ -0,0 +1,97 @@ +package com.direwolf20.justdirethings.common.capabilities; + +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; +import com.direwolf20.justdirethings.setup.Registration; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.templates.FluidTank; + +import java.util.function.Predicate; + +public class ExperienceHolderFluidTank extends FluidTank { + private final ExperienceHolderBE experienceHolderBE; + + public ExperienceHolderFluidTank(ExperienceHolderBE experienceHolderBE, Predicate validator) { + super(Integer.MAX_VALUE, validator); + this.experienceHolderBE = experienceHolderBE; + fluid = new FluidStack(Registration.XP_FLUID_SOURCE.get(), getFluidAmount()); + } + + public int getFluidAmount() { + // Prevent overflow by capping experienceHolderBE.exp at Integer.MAX_VALUE / 20 + if (experienceHolderBE.exp > Integer.MAX_VALUE / 20) { + return Integer.MAX_VALUE; // If multiplying by 20 would overflow, return max int value + } + + // Safe to multiply without overflow + return Math.min(experienceHolderBE.exp * 20, getCapacity()); + } + + public int getCapacity() { + return Integer.MAX_VALUE; + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + if (resource.isEmpty() || !isFluidValid(resource)) { + return 0; + } + if (action.simulate()) { + if (fluid.isEmpty()) { + return Math.min(capacity, resource.getAmount() - resource.getAmount() % 20); + } + if (!FluidStack.isSameFluidSameComponents(fluid, resource)) { + return 0; + } + return Math.min(capacity - getFluidAmount() - ((capacity - getFluidAmount()) % 20), resource.getAmount() - resource.getAmount() % 20); + } + if (fluid.isEmpty()) { + return resource.getAmount() - insertFluid(resource.getAmount()); + } + if (!FluidStack.isSameFluidSameComponents(fluid, resource)) { + return 0; + } + int filled = resource.getAmount() - insertFluid(resource.getAmount()); + if (filled > 0) + onContentsChanged(); + return filled; + } + + public int insertFluid(int amt) { + int remaining = experienceHolderBE.addExp(amt / 20); + int excessFluid = amt % 20; // Calculate remainder fluid (less than 1 XP) + return (remaining * 20) + excessFluid; + } + + public int extractFluid(int amt) { + int expNeeded = amt / 20; + int unAvailable = experienceHolderBE.subExp(expNeeded); + return (unAvailable * 20) + (amt % 20); + } + + @Override + public FluidStack drain(FluidStack resource, FluidAction action) { + if (resource.isEmpty() || !FluidStack.isSameFluidSameComponents(resource, fluid)) { + return FluidStack.EMPTY; + } + return drain(resource.getAmount(), action); + } + + @Override + public FluidStack drain(int maxDrain, FluidAction action) { + int drained = maxDrain - (maxDrain % 20); //Trim remainder + if (getFluidAmount() < drained) { + drained = getFluidAmount(); + } + FluidStack stack = fluid.copyWithAmount(drained); + if (action.execute() && drained > 0) { + extractFluid(drained); + onContentsChanged(); + } + return stack; + } + + @Override + protected void onContentsChanged() { + experienceHolderBE.markDirtyClient(); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/containers/ExperienceHolderContainer.java b/src/main/java/com/direwolf20/justdirethings/common/containers/ExperienceHolderContainer.java new file mode 100644 index 00000000..3fb00a0a --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/containers/ExperienceHolderContainer.java @@ -0,0 +1,37 @@ +package com.direwolf20.justdirethings.common.containers; + +import com.direwolf20.justdirethings.common.containers.basecontainers.BaseMachineContainer; +import com.direwolf20.justdirethings.setup.Registration; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.item.ItemStack; + +public class ExperienceHolderContainer extends BaseMachineContainer { + + public ExperienceHolderContainer(int windowId, Inventory playerInventory, FriendlyByteBuf extraData) { + this(windowId, playerInventory, extraData.readBlockPos()); + } + + public ExperienceHolderContainer(int windowId, Inventory playerInventory, BlockPos blockPos) { + super(Registration.Experience_Holder_Container.get(), windowId, playerInventory, blockPos); + addPlayerSlots(player.getInventory()); + } + + @Override + public boolean stillValid(Player playerIn) { + return stillValid(ContainerLevelAccess.create(player.level(), pos), player, Registration.ExperienceHolder.get()); + } + + @Override + public ItemStack quickMoveStack(Player playerIn, int index) { + return super.quickMoveStack(playerIn, index); //Only does filter slots! + } + + @Override + public void removed(Player playerIn) { + super.removed(playerIn); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluid.java b/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluid.java new file mode 100644 index 00000000..b7c1ff65 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluid.java @@ -0,0 +1,79 @@ +package com.direwolf20.justdirethings.common.fluids.xpfluid; + +import com.direwolf20.justdirethings.setup.Registration; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.neoforged.neoforge.fluids.BaseFlowingFluid; + +public abstract class XPFluid extends BaseFlowingFluid { + public static final Properties PROPERTIES = new Properties( + Registration.XP_FLUID_TYPE, + Registration.XP_FLUID_FLOWING, + Registration.XP_FLUID_SOURCE + ).bucket(Registration.XP_FLUID_BUCKET).block(Registration.XP_FLUID_BLOCK); + + protected XPFluid(Properties properties) { + super(properties); + } + + @Override + public Fluid getFlowing() { + return Registration.XP_FLUID_FLOWING.get(); + } + + @Override + public Fluid getSource() { + return Registration.XP_FLUID_SOURCE.get(); + } + + @Override + public Item getBucket() { + return Registration.XP_FLUID_BUCKET.get(); + } + + @Override + protected boolean canConvertToSource(Level pLevel) { + return false; + } + + public static class Flowing extends XPFluid { + public Flowing() { + super(PROPERTIES); + } + + @Override + protected void createFluidStateDefinition(StateDefinition.Builder pBuilder) { + super.createFluidStateDefinition(pBuilder); + pBuilder.add(LEVEL); + } + + @Override + public int getAmount(FluidState pState) { + return pState.getValue(LEVEL); + } + + @Override + public boolean isSource(FluidState pState) { + return false; + } + } + + public static class Source extends XPFluid { + public Source() { + super(PROPERTIES); + } + + @Override + public int getAmount(FluidState pState) { + return 8; + } + + @Override + public boolean isSource(FluidState pState) { + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluidBlock.java b/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluidBlock.java new file mode 100644 index 00000000..8ccfd77c --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluidBlock.java @@ -0,0 +1,22 @@ +package com.direwolf20.justdirethings.common.fluids.xpfluid; + +import com.direwolf20.justdirethings.setup.Registration; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.material.MapColor; +import net.minecraft.world.level.material.PushReaction; + +public class XPFluidBlock extends LiquidBlock { + public XPFluidBlock() { + super(Registration.XP_FLUID_SOURCE.get(), Properties.of() + .mapColor(MapColor.COLOR_LIGHT_GREEN) + .replaceable() + .noCollission() + .strength(100.0F) + .pushReaction(PushReaction.DESTROY) + .noLootTable() + .liquid() + .sound(SoundType.EMPTY) + ); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluidType.java b/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluidType.java new file mode 100644 index 00000000..959fdc48 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/fluids/xpfluid/XPFluidType.java @@ -0,0 +1,22 @@ +package com.direwolf20.justdirethings.common.fluids.xpfluid; + +import net.minecraft.core.BlockPos; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.material.FluidState; +import net.neoforged.neoforge.common.SoundActions; +import net.neoforged.neoforge.fluids.FluidType; + +public class XPFluidType extends FluidType { + public XPFluidType() { + super(Properties.create().density(1000).viscosity(1000).temperature(300) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY) + .sound(SoundActions.FLUID_VAPORIZE, SoundEvents.FIRE_EXTINGUISH)); + } + + @Override + public boolean canConvertToSource(FluidState state, LevelReader reader, BlockPos pos) { + return false; + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java b/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java index 0a8bb021..076125d0 100644 --- a/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java +++ b/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java @@ -38,6 +38,8 @@ public static void registerNetworking(final RegisterPayloadHandlersEvent event) registrar.playToServer(InventoryHolderSaveSlotPayload.TYPE, InventoryHolderSaveSlotPayload.STREAM_CODEC, InventoryHolderSaveSlotPacket.get()::handle); registrar.playToServer(InventoryHolderSettingsPayload.TYPE, InventoryHolderSettingsPayload.STREAM_CODEC, InventoryHolderSettingsPacket.get()::handle); registrar.playToServer(InventoryHolderMoveItemsPayload.TYPE, InventoryHolderMoveItemsPayload.STREAM_CODEC, InventoryHolderMoveItemsPacket.get()::handle); + registrar.playToServer(ExperienceHolderPayload.TYPE, ExperienceHolderPayload.STREAM_CODEC, ExperienceHolderPacket.get()::handle); + registrar.playToServer(ExperienceHolderSettingsPayload.TYPE, ExperienceHolderSettingsPayload.STREAM_CODEC, ExperienceHolderSettingsPacket.get()::handle); //Going to Client registrar.playToClient(ClientSoundPayload.TYPE, ClientSoundPayload.STREAM_CODEC, ClientSoundPacket.get()::handle); diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderPayload.java b/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderPayload.java new file mode 100644 index 00000000..5f9ee887 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderPayload.java @@ -0,0 +1,26 @@ +package com.direwolf20.justdirethings.common.network.data; + +import com.direwolf20.justdirethings.JustDireThings; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record ExperienceHolderPayload( + boolean add, + int levels +) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "experience_holder")); + + @Override + public Type type() { + return TYPE; + } + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, ExperienceHolderPayload::add, + ByteBufCodecs.INT, ExperienceHolderPayload::levels, + ExperienceHolderPayload::new + ); +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderSettingsPayload.java b/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderSettingsPayload.java new file mode 100644 index 00000000..00a93211 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderSettingsPayload.java @@ -0,0 +1,28 @@ +package com.direwolf20.justdirethings.common.network.data; + +import com.direwolf20.justdirethings.JustDireThings; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record ExperienceHolderSettingsPayload( + int targetExp, + boolean ownerOnly, + boolean collectExp +) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "experience_holder_settings")); + + @Override + public Type type() { + return TYPE; + } + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, ExperienceHolderSettingsPayload::targetExp, + ByteBufCodecs.BOOL, ExperienceHolderSettingsPayload::ownerOnly, + ByteBufCodecs.BOOL, ExperienceHolderSettingsPayload::collectExp, + ExperienceHolderSettingsPayload::new + ); +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderPacket.java b/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderPacket.java new file mode 100644 index 00000000..54f213c7 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderPacket.java @@ -0,0 +1,30 @@ +package com.direwolf20.justdirethings.common.network.handler; + +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; +import com.direwolf20.justdirethings.common.containers.ExperienceHolderContainer; +import com.direwolf20.justdirethings.common.network.data.ExperienceHolderPayload; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public class ExperienceHolderPacket { + public static final ExperienceHolderPacket INSTANCE = new ExperienceHolderPacket(); + + public static ExperienceHolderPacket get() { + return INSTANCE; + } + + public void handle(final ExperienceHolderPayload payload, final IPayloadContext context) { + context.enqueueWork(() -> { + Player sender = context.player(); + AbstractContainerMenu container = sender.containerMenu; + + if (container instanceof ExperienceHolderContainer experienceHolderContainer && experienceHolderContainer.baseMachineBE instanceof ExperienceHolderBE experienceHolderBE) { + if (payload.add()) + experienceHolderBE.storeExp(sender, payload.levels()); + else + experienceHolderBE.extractExp(sender, payload.levels()); + } + }); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderSettingsPacket.java b/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderSettingsPacket.java new file mode 100644 index 00000000..986e2360 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderSettingsPacket.java @@ -0,0 +1,27 @@ +package com.direwolf20.justdirethings.common.network.handler; + +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; +import com.direwolf20.justdirethings.common.containers.ExperienceHolderContainer; +import com.direwolf20.justdirethings.common.network.data.ExperienceHolderSettingsPayload; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public class ExperienceHolderSettingsPacket { + public static final ExperienceHolderSettingsPacket INSTANCE = new ExperienceHolderSettingsPacket(); + + public static ExperienceHolderSettingsPacket get() { + return INSTANCE; + } + + public void handle(final ExperienceHolderSettingsPayload payload, final IPayloadContext context) { + context.enqueueWork(() -> { + Player sender = context.player(); + AbstractContainerMenu container = sender.containerMenu; + + if (container instanceof ExperienceHolderContainer experienceHolderContainer && experienceHolderContainer.baseMachineBE instanceof ExperienceHolderBE experienceHolderBE) { + experienceHolderBE.changeSettings(sender, payload.targetExp(), payload.ownerOnly(), payload.collectExp()); + } + }); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireBlockTags.java b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireBlockTags.java index 0710f39f..18916d73 100644 --- a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireBlockTags.java +++ b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireBlockTags.java @@ -83,7 +83,8 @@ protected void addTags(HolderLookup.Provider provider) { .add(Registration.TimeCrystalCluster.get()) .add(Registration.TimeCrystalBlock.get()) .add(Registration.ParadoxMachine.get()) - .add(Registration.InventoryHolder.get()); + .add(Registration.InventoryHolder.get()) + .add(Registration.ExperienceHolder.get()); tag(LAWNMOWERABLE) .addTag(BlockTags.FLOWERS) .add(Blocks.TALL_GRASS) diff --git a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireItemModels.java b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireItemModels.java index 6c2c2697..c1619535 100644 --- a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireItemModels.java +++ b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireItemModels.java @@ -75,6 +75,7 @@ protected void registerModels() { withExistingParent(Registration.TimeCrystalCluster.getId().getPath(), modLoc("block/time_crystal_cluster")); withExistingParent(Registration.ParadoxMachine.getId().getPath(), modLoc("block/paradoxmachine")); withExistingParent(Registration.InventoryHolder.getId().getPath(), modLoc("block/inventory_holder")); + withExistingParent(Registration.ExperienceHolder_ITEM.getId().getPath(), modLoc("block/experienceholder")); //Item items singleTexture(Registration.Fuel_Canister.getId().getPath(), mcLoc("item/generated"), "layer0", modLoc("item/fuel_canister")); diff --git a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java index 3b3bec4e..7e666b62 100644 --- a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java +++ b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java @@ -59,6 +59,7 @@ protected void addTranslations() { add(Registration.TimeCrystalCluster_Large.get(), "Large Time Crystal Cluster"); add(Registration.ParadoxMachine.get(), "Paradox Machine"); add(Registration.InventoryHolder.get(), "Inventory Holder"); + add(Registration.ExperienceHolder.get(), "Experience Holder"); //Fluids add(Registration.PORTAL_FLUID_BLOCK.get(), "Portal Fluid"); @@ -91,6 +92,9 @@ protected void addTranslations() { add(Registration.TIME_FLUID_BLOCK.get(), "Time Fluid"); add(Registration.TIME_FLUID_BUCKET.get(), "Time Fluid Bucket"); add("fluid_type.justdirethings.time_fluid_type", "Time Fluid"); + add(Registration.XP_FLUID_BLOCK.get(), "XP Fluid"); + add(Registration.XP_FLUID_BUCKET.get(), "XP Fluid Bucket"); + add("fluid_type.justdirethings.xp_fluid_type", "XP Fluid"); //Resources add(Registration.FerricoreBlock.get(), "Ferricore Block"); @@ -520,6 +524,13 @@ protected void addTranslations() { add("justdirethings.screen.senditems", "Push Items"); add("justdirethings.screen.pullitems", "Pull Items"); add("justdirethings.screen.swapitems", "Swap Items"); + add("justdirethings.screen.storeexp", "Store Level"); + add("justdirethings.screen.retrieveexp", "Retrieve Level"); + add("justdirethings.screen.targetexp", "Target Level"); + add("justdirethings.screen.owneronly", "Owner Only"); + add("justdirethings.screen.collectexp", "Collect Experience"); + add("justdirethings.screen.pushfluids", "Push Fluids"); + add("justdirethings.screen.pullfluids", "Pull Fluids"); //Buttons //add("justdirethings.buttons.save", "Save"); diff --git a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLootTables.java b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLootTables.java index ec927f7e..06526459 100644 --- a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLootTables.java +++ b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLootTables.java @@ -69,6 +69,7 @@ protected void generate() { add(Registration.TimeCrystalBuddingBlock.get(), noDrop()); dropSelf(Registration.ParadoxMachine.get()); dropSelf(Registration.InventoryHolder.get()); + dropSelf(Registration.ExperienceHolder.get()); //Raw Ores add(Registration.RawFerricoreOre.get(), createSilkTouchDispatchTable( diff --git a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireRecipes.java b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireRecipes.java index 27377a6c..7550399b 100644 --- a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireRecipes.java +++ b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireRecipes.java @@ -100,7 +100,18 @@ protected void buildRecipes(RecipeOutput consumer) { .define('d', Registration.Celestigem.get()) .define('h', Items.REDSTONE) .group("justdirethings") - .unlockedBy("has_ferricore_ingot", InventoryChangeTrigger.TriggerInstance.hasItems(Registration.FerricoreIngot.get())) + .unlockedBy("has_blazegold_ingot", InventoryChangeTrigger.TriggerInstance.hasItems(Registration.BlazegoldIngot.get())) + .save(consumer); + ShapedRecipeBuilder.shaped(RecipeCategory.MISC, Registration.ExperienceHolder.get()) + .pattern(" d ") + .pattern("heh") + .pattern("fff") + .define('e', Items.ENDER_PEARL) + .define('f', Registration.BlazegoldIngot.get()) + .define('d', Items.EMERALD) + .define('h', Items.BOOK) + .group("justdirethings") + .unlockedBy("has_blazegold_ingot", InventoryChangeTrigger.TriggerInstance.hasItems(Registration.BlazegoldIngot.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, Registration.BlockBreakerT1.get()) .pattern("fdf") diff --git a/src/main/java/com/direwolf20/justdirethings/setup/ClientSetup.java b/src/main/java/com/direwolf20/justdirethings/setup/ClientSetup.java index 1c0984c7..2f662dfe 100644 --- a/src/main/java/com/direwolf20/justdirethings/setup/ClientSetup.java +++ b/src/main/java/com/direwolf20/justdirethings/setup/ClientSetup.java @@ -189,6 +189,7 @@ public static void registerScreens(RegisterMenuScreensEvent event) { event.register(Registration.PotionCanister_Container.get(), PotionCanisterScreen::new); event.register(Registration.ParadoxMachine_Container.get(), ParadoxMachineScreen::new); event.register(Registration.InventoryHolder_Container.get(), InventoryHolderScreen::new); + event.register(Registration.Experience_Holder_Container.get(), ExperienceHolderScreen::new); } @SubscribeEvent @@ -216,6 +217,7 @@ public static void registerRenderers(EntityRenderersEvent.RegisterRenderers even event.registerBlockEntityRenderer(Registration.FluidCollectorT2BE.get(), FluidCollectorT2BER::new); event.registerBlockEntityRenderer(Registration.ParadoxMachineBE.get(), ParadoxMachineBER::new); event.registerBlockEntityRenderer(Registration.InventoryHolderBE.get(), InventoryHolderBER::new); + event.registerBlockEntityRenderer(Registration.ExperienceHolderBE.get(), ExperienceHolderBER::new); //Entities event.registerEntityRenderer(Registration.CreatureCatcherEntity.get(), CreatureCatcherEntityRender::new); @@ -553,6 +555,37 @@ public int getTintColor(FluidState state, BlockAndTintGetter getter, BlockPos po return 0x7700FF00; } }, Registration.TIME_FLUID_TYPE.get()); + event.registerFluidType(new IClientFluidTypeExtensions() { + @Override + public ResourceLocation getStillTexture() { + return WATER_STILL; + } + + @Override + public ResourceLocation getFlowingTexture() { + return WATER_FLOW; + } + + @Override + public ResourceLocation getOverlayTexture() { + return WATER_OVERLAY; + } + + @Override + public ResourceLocation getRenderOverlayTexture(Minecraft mc) { + return UNDERWATER_LOCATION; + } + + @Override + public int getTintColor() { + return 0xFF32CD32; + } + + @Override + public int getTintColor(FluidState state, BlockAndTintGetter getter, BlockPos pos) { + return 0xFF32CD32; + } + }, Registration.XP_FLUID_TYPE.get()); } @SubscribeEvent diff --git a/src/main/java/com/direwolf20/justdirethings/setup/Registration.java b/src/main/java/com/direwolf20/justdirethings/setup/Registration.java index 790b9c11..c5c1c384 100644 --- a/src/main/java/com/direwolf20/justdirethings/setup/Registration.java +++ b/src/main/java/com/direwolf20/justdirethings/setup/Registration.java @@ -51,6 +51,9 @@ import com.direwolf20.justdirethings.common.fluids.unstableportalfluid.UnstablePortalFluid; import com.direwolf20.justdirethings.common.fluids.unstableportalfluid.UnstablePortalFluidBlock; import com.direwolf20.justdirethings.common.fluids.unstableportalfluid.UnstablePortalFluidType; +import com.direwolf20.justdirethings.common.fluids.xpfluid.XPFluid; +import com.direwolf20.justdirethings.common.fluids.xpfluid.XPFluidBlock; +import com.direwolf20.justdirethings.common.fluids.xpfluid.XPFluidType; import com.direwolf20.justdirethings.common.items.*; import com.direwolf20.justdirethings.common.items.abilityupgrades.Upgrade; import com.direwolf20.justdirethings.common.items.abilityupgrades.UpgradeBlank; @@ -313,6 +316,18 @@ public static void init(IEventBus eventBus) { public static final DeferredHolder REFINED_T4_FLUID_BUCKET = BUCKET_ITEMS.register("refined_t4_fluid_bucket", () -> new BucketItem(Registration.REFINED_T4_FLUID_SOURCE.get(), new Item.Properties().craftRemainder(Items.BUCKET).stacksTo(1))); + //XP Fluid + public static final DeferredHolder XP_FLUID_TYPE = FLUID_TYPES.register("xp_fluid_type", + XPFluidType::new); + public static final DeferredHolder XP_FLUID_FLOWING = FLUIDS.register("xp_fluid_flowing", + XPFluid.Flowing::new); + public static final DeferredHolder XP_FLUID_SOURCE = FLUIDS.register("xp_fluid_source", + XPFluid.Source::new); + public static final DeferredHolder XP_FLUID_BLOCK = FLUID_BLOCKS.register("xp_fluid_block", + XPFluidBlock::new); + public static final DeferredHolder XP_FLUID_BUCKET = BUCKET_ITEMS.register("xp_fluid_bucket", + () -> new BucketItem(Registration.XP_FLUID_SOURCE.get(), new Item.Properties().craftRemainder(Items.BUCKET).stacksTo(1))); + //Machines public static final DeferredHolder ItemCollector = BLOCKS.register("itemcollector", ItemCollector::new); @@ -355,6 +370,8 @@ public static void init(IEventBus eventBus) { public static final DeferredHolder ParadoxMachine_ITEM = ITEMS.register("paradoxmachine", () -> new BlockItem(ParadoxMachine.get(), new Item.Properties())); public static final DeferredHolder InventoryHolder = BLOCKS.register("inventory_holder", InventoryHolder::new); public static final DeferredHolder InventoryHolder_ITEM = ITEMS.register("inventory_holder", () -> new BlockItem(InventoryHolder.get(), new Item.Properties())); + public static final DeferredHolder ExperienceHolder = BLOCKS.register("experienceholder", ExperienceHolder::new); + public static final DeferredHolder ExperienceHolder_ITEM = ITEMS.register("experienceholder", () -> new BlockItem(ExperienceHolder.get(), new Item.Properties())); //Power Machines @@ -457,6 +474,7 @@ public static void init(IEventBus eventBus) { public static final DeferredHolder, BlockEntityType> FluidCollectorT2BE = BLOCK_ENTITIES.register("fluidcollectort2", () -> BlockEntityType.Builder.of(FluidCollectorT2BE::new, FluidCollectorT2.get()).build(null)); public static final DeferredHolder, BlockEntityType> ParadoxMachineBE = BLOCK_ENTITIES.register("paradoxmachine", () -> BlockEntityType.Builder.of(ParadoxMachineBE::new, ParadoxMachine.get()).build(null)); public static final DeferredHolder, BlockEntityType> InventoryHolderBE = BLOCK_ENTITIES.register("inventory_holder", () -> BlockEntityType.Builder.of(InventoryHolderBE::new, InventoryHolder.get()).build(null)); + public static final DeferredHolder, BlockEntityType> ExperienceHolderBE = BLOCK_ENTITIES.register("experienceholder", () -> BlockEntityType.Builder.of(ExperienceHolderBE::new, ExperienceHolder.get()).build(null)); //Items - Raw Resources public static final DeferredHolder RawFerricore = ITEMS.register("raw_ferricore", RawFerricore::new); @@ -713,6 +731,8 @@ public static void init(IEventBus eventBus) { () -> IMenuTypeExtension.create(ParadoxMachineContainer::new)); public static final DeferredHolder, MenuType> InventoryHolder_Container = CONTAINERS.register("inventoryholder_container", () -> IMenuTypeExtension.create(InventoryHolderContainer::new)); + public static final DeferredHolder, MenuType> Experience_Holder_Container = CONTAINERS.register("experienceholder_container", + () -> IMenuTypeExtension.create(ExperienceHolderContainer::new)); //Data Attachments public static final Supplier> HANDLER = ATTACHMENT_TYPES.register( diff --git a/src/main/java/com/direwolf20/justdirethings/util/ExperienceUtils.java b/src/main/java/com/direwolf20/justdirethings/util/ExperienceUtils.java new file mode 100644 index 00000000..a5eeeb8f --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/util/ExperienceUtils.java @@ -0,0 +1,84 @@ +package com.direwolf20.justdirethings.util; + +import net.minecraft.world.entity.player.Player; + +public class ExperienceUtils { + // Calculate experience required to go from level 0 to target level + public static int getTotalExperienceForLevel(int level) { + if (level <= 16) { + return level * level + 6 * level; + } else if (level <= 31) { + return (int) (2.5 * level * level - 40.5 * level + 360); + } else { + return (int) (4.5 * level * level - 162.5 * level + 2220); + } + } + + public static int getLevelFromTotalExperience(int totalExp) { + // Check in the level ranges where experience requirements change + if (totalExp < getTotalExperienceForLevel(16)) { + // Level range 0-15 + return (int) Math.floor((-6 + Math.sqrt(36 + 4 * totalExp)) / 2); + } else if (totalExp < getTotalExperienceForLevel(31)) { + // Level range 16-30 + return (int) Math.floor((40.5 + Math.sqrt(-40.5 * -40.5 - 4 * 2.5 * (360 - totalExp))) / (2 * 2.5)); + } else { + // Level range 31+ + return (int) Math.floor((162.5 + Math.sqrt(-162.5 * -162.5 - 4 * 4.5 * (2220 - totalExp))) / (2 * 4.5)); + } + } + + // Remove levels from a player, ensuring they don't lose more than available + public static int removeLevels(Player player, int levelsToRemove) { + int currentTotalExp = getPlayerTotalExperience(player); + int targetLevel = Math.max(0, player.experienceLevel - levelsToRemove); + + // Calculate how much exp is required to be at the target level + int targetTotalExp = getTotalExperienceForLevel(targetLevel); + int expToRemove = currentTotalExp - targetTotalExp; + + player.giveExperienceLevels(-levelsToRemove); + return expToRemove; // Amount of exp removed + } + + // Calculate experience required to go from one level to the next + public static int getExperienceForNextLevel(int level) { + if (level >= 30) { + return 112 + (level - 30) * 9; + } else { + return level >= 15 ? 37 + (level - 15) * 5 : 7 + level * 2; + } + } + + // Calculate total experience points player currently has (given level and progress) + public static int getPlayerTotalExperience(Player player) { + int exp = getTotalExperienceForLevel(player.experienceLevel); + exp += Math.round(player.experienceProgress * player.getXpNeededForNextLevel()); + return exp; + } + + public static int getExpNeededForNextLevel(Player player) { + return player.getXpNeededForNextLevel() - (int) (player.experienceProgress * player.getXpNeededForNextLevel()); + } + + // Get the progress to the next level as a fraction (float) from total experience points + public static float getProgressToNextLevel(int totalExp) { + int level = getLevelFromTotalExperience(totalExp); // Get the number of full levels + int expForCurrentLevel = getTotalExperienceForLevel(level); // Total exp required to reach this level + int expForNextLevel = getExperienceForNextLevel(level); // Exp needed for the next level + + // Remaining experience after subtracting full levels + int expAfterFullLevels = totalExp - expForCurrentLevel; + + // Calculate the fractional progress (as a float between 0.0 and 1.0) + return (float) expAfterFullLevels / (float) expForNextLevel; + } + + // Remove points from a player, ensuring they don't lose more than available + public static int removePoints(Player player, int pointsToRemove) { + int currentTotalExp = getPlayerTotalExperience(player); + int expToRemove = Math.min(currentTotalExp, pointsToRemove); + player.giveExperiencePoints(-expToRemove); + return expToRemove; // Amount of exp removed + } +} diff --git a/src/main/resources/assets/justdirethings/blockstates/experienceholder.json b/src/main/resources/assets/justdirethings/blockstates/experienceholder.json new file mode 100644 index 00000000..3418fe5f --- /dev/null +++ b/src/main/resources/assets/justdirethings/blockstates/experienceholder.json @@ -0,0 +1,37 @@ +{ + "variants": { + "facing=down": { + "model": "justdirethings:block/experienceholder" + }, + "facing=east": { + "model": "justdirethings:block/experienceholder", + "x": -90, + "y": 90, + "z": 0 + }, + "facing=north": { + "model": "justdirethings:block/experienceholder", + "x": -90, + "y": 0, + "z": 0 + }, + "facing=south": { + "model": "justdirethings:block/experienceholder", + "x": -90, + "y": 180, + "z": 0 + }, + "facing=west": { + "model": "justdirethings:block/experienceholder", + "x": -90, + "y": 270, + "z": 0 + }, + "facing=up": { + "model": "justdirethings:block/experienceholder", + "x": -180, + "y": 180, + "z": 180 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/justdirethings/models/block/experienceholder.json b/src/main/resources/assets/justdirethings/models/block/experienceholder.json new file mode 100644 index 00000000..4cf1fbec --- /dev/null +++ b/src/main/resources/assets/justdirethings/models/block/experienceholder.json @@ -0,0 +1,738 @@ +{ + "credit": "Made with Blockbench", + "texture_size": [ + 32, + 32 + ], + "textures": { + "0": "justdirethings:block/experienceholder", + "1": "justdirethings:block/experienceholder_animation", + "particle": "justdirethings:block/experienceholder" + }, + "elements": [ + { + "from": [ + 4, + 0, + 4 + ], + "to": [ + 12, + 1, + 12 + ], + "faces": { + "north": { + "uv": [ + 0, + 3, + 4, + 3.5 + ], + "texture": "#0" + }, + "east": { + "uv": [ + 0, + 4.5, + 4, + 5 + ], + "texture": "#0" + }, + "south": { + "uv": [ + 0, + 3, + 4, + 3.5 + ], + "texture": "#0" + }, + "west": { + "uv": [ + 0, + 4.5, + 4, + 5 + ], + "texture": "#0" + }, + "up": { + "uv": [ + 0, + 0, + 4, + 4 + ], + "texture": "#missing", + "cullface": "up" + }, + "down": { + "uv": [ + 0, + 6, + 4, + 10 + ], + "texture": "#0" + } + } + }, + { + "from": [ + 3, + 1, + 3 + ], + "to": [ + 13, + 2, + 13 + ], + "faces": { + "north": { + "uv": [ + 6, + 0, + 11, + 0.5 + ], + "rotation": 180, + "texture": "#0" + }, + "east": { + "uv": [ + 10.5, + 0, + 11, + 5 + ], + "rotation": 90, + "texture": "#0" + }, + "south": { + "uv": [ + 6, + 4.5, + 11, + 5 + ], + "texture": "#0" + }, + "west": { + "uv": [ + 6, + 0, + 6.5, + 5 + ], + "rotation": 270, + "texture": "#0" + }, + "up": { + "uv": [ + 6, + 0, + 11, + 5 + ], + "texture": "#0" + }, + "down": { + "uv": [ + 6, + 5, + 11, + 0 + ], + "texture": "#0" + } + } + }, + { + "from": [ + 6, + 2, + 11 + ], + "to": [ + 10, + 8, + 12 + ], + "rotation": { + "angle": 0, + "axis": "x", + "origin": [ + 8, + 2, + 12 + ] + }, + "faces": { + "north": { + "uv": [ + 0, + 0, + 2, + 2.5 + ], + "texture": "#1", + "cullface": "north" + }, + "east": { + "uv": [ + 0, + 0, + 1, + 6 + ], + "rotation": 180, + "texture": "#1" + }, + "south": { + "uv": [ + 0, + 0, + 4, + 6 + ], + "rotation": 180, + "texture": "#1" + }, + "west": { + "uv": [ + 3, + 0, + 4, + 6 + ], + "rotation": 180, + "texture": "#1" + }, + "up": { + "uv": [ + 4, + 5, + 0, + 6 + ], + "texture": "#1", + "cullface": "up" + }, + "down": { + "uv": [ + 1, + 4, + 3, + 4.5 + ], + "texture": "#1", + "cullface": "down" + } + } + }, + { + "from": [ + 6, + 2, + 4 + ], + "to": [ + 10, + 8, + 5 + ], + "rotation": { + "angle": 0, + "axis": "x", + "origin": [ + 8, + 2, + 4 + ] + }, + "faces": { + "north": { + "uv": [ + 8, + 0, + 4, + 6 + ], + "rotation": 180, + "texture": "#1" + }, + "east": { + "uv": [ + 5, + 0, + 4, + 6 + ], + "rotation": 180, + "texture": "#1" + }, + "south": { + "uv": [ + 6, + 0, + 4, + 2.5 + ], + "texture": "#1", + "cullface": "north" + }, + "west": { + "uv": [ + 8, + 0, + 7, + 6 + ], + "rotation": 180, + "texture": "#1" + }, + "up": { + "uv": [ + 8, + 6, + 4, + 5 + ], + "texture": "#1", + "cullface": "up" + }, + "down": { + "uv": [ + 5, + 4.5, + 7, + 4 + ], + "texture": "#1", + "cullface": "down" + } + } + }, + { + "from": [ + 11, + 2, + 6 + ], + "to": [ + 12, + 8, + 10 + ], + "rotation": { + "angle": 0, + "axis": "x", + "origin": [ + 8, + 2, + 4 + ] + }, + "faces": { + "north": { + "uv": [ + 4, + 6, + 3, + 12 + ], + "rotation": 180, + "texture": "#1" + }, + "east": { + "uv": [ + 4, + 6, + 0, + 12 + ], + "rotation": 180, + "texture": "#1" + }, + "south": { + "uv": [ + 1, + 6, + 0, + 12 + ], + "rotation": 180, + "texture": "#1" + }, + "west": { + "uv": [ + 2, + 6, + 0, + 8.5 + ], + "texture": "#1", + "cullface": "north" + }, + "up": { + "uv": [ + 4, + 12, + 0, + 11 + ], + "rotation": 90, + "texture": "#1", + "cullface": "up" + }, + "down": { + "uv": [ + 1, + 10.5, + 3, + 10 + ], + "rotation": 270, + "texture": "#1", + "cullface": "down" + } + } + }, + { + "from": [ + 4, + 2, + 6 + ], + "to": [ + 5, + 8, + 10 + ], + "rotation": { + "angle": 0, + "axis": "x", + "origin": [ + 8, + 2, + 4 + ] + }, + "faces": { + "north": { + "uv": [ + 7, + 6, + 8, + 12 + ], + "rotation": 180, + "texture": "#1" + }, + "east": { + "uv": [ + 4, + 6, + 6, + 8.5 + ], + "texture": "#1", + "cullface": "north" + }, + "south": { + "uv": [ + 4, + 6, + 5, + 12 + ], + "rotation": 180, + "texture": "#1" + }, + "west": { + "uv": [ + 4, + 6, + 8, + 12 + ], + "rotation": 180, + "texture": "#1" + }, + "up": { + "uv": [ + 8, + 11, + 4, + 12 + ], + "rotation": 90, + "texture": "#1", + "cullface": "up" + }, + "down": { + "uv": [ + 5, + 10, + 7, + 10.5 + ], + "rotation": 270, + "texture": "#1", + "cullface": "down" + } + } + }, + { + "from": [ + 5, + 2, + 5 + ], + "to": [ + 11, + 9, + 11 + ], + "faces": { + "north": { + "uv": [ + 0, + 10, + 3, + 13.5 + ], + "texture": "#0" + }, + "east": { + "uv": [ + 9, + 10, + 12, + 13.5 + ], + "texture": "#0" + }, + "south": { + "uv": [ + 6, + 10, + 9, + 13.5 + ], + "texture": "#0" + }, + "west": { + "uv": [ + 3, + 10, + 6, + 13.5 + ], + "texture": "#0" + }, + "up": { + "uv": [ + 0, + 0, + 3, + 3 + ], + "texture": "#0" + }, + "down": { + "uv": [ + 0, + 0, + 2, + 2 + ], + "texture": "#missing", + "cullface": "down" + } + } + }, + { + "from": [ + 7, + 9, + 7 + ], + "to": [ + 9, + 11, + 9 + ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ + 0, + 3, + 0 + ] + }, + "faces": { + "north": { + "uv": [ + 5, + 1, + 6, + 2 + ], + "texture": "#0" + }, + "east": { + "uv": [ + 6, + 0, + 5, + 1 + ], + "texture": "#0" + }, + "south": { + "uv": [ + 5, + 0, + 6, + 1 + ], + "texture": "#0" + }, + "west": { + "uv": [ + 6, + 1, + 5, + 2 + ], + "texture": "#0" + }, + "up": { + "uv": [ + 5, + 2, + 6, + 3 + ], + "texture": "#0" + }, + "down": { + "uv": [ + 0, + 0, + 1, + 1 + ], + "texture": "#missing", + "cullface": "down" + } + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [ + 75, + 45, + 0 + ], + "translation": [ + 0, + 2.5, + 2 + ], + "scale": [ + 0.375, + 0.375, + 0.375 + ] + }, + "thirdperson_lefthand": { + "rotation": [ + 75, + 45, + 0 + ], + "translation": [ + 0, + 2.5, + 2 + ], + "scale": [ + 0.375, + 0.375, + 0.375 + ] + }, + "firstperson_righthand": { + "rotation": [ + 0, + 45, + 0 + ], + "translation": [ + 0, + 2.5, + 0 + ], + "scale": [ + 0.6, + 0.6, + 0.6 + ] + }, + "firstperson_lefthand": { + "rotation": [ + 0, + 225, + 0 + ], + "translation": [ + 0, + 2.5, + 0 + ], + "scale": [ + 0.6, + 0.6, + 0.6 + ] + }, + "ground": { + "translation": [ + 0, + 3, + 0 + ], + "scale": [ + 0.25, + 0.25, + 0.25 + ] + }, + "gui": { + "rotation": [ + 30, + 225, + 0 + ], + "translation": [ + 0, + 3, + 0 + ] + }, + "fixed": { + "scale": [ + 0.5, + 0.5, + 0.5 + ] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/justdirethings/patchouli_books/justdirethingsbook/en_us/entries/mach_experienceholder.json b/src/main/resources/assets/justdirethings/patchouli_books/justdirethingsbook/en_us/entries/mach_experienceholder.json new file mode 100644 index 00000000..48e10b4a --- /dev/null +++ b/src/main/resources/assets/justdirethings/patchouli_books/justdirethingsbook/en_us/entries/mach_experienceholder.json @@ -0,0 +1,41 @@ +{ + "name": "Experience Holder", + "sortnum": 3, + "category": "justdirethings:machines", + "icon": "justdirethings:experienceholder", + "pages": [ + { + "type": "patchouli:text", + "text": "The experience holder does what it says on the tin. It holds experience!$(br2)In the UI, you'll see the current levels stored (represented by the green number), and the partial levels stored (represented by the green exp bar). Clicking the + or - buttons will transfer 1 level from you (the player) to the block." + }, + { + "type": "patchouli:text", + "text": "Hold Shift while clicking these buttons to transfer 10 levels at a time, and hold Ctrl while clicking to transfer ALL your levels at once!$(br2)You'll notice this has an Area of Effect on it, which serves two purposes. First of which is automating nearby player's experience levels." + }, + { + "type": "patchouli:text", + "text": "Using the Number Button to the left of the - button, set the desired amount of experience a player inside the area of affect should have. Then, use the redstone control button to determine when this occurs, either on pulse (by default) or always. The machine will use the Speed (ticks) to attempt to modify nearby player(s) levels every so often." + }, + { + "type": "patchouli:text", + "text": "The machine will add or remove levels to the player as needed to get that player's level at the target -- assuming of course theres enough exp stored in the machine to raise them that high!$(br2)Toggle the 'owner only' button to ensure this only gives experience to the player who originally placed the block." + }, + { + "type": "patchouli:text", + "text": "Use this to automatically store your experience when you return to your base by setting it to 0, or keep your experience always at level 30 while enchanting at an enchanting table! Every time you enchant something, this machine can automatically top you off back to exactly level 30!" + }, + { + "type": "patchouli:text", + "text": "In addition, there is a 'Collect Experience' button, which will attempt to find experience orbs inside the area of effect. When this is enabled, it will absorb any exp orbs nearby, and store them. This feature ignores the redstone state of the block, and either runs or not based on this buttons setting." + }, + { + "type": "patchouli:text", + "text": "The block is a fluid tank as well, and can transfer exp points into XP Fluid at a rate of 20mb per 1 experience point. You can interact with this block using either a bucket or the $(l:justdirethings:item_fluid_canister)Fluid Canister$(/l).$(br2)Its also possible to extract or insert the fluid using pipes or Laserio, etc.$(br2)The block will retain its experience when broken, so no worries if you need to relocate it!" + }, + { + "type": "patchouli:crafting", + "title": "Experience Holder", + "recipe": "justdirethings:experienceholder" + } + ] +} diff --git a/src/main/resources/assets/justdirethings/textures/block/experienceholder.png b/src/main/resources/assets/justdirethings/textures/block/experienceholder.png new file mode 100644 index 00000000..0be76da0 Binary files /dev/null and b/src/main/resources/assets/justdirethings/textures/block/experienceholder.png differ diff --git a/src/main/resources/assets/justdirethings/textures/block/experienceholder_animation.png b/src/main/resources/assets/justdirethings/textures/block/experienceholder_animation.png new file mode 100644 index 00000000..8730fdc5 Binary files /dev/null and b/src/main/resources/assets/justdirethings/textures/block/experienceholder_animation.png differ diff --git a/src/main/resources/assets/justdirethings/textures/block/experienceholder_animation.png.mcmeta b/src/main/resources/assets/justdirethings/textures/block/experienceholder_animation.png.mcmeta new file mode 100644 index 00000000..752d1823 --- /dev/null +++ b/src/main/resources/assets/justdirethings/textures/block/experienceholder_animation.png.mcmeta @@ -0,0 +1,17 @@ +{ + "animation": { + "interpolate": true, + "frametime": 4, + "frames": [ + 4, + 0, + 1, + 2, + 3, + { + "index": 4, + "time": 15 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/justdirethings/textures/gui/buttons/pullfluids.png b/src/main/resources/assets/justdirethings/textures/gui/buttons/pullfluids.png new file mode 100644 index 00000000..c359c676 Binary files /dev/null and b/src/main/resources/assets/justdirethings/textures/gui/buttons/pullfluids.png differ diff --git a/src/main/resources/assets/justdirethings/textures/gui/buttons/pushfluids.png b/src/main/resources/assets/justdirethings/textures/gui/buttons/pushfluids.png new file mode 100644 index 00000000..e823850c Binary files /dev/null and b/src/main/resources/assets/justdirethings/textures/gui/buttons/pushfluids.png differ