diff --git a/changelog.md b/changelog.md index e62e75725f..a24b5b089e 100644 --- a/changelog.md +++ b/changelog.md @@ -149,3 +149,6 @@ _Now using Flywheel 1.0_ - Synced AllPortalTracks with Create Fabric - Implemented DyeHelper api (#7265) - Implemented api to add custom block train conductors (#7030) +- Convert Potato Cannon project types into a dynamic registry + - Everything can be done with datapacks now, and there is no need to write a mod unless you need to add new + Render Modes, Entity Hit Actions or Block Hit Actions diff --git a/src/generated/resources/.cache/eaed56ca9d9781c7626be345dd9f2c9a1fad638e b/src/generated/resources/.cache/eaed56ca9d9781c7626be345dd9f2c9a1fad638e index 8022840b42..9dad6da61c 100644 --- a/src/generated/resources/.cache/eaed56ca9d9781c7626be345dd9f2c9a1fad638e +++ b/src/generated/resources/.cache/eaed56ca9d9781c7626be345dd9f2c9a1fad638e @@ -1,4 +1,29 @@ -// 1.20.1 2025-02-02T11:59:39.5970888 Create's Generated Registry Entries +// 1.20.1 2025-02-23T18:42:20.032170723 Create's Generated Registry Entries +abb9dd2c98388abb430343d4ac02d460e5edc086 data/create/create/potato_projectile/type/apple.json +1a20cb4e64dc07918a28fbccc73102bca3560f16 data/create/create/potato_projectile/type/baked_potato.json +8450576952f7723d22250da6f554dd0fda4e9fce data/create/create/potato_projectile/type/beetroot.json +9d927ec9c8b76a093e7ff8ff8f81be3a438d4aba data/create/create/potato_projectile/type/blaze_cake.json +46ccaf468caaa4faaca4f1494f60e99cd00785c9 data/create/create/potato_projectile/type/cake.json +22e0f5befe5835c7a9a6448e17e73e6fb7d0fb9c data/create/create/potato_projectile/type/carrot.json +af17d72549ee9421d2643a215ffc2291874fff93 data/create/create/potato_projectile/type/chocolate_berry.json +8cb4811601d46db5fdc0291edce3ec2f3a64e902 data/create/create/potato_projectile/type/chorus_fruit.json +9dd1298993a4657d69a9d2ac4de3ad7879d9aa8d data/create/create/potato_projectile/type/enchanted_golden_apple.json +e3ab3c4bd70ad742d37091ae05774aadffb1d1d5 data/create/create/potato_projectile/type/fallback.json +98bf0ce51484c5a9709fc69d046d0c70d987cfdd data/create/create/potato_projectile/type/fish.json +afe4e7cc87110d6f045f6672fb312a09290ca04a data/create/create/potato_projectile/type/glistering_melon.json +050c9333d41efd2577e1e597c7c68b00436b0963 data/create/create/potato_projectile/type/glow_berry.json +52046463af1abc92c5257992c302d7ab26c63e28 data/create/create/potato_projectile/type/golden_apple.json +c6234751ccad5111857b0ae9e297e42f529ad32a data/create/create/potato_projectile/type/golden_carrot.json +583b75978a4f920c2969473e5353fcc89886b612 data/create/create/potato_projectile/type/honeyed_apple.json +7a269ded36d1397f86f23c4c431c7a99fea74446 data/create/create/potato_projectile/type/melon_block.json +352402ba2fff3f23af178d15d61b0ba0a151097a data/create/create/potato_projectile/type/melon_slice.json +cbfd3028ad878bddbdaeb0eec0cd5a1c50d4c901 data/create/create/potato_projectile/type/poison_potato.json +b58e779803d29d4b36ecf79fde4e918577c71748 data/create/create/potato_projectile/type/potato.json +d15070ca23129e7d4025d86cb4be5701a466e593 data/create/create/potato_projectile/type/pufferfish.json +0c099b5f1bd7ba3832ea2d29e8c1c22c83ed1c3d data/create/create/potato_projectile/type/pumpkin_block.json +7d58a0671c55e40cdb728c60152665c89209fb91 data/create/create/potato_projectile/type/pumpkin_pie.json +2dc21c61b4fd4c60b9ade2974b395df42611ee26 data/create/create/potato_projectile/type/suspicious_stew.json +b4a7c10bd9cdcfe67df2a5766e5ec2e35a3f9b24 data/create/create/potato_projectile/type/sweet_berry.json 030ede1044384c4117ac1e491bf5c78bbd2842f5 data/create/damage_type/crush.json 92b0416950ffeb3ba68811e587177c2f8811c2c5 data/create/damage_type/cuckoo_surprise.json d2a4fdb64f4ba817e13a7b20c73fd1ca34b825fc data/create/damage_type/fan_fire.json diff --git a/src/generated/resources/data/create/create/potato_projectile/type/apple.json b/src/generated/resources/data/create/create/potato_projectile/type/apple.json new file mode 100644 index 0000000000..f2ccca1d80 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/apple.json @@ -0,0 +1,10 @@ +{ + "damage": 5, + "items": "minecraft:apple", + "knockback": 0.5, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.1, + "velocity_multiplier": 1.45 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/baked_potato.json b/src/generated/resources/data/create/create/potato_projectile/type/baked_potato.json new file mode 100644 index 0000000000..fc4ac7dcdb --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/baked_potato.json @@ -0,0 +1,14 @@ +{ + "damage": 5, + "items": "minecraft:baked_potato", + "knockback": 0.5, + "pre_entity_hit": { + "type": "create:set_on_fire", + "ticks": 60 + }, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "velocity_multiplier": 1.25 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/beetroot.json b/src/generated/resources/data/create/create/potato_projectile/type/beetroot.json new file mode 100644 index 0000000000..973aa6bef4 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/beetroot.json @@ -0,0 +1,13 @@ +{ + "damage": 2, + "items": "minecraft:beetroot", + "knockback": 0.1, + "reload_ticks": 5, + "render_mode": { + "type": "create:toward_motion", + "spin": 2.0, + "sprite_angle_offset": 140 + }, + "sound_pitch": 1.6, + "velocity_multiplier": 1.6 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/blaze_cake.json b/src/generated/resources/data/create/create/potato_projectile/type/blaze_cake.json new file mode 100644 index 0000000000..825c465075 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/blaze_cake.json @@ -0,0 +1,15 @@ +{ + "damage": 15, + "items": "create:blaze_cake", + "knockback": 0.3, + "pre_entity_hit": { + "type": "create:set_on_fire", + "ticks": 240 + }, + "reload_ticks": 20, + "render_mode": { + "type": "create:tumble" + }, + "sticky": true, + "velocity_multiplier": 1.1 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/cake.json b/src/generated/resources/data/create/create/potato_projectile/type/cake.json new file mode 100644 index 0000000000..08abe4660c --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/cake.json @@ -0,0 +1,11 @@ +{ + "damage": 8, + "items": "minecraft:cake", + "knockback": 0.1, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "sticky": true, + "velocity_multiplier": 1.1 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/carrot.json b/src/generated/resources/data/create/create/potato_projectile/type/carrot.json new file mode 100644 index 0000000000..614582ce30 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/carrot.json @@ -0,0 +1,17 @@ +{ + "damage": 4, + "items": "minecraft:carrot", + "knockback": 0.3, + "on_entity_hit": { + "type": "create:plant_crop", + "block": "minecraft:carrots" + }, + "reload_ticks": 12, + "render_mode": { + "type": "create:toward_motion", + "spin": 1.0, + "sprite_angle_offset": 140 + }, + "sound_pitch": 1.5, + "velocity_multiplier": 1.45 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/chocolate_berry.json b/src/generated/resources/data/create/create/potato_projectile/type/chocolate_berry.json new file mode 100644 index 0000000000..028bed0e85 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/chocolate_berry.json @@ -0,0 +1,11 @@ +{ + "damage": 4, + "items": "create:chocolate_glazed_berries", + "knockback": 0.2, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.25, + "split": 3, + "velocity_multiplier": 1.05 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/chorus_fruit.json b/src/generated/resources/data/create/create/potato_projectile/type/chorus_fruit.json new file mode 100644 index 0000000000..bba651329a --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/chorus_fruit.json @@ -0,0 +1,14 @@ +{ + "damage": 3, + "items": "minecraft:chorus_fruit", + "knockback": 0.05, + "on_entity_hit": { + "type": "create:chorus_teleport", + "teleport_diameter": 20.0 + }, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "velocity_multiplier": 1.2 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/enchanted_golden_apple.json b/src/generated/resources/data/create/create/potato_projectile/type/enchanted_golden_apple.json new file mode 100644 index 0000000000..d3ad360bf2 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/enchanted_golden_apple.json @@ -0,0 +1,51 @@ +{ + "items": "minecraft:enchanted_golden_apple", + "knockback": 0.05, + "on_entity_hit": { + "type": "create:food_effects", + "food_property": { + "can_always_eat": true, + "effects": [ + { + "effect": { + "amplifier": 1, + "duration": 400, + "effect": "minecraft:regeneration" + }, + "probability": 1.0 + }, + { + "effect": { + "duration": 6000, + "effect": "minecraft:resistance" + }, + "probability": 1.0 + }, + { + "effect": { + "duration": 6000, + "effect": "minecraft:fire_resistance" + }, + "probability": 1.0 + }, + { + "effect": { + "amplifier": 3, + "duration": 2400, + "effect": "minecraft:absorption" + }, + "probability": 1.0 + } + ], + "nutrition": 4, + "saturation_modifier": 1.2 + }, + "recoverable": false + }, + "reload_ticks": 100, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.1, + "velocity_multiplier": 1.45 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/fallback.json b/src/generated/resources/data/create/create/potato_projectile/type/fallback.json new file mode 100644 index 0000000000..c67cae4213 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/fallback.json @@ -0,0 +1,4 @@ +{ + "damage": 0, + "items": [] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/fish.json b/src/generated/resources/data/create/create/potato_projectile/type/fish.json new file mode 100644 index 0000000000..6a4a526b88 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/fish.json @@ -0,0 +1,19 @@ +{ + "damage": 4, + "items": [ + "minecraft:cod", + "minecraft:cooked_cod", + "minecraft:salmon", + "minecraft:cooked_salmon", + "minecraft:tropical_fish" + ], + "knockback": 0.6, + "render_mode": { + "type": "create:toward_motion", + "spin": 1.0, + "sprite_angle_offset": 140 + }, + "sound_pitch": 1.3, + "sticky": true, + "velocity_multiplier": 1.3 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/glistering_melon.json b/src/generated/resources/data/create/create/potato_projectile/type/glistering_melon.json new file mode 100644 index 0000000000..67ca2a18fb --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/glistering_melon.json @@ -0,0 +1,18 @@ +{ + "damage": 5, + "items": "minecraft:glistering_melon_slice", + "knockback": 0.1, + "on_entity_hit": { + "type": "create:potion_effect", + "effect": "minecraft:glowing", + "level": 1, + "recoverable": true, + "ticks": 100 + }, + "reload_ticks": 8, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.5, + "velocity_multiplier": 1.45 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/glow_berry.json b/src/generated/resources/data/create/create/potato_projectile/type/glow_berry.json new file mode 100644 index 0000000000..dfacc954b6 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/glow_berry.json @@ -0,0 +1,18 @@ +{ + "damage": 2, + "items": "minecraft:glow_berries", + "knockback": 0.05, + "on_entity_hit": { + "type": "create:potion_effect", + "effect": "minecraft:glowing", + "level": 1, + "recoverable": false, + "ticks": 200 + }, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.2, + "split": 2, + "velocity_multiplier": 1.05 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/golden_apple.json b/src/generated/resources/data/create/create/potato_projectile/type/golden_apple.json new file mode 100644 index 0000000000..d63dcc81aa --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/golden_apple.json @@ -0,0 +1,13 @@ +{ + "items": "minecraft:golden_apple", + "knockback": 0.05, + "on_entity_hit": { + "type": "create:cure_zombie_villager" + }, + "reload_ticks": 100, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.1, + "velocity_multiplier": 1.45 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/golden_carrot.json b/src/generated/resources/data/create/create/potato_projectile/type/golden_carrot.json new file mode 100644 index 0000000000..2c5f25450b --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/golden_carrot.json @@ -0,0 +1,13 @@ +{ + "damage": 12, + "items": "minecraft:golden_carrot", + "knockback": 0.5, + "reload_ticks": 15, + "render_mode": { + "type": "create:toward_motion", + "spin": 2.0, + "sprite_angle_offset": 140 + }, + "sound_pitch": 1.5, + "velocity_multiplier": 1.45 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/honeyed_apple.json b/src/generated/resources/data/create/create/potato_projectile/type/honeyed_apple.json new file mode 100644 index 0000000000..0e658c2187 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/honeyed_apple.json @@ -0,0 +1,18 @@ +{ + "damage": 6, + "items": "create:honeyed_apple", + "knockback": 0.1, + "on_entity_hit": { + "type": "create:potion_effect", + "effect": "minecraft:slowness", + "level": 2, + "recoverable": true, + "ticks": 160 + }, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.1, + "velocity_multiplier": 1.35 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/melon_block.json b/src/generated/resources/data/create/create/potato_projectile/type/melon_block.json new file mode 100644 index 0000000000..a6c23c53b3 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/melon_block.json @@ -0,0 +1,15 @@ +{ + "damage": 8, + "items": "minecraft:melon", + "knockback": 2.0, + "on_entity_hit": { + "type": "create:place_block_on_ground", + "block": "minecraft:melon" + }, + "reload_ticks": 20, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 0.9, + "velocity_multiplier": 0.95 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/melon_slice.json b/src/generated/resources/data/create/create/potato_projectile/type/melon_slice.json new file mode 100644 index 0000000000..42caeeac39 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/melon_slice.json @@ -0,0 +1,11 @@ +{ + "damage": 3, + "items": "minecraft:melon_slice", + "knockback": 0.1, + "reload_ticks": 8, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.5, + "velocity_multiplier": 1.45 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/poison_potato.json b/src/generated/resources/data/create/create/potato_projectile/type/poison_potato.json new file mode 100644 index 0000000000..d34bfb8c06 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/poison_potato.json @@ -0,0 +1,17 @@ +{ + "damage": 5, + "items": "minecraft:poisonous_potato", + "knockback": 0.05, + "on_entity_hit": { + "type": "create:potion_effect", + "effect": "minecraft:poison", + "level": 1, + "recoverable": true, + "ticks": 160 + }, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "velocity_multiplier": 1.25 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/potato.json b/src/generated/resources/data/create/create/potato_projectile/type/potato.json new file mode 100644 index 0000000000..8b91116f61 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/potato.json @@ -0,0 +1,14 @@ +{ + "damage": 5, + "items": "minecraft:potato", + "knockback": 1.5, + "on_entity_hit": { + "type": "create:plant_crop", + "block": "minecraft:potatoes" + }, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "velocity_multiplier": 1.25 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/pufferfish.json b/src/generated/resources/data/create/create/potato_projectile/type/pufferfish.json new file mode 100644 index 0000000000..649c29daf9 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/pufferfish.json @@ -0,0 +1,46 @@ +{ + "damage": 4, + "items": "minecraft:pufferfish", + "knockback": 0.4, + "on_entity_hit": { + "type": "create:food_effects", + "food_property": { + "effects": [ + { + "effect": { + "amplifier": 1, + "duration": 1200, + "effect": "minecraft:poison" + }, + "probability": 1.0 + }, + { + "effect": { + "amplifier": 2, + "duration": 300, + "effect": "minecraft:hunger" + }, + "probability": 1.0 + }, + { + "effect": { + "duration": 300, + "effect": "minecraft:nausea" + }, + "probability": 1.0 + } + ], + "nutrition": 1, + "saturation_modifier": 0.1 + }, + "recoverable": false + }, + "render_mode": { + "type": "create:toward_motion", + "spin": 1.0, + "sprite_angle_offset": 140 + }, + "sound_pitch": 1.1, + "sticky": true, + "velocity_multiplier": 1.1 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/pumpkin_block.json b/src/generated/resources/data/create/create/potato_projectile/type/pumpkin_block.json new file mode 100644 index 0000000000..8f2e8a207f --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/pumpkin_block.json @@ -0,0 +1,15 @@ +{ + "damage": 6, + "items": "minecraft:pumpkin", + "knockback": 2.0, + "on_entity_hit": { + "type": "create:place_block_on_ground", + "block": "minecraft:pumpkin" + }, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 0.9, + "velocity_multiplier": 0.95 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/pumpkin_pie.json b/src/generated/resources/data/create/create/potato_projectile/type/pumpkin_pie.json new file mode 100644 index 0000000000..9a92807e7a --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/pumpkin_pie.json @@ -0,0 +1,12 @@ +{ + "damage": 7, + "items": "minecraft:pumpkin_pie", + "knockback": 0.05, + "reload_ticks": 15, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.1, + "sticky": true, + "velocity_multiplier": 1.1 +} \ No newline at end of file diff --git a/src/generated/resources/data/create/create/potato_projectile/type/suspicious_stew.json b/src/generated/resources/data/create/create/potato_projectile/type/suspicious_stew.json new file mode 100644 index 0000000000..51086d1ea4 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/suspicious_stew.json @@ -0,0 +1,19 @@ +{ + "damage": 3, + "drop_stack": { + "Count": 1, + "id": "minecraft:bowl" + }, + "items": "minecraft:suspicious_stew", + "knockback": 0.2, + "on_entity_hit": { + "type": "create:suspicious_stew" + }, + "reload_ticks": 40, + "render_mode": { + "type": "create:toward_motion", + "spin": 1.0, + "sprite_angle_offset": 140 + }, + "velocity_multiplier": 0.8 +} diff --git a/src/generated/resources/data/create/create/potato_projectile/type/sweet_berry.json b/src/generated/resources/data/create/create/potato_projectile/type/sweet_berry.json new file mode 100644 index 0000000000..d065288124 --- /dev/null +++ b/src/generated/resources/data/create/create/potato_projectile/type/sweet_berry.json @@ -0,0 +1,11 @@ +{ + "damage": 3, + "items": "minecraft:sweet_berries", + "knockback": 0.1, + "render_mode": { + "type": "create:tumble" + }, + "sound_pitch": 1.25, + "split": 3, + "velocity_multiplier": 1.05 +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/AllPackets.java b/src/main/java/com/simibubi/create/AllPackets.java index 04a05d3100..5dc3a044e0 100644 --- a/src/main/java/com/simibubi/create/AllPackets.java +++ b/src/main/java/com/simibubi/create/AllPackets.java @@ -40,7 +40,6 @@ import com.simibubi.create.content.equipment.blueprint.BlueprintAssignCompleteRe import com.simibubi.create.content.equipment.clipboard.ClipboardEditPacket; import com.simibubi.create.content.equipment.extendoGrip.ExtendoGripInteractionPacket; import com.simibubi.create.content.equipment.potatoCannon.PotatoCannonPacket; -import com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileTypeManager; import com.simibubi.create.content.equipment.symmetryWand.ConfigureSymmetryWandPacket; import com.simibubi.create.content.equipment.symmetryWand.SymmetryEffectPacket; import com.simibubi.create.content.equipment.tool.KnockbackPacket; @@ -190,7 +189,7 @@ public enum AllPackets { CONTRAPTION_COLLIDER_LOCK_REQUEST(ContraptionColliderLockPacketRequest.class, ContraptionColliderLockPacketRequest::new, PLAY_TO_SERVER), RADIAL_WRENCH_MENU_SUBMIT(RadialWrenchMenuSubmitPacket.class, RadialWrenchMenuSubmitPacket::new, - PLAY_TO_SERVER), + PLAY_TO_SERVER), LOGISTICS_STOCK_REQUEST(LogisticalStockRequestPacket.class, LogisticalStockRequestPacket::new, PLAY_TO_SERVER), LOGISTICS_PACKAGE_REQUEST(PackageOrderRequestPacket.class, PackageOrderRequestPacket::new, PLAY_TO_SERVER), CHAIN_CONVEYOR_CONNECT(ChainConveyorConnectionPacket.class, ChainConveyorConnectionPacket::new, PLAY_TO_SERVER), @@ -227,8 +226,6 @@ public enum AllPackets { SOUL_PULSE(SoulPulseEffectPacket.class, SoulPulseEffectPacket::new, PLAY_TO_CLIENT), PERSISTENT_DATA(ISyncPersistentData.PersistentDataPacket.class, ISyncPersistentData.PersistentDataPacket::new, PLAY_TO_CLIENT), - SYNC_POTATO_PROJECTILE_TYPES(PotatoProjectileTypeManager.SyncPacket.class, - PotatoProjectileTypeManager.SyncPacket::new, PLAY_TO_CLIENT), SYNC_RAIL_GRAPH(TrackGraphSyncPacket.class, TrackGraphSyncPacket::new, PLAY_TO_CLIENT), SYNC_EDGE_GROUP(SignalEdgeGroupPacket.class, SignalEdgeGroupPacket::new, PLAY_TO_CLIENT), SYNC_TRAIN(TrainPacket.class, TrainPacket::new, PLAY_TO_CLIENT), @@ -280,7 +277,7 @@ public enum AllPackets { private PacketType packetType; AllPackets(Class type, Function factory, - NetworkDirection direction) { + NetworkDirection direction) { packetType = new PacketType<>(type, factory, direction); } diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java index ab13776bab..8e7244882f 100644 --- a/src/main/java/com/simibubi/create/Create.java +++ b/src/main/java/com/simibubi/create/Create.java @@ -12,7 +12,9 @@ import com.simibubi.create.compat.Mods; import com.simibubi.create.compat.computercraft.ComputerCraftProxy; import com.simibubi.create.compat.curios.Curios; import com.simibubi.create.content.decoration.palettes.AllPaletteBlocks; -import com.simibubi.create.content.equipment.potatoCannon.BuiltinPotatoProjectileTypes; +import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileBlockHitActions; +import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions; +import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileRenderModes; import com.simibubi.create.content.fluids.tank.BoilerHeaters; import com.simibubi.create.content.kinetics.TorquePropagator; import com.simibubi.create.content.kinetics.fan.processing.AllFanProcessingTypes; @@ -56,6 +58,7 @@ import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.registries.RegisterEvent; @Mod(Create.ID) public class Create { @@ -68,7 +71,9 @@ public class Create { .disableHtmlEscaping() .create(); - /** Use the {@link Random} of a local {@link Level} or {@link Entity} or create one */ + /** + * Use the {@link Random} of a local {@link Level} or {@link Entity} or create one + */ @Deprecated public static final Random RANDOM = new Random(); @@ -82,7 +87,7 @@ public class Create { static { REGISTRATE.setTooltipModifierFactory(item -> new ItemDescription.Modifier(item, FontHelper.Palette.STANDARD_CREATE) - .andThen(TooltipModifier.mapNull(KineticStats.create(item))) + .andThen(TooltipModifier.mapNull(KineticStats.create(item))) ); } @@ -132,6 +137,7 @@ public class Create { AllConfigs.register(modLoadingContext); + // TODO - Make these use Registry.register and move them into the RegisterEvent AllArmInteractionPointTypes.register(modEventBus); AllFanProcessingTypes.register(modEventBus); AllItemAttributeTypes.register(modEventBus); @@ -148,6 +154,7 @@ public class Create { CopperRegistries.inject(); modEventBus.addListener(Create::init); + modEventBus.addListener(Create::onRegister); modEventBus.addListener(AllEntityTypes::registerEntityAttributes); modEventBus.addListener(EventPriority.LOWEST, CreateDatagen::gatherData); modEventBus.addListener(AllSoundEvents::register); @@ -166,7 +173,6 @@ public class Create { // TODO: custom registration should all happen in one place // Most registration happens in the constructor. // These registrations use Create's registered objects directly so they must run after registration has finished. - BuiltinPotatoProjectileTypes.register(); BoilerHeaters.registerDefaults(); AllPortalTracks.registerDefaults(); BlockSpoutingBehaviour.registerDefaults(); @@ -182,6 +188,12 @@ public class Create { }); } + public static void onRegister(final RegisterEvent event) { + AllPotatoProjectileRenderModes.init(); + AllPotatoProjectileEntityHitActions.init(); + AllPotatoProjectileBlockHitActions.init(); + } + public static LangBuilder lang() { return new LangBuilder(ID); } diff --git a/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoCannonProjectileType.java b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoCannonProjectileType.java new file mode 100644 index 0000000000..6e293bd68c --- /dev/null +++ b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoCannonProjectileType.java @@ -0,0 +1,230 @@ +package com.simibubi.create.api.equipment.potatoCannon; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.Type; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Billboard; +import com.simibubi.create.api.registry.CreateRegistries; + +import net.minecraft.core.Holder; +import net.minecraft.core.Holder.Reference; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryCodecs; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.worldgen.BootstapContext; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; + +public record PotatoCannonProjectileType(HolderSet items, int reloadTicks, int damage, int split, float knockback, + float drag, float velocityMultiplier, float gravityMultiplier, + float soundPitch, boolean sticky, ItemStack dropStack, + PotatoProjectileRenderMode renderMode, + Optional preEntityHit, + Optional onEntityHit, + Optional onBlockHit) { + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + RegistryCodecs.homogeneousList(Registries.ITEM).fieldOf("items").forGetter(PotatoCannonProjectileType::items), + Codec.INT.optionalFieldOf("reload_ticks", 10).forGetter(PotatoCannonProjectileType::reloadTicks), + Codec.INT.optionalFieldOf("damage", 1).forGetter(PotatoCannonProjectileType::damage), + Codec.INT.optionalFieldOf("split", 1).forGetter(PotatoCannonProjectileType::split), + Codec.FLOAT.optionalFieldOf("knockback", 1f).forGetter(PotatoCannonProjectileType::knockback), + Codec.FLOAT.optionalFieldOf("drag", .99f).forGetter(PotatoCannonProjectileType::drag), + Codec.FLOAT.optionalFieldOf("velocity_multiplier", 1f).forGetter(PotatoCannonProjectileType::velocityMultiplier), + Codec.FLOAT.optionalFieldOf("gravity_multiplier", 1f).forGetter(PotatoCannonProjectileType::gravityMultiplier), + Codec.FLOAT.optionalFieldOf("sound_pitch", 1f).forGetter(PotatoCannonProjectileType::soundPitch), + Codec.BOOL.optionalFieldOf("sticky", false).forGetter(PotatoCannonProjectileType::sticky), + ItemStack.CODEC.optionalFieldOf("drop_stack", ItemStack.EMPTY).forGetter(PotatoCannonProjectileType::dropStack), + PotatoProjectileRenderMode.CODEC.optionalFieldOf("render_mode", Billboard.INSTANCE).forGetter(PotatoCannonProjectileType::renderMode), + PotatoProjectileEntityHitAction.CODEC.optionalFieldOf("pre_entity_hit").forGetter(p -> p.preEntityHit), + PotatoProjectileEntityHitAction.CODEC.optionalFieldOf("on_entity_hit").forGetter(p -> p.onEntityHit), + PotatoProjectileBlockHitAction.CODEC.optionalFieldOf("on_entity_hit").forGetter(p -> p.onBlockHit) + ).apply(i, PotatoCannonProjectileType::new)); + + @Nullable + public static PotatoCannonProjectileType getTypeForItem(Level level, Item item) { + // Cache this if it causes performance issues, but it probably won't + List types = level.registryAccess() + .lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE) + .listElements() + .map(Reference::value) + .toList(); + + for (PotatoCannonProjectileType type : types) + if (type.items.contains(item.builtInRegistryHolder())) + return type; + + return null; + } + + public static Optional getTypeForStack(Level level, ItemStack item) { + if (item.isEmpty()) + return Optional.empty(); + return Optional.ofNullable(getTypeForItem(level, item.getItem())); + } + + public boolean preEntityHit(ItemStack stack, EntityHitResult ray) { + return preEntityHit.map(i -> i.execute(stack, ray, Type.PRE_HIT)).orElse(false); + } + + public boolean onEntityHit(ItemStack stack, EntityHitResult ray) { + return onEntityHit.map(i -> i.execute(stack, ray, Type.ON_HIT)).orElse(false); + } + + public boolean onBlockHit(LevelAccessor level, ItemStack stack, BlockHitResult ray) { + return onBlockHit.map(i -> i.execute(level, stack, ray)).orElse(false); + } + + public static class Builder { + private ResourceLocation id; + + private final List> items = new ArrayList<>(); + private int reloadTicks = 10; + private int damage = 1; + private int split = 1; + private float knockback = 1f; + private float drag = 0.99f; + private float velocityMultiplier = 1f; + private float gravityMultiplier = 1f; + private float soundPitch = 1f; + private boolean sticky = false; + private ItemStack dropStack = ItemStack.EMPTY; + private PotatoProjectileRenderMode renderMode = Billboard.INSTANCE; + private PotatoProjectileEntityHitAction preEntityHit = null; + private PotatoProjectileEntityHitAction onEntityHit = null; + private PotatoProjectileBlockHitAction onBlockHit = null; + + public Builder(ResourceLocation id) { + this.id = id; + } + + public Builder reloadTicks(int reload) { + this.reloadTicks = reload; + return this; + } + + public Builder damage(int damage) { + this.damage = damage; + return this; + } + + public Builder splitInto(int split) { + this.split = split; + return this; + } + + public Builder knockback(float knockback) { + this.knockback = knockback; + return this; + } + + public Builder drag(float drag) { + this.drag = drag; + return this; + } + + public Builder velocity(float velocity) { + this.velocityMultiplier = velocity; + return this; + } + + public Builder gravity(float modifier) { + this.gravityMultiplier = modifier; + return this; + } + + public Builder soundPitch(float pitch) { + this.soundPitch = pitch; + return this; + } + + public Builder sticky() { + this.sticky = true; + return this; + } + + public Builder dropStack(ItemStack stack) { + this.dropStack = stack; + return this; + } + + public Builder renderMode(PotatoProjectileRenderMode renderMode) { + this.renderMode = renderMode; + return this; + } + + public Builder renderBillboard() { + renderMode(PotatoProjectileRenderMode.Billboard.INSTANCE); + return this; + } + + public Builder renderTumbling() { + renderMode(PotatoProjectileRenderMode.Tumble.INSTANCE); + return this; + } + + public Builder renderTowardMotion(int spriteAngle, float spin) { + renderMode(new PotatoProjectileRenderMode.TowardMotion(spriteAngle, spin)); + return this; + } + + public Builder preEntityHit(PotatoProjectileEntityHitAction entityHitAction) { + this.preEntityHit = entityHitAction; + return this; + } + + public Builder onEntityHit(PotatoProjectileEntityHitAction entityHitAction) { + this.onEntityHit = entityHitAction; + return this; + } + + public Builder onBlockHit(PotatoProjectileBlockHitAction blockHitAction) { + this.onBlockHit = blockHitAction; + return this; + } + + public Builder addItems(ItemLike... items) { + for (ItemLike provider : items) + this.items.add(provider.asItem().builtInRegistryHolder()); + return this; + } + + public void register(BootstapContext ctx) { + PotatoCannonProjectileType type = new PotatoCannonProjectileType( + HolderSet.direct(items), + reloadTicks, + damage, + split, + knockback, + drag, + velocityMultiplier, + gravityMultiplier, + soundPitch, + sticky, + dropStack, + renderMode, + Optional.ofNullable(preEntityHit), + Optional.ofNullable(onEntityHit), + Optional.ofNullable(onBlockHit) + ); + ctx.register(ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, id), type); + } + + public void registerAndAssign(BootstapContext ctx, ItemLike... items) { + addItems(items); + register(ctx); + } + } +} diff --git a/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileBlockHitAction.java b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileBlockHitAction.java new file mode 100644 index 0000000000..87a003a057 --- /dev/null +++ b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileBlockHitAction.java @@ -0,0 +1,120 @@ +package com.simibubi.create.api.equipment.potatoCannon; + +import java.util.function.Function; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.simibubi.create.api.registry.CreateBuiltInRegistries; +import com.simibubi.create.foundation.mixin.accessor.FallingBlockEntityAccessor; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.item.FallingBlockEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; + +import net.minecraftforge.common.IPlantable; +import net.minecraftforge.registries.ForgeRegistries; + +public interface PotatoProjectileBlockHitAction { + Codec CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION.byNameCodec() + .dispatch(PotatoProjectileBlockHitAction::codec, Function.identity()); + + boolean execute(LevelAccessor level, ItemStack projectile, BlockHitResult ray); + + Codec codec(); + + record PlantCrop(Holder cropBlock) implements PotatoProjectileBlockHitAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlantCrop::cropBlock) + ).apply(instance, PlantCrop::new)); + + public PlantCrop(Block cropBlock) { + this(ForgeRegistries.BLOCKS.getDelegateOrThrow(cropBlock)); + } + + @Override + public boolean execute(LevelAccessor level, ItemStack projectile, BlockHitResult ray) { + if (level.isClientSide()) + return true; + + BlockPos hitPos = ray.getBlockPos(); + if (level instanceof Level l && !l.isLoaded(hitPos)) + return true; + Direction face = ray.getDirection(); + if (face != Direction.UP) + return false; + BlockPos placePos = hitPos.relative(face); + if (!level.getBlockState(placePos) + .canBeReplaced()) + return false; + if (!(cropBlock.get() instanceof IPlantable)) + return false; + BlockState blockState = level.getBlockState(hitPos); + if (!blockState.canSustainPlant(level, hitPos, face, (IPlantable) cropBlock.get())) + return false; + level.setBlock(placePos, cropBlock.get() + .defaultBlockState(), 3); + return true; + } + + @Override + public Codec codec() { + return CODEC; + } + } + + record PlaceBlockOnGround(Holder block) implements PotatoProjectileBlockHitAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlaceBlockOnGround::block) + ).apply(instance, PlaceBlockOnGround::new)); + + public PlaceBlockOnGround(Block block) { + this(ForgeRegistries.BLOCKS.getDelegateOrThrow(block)); + } + + @Override + public boolean execute(LevelAccessor levelAccessor, ItemStack projectile, BlockHitResult ray) { + if (levelAccessor.isClientSide()) + return true; + + BlockPos hitPos = ray.getBlockPos(); + if (levelAccessor instanceof Level l && !l.isLoaded(hitPos)) + return true; + Direction face = ray.getDirection(); + BlockPos placePos = hitPos.relative(face); + if (!levelAccessor.getBlockState(placePos) + .canBeReplaced()) + return false; + + if (face == Direction.UP) { + levelAccessor.setBlock(placePos, block.value() + .defaultBlockState(), 3); + } else if (levelAccessor instanceof Level level) { + double y = ray.getLocation().y - 0.5; + if (!level.isEmptyBlock(placePos.above())) + y = Math.min(y, placePos.getY()); + if (!level.isEmptyBlock(placePos.below())) + y = Math.max(y, placePos.getY()); + + FallingBlockEntity falling = FallingBlockEntityAccessor.create$callInit(level, placePos.getX() + 0.5, y, + placePos.getZ() + 0.5, block.get().defaultBlockState()); + falling.time = 1; + level.addFreshEntity(falling); + } + + return true; + } + + @Override + public Codec codec() { + return CODEC; + } + } +} diff --git a/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileEntityHitAction.java b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileEntityHitAction.java new file mode 100644 index 0000000000..b174de8a08 --- /dev/null +++ b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileEntityHitAction.java @@ -0,0 +1,245 @@ +package com.simibubi.create.api.equipment.potatoCannon; + +import java.util.UUID; +import java.util.function.Function; + +import com.mojang.authlib.GameProfile; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.simibubi.create.api.registry.CreateBuiltInRegistries; +import com.simibubi.create.foundation.mixin.accessor.SuspiciousStewItemAccessor; +import com.simibubi.create.foundation.utility.CreateCodecs; + +import net.createmod.catnip.data.WorldAttached; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.ExtraCodecs; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.Fox; +import net.minecraft.world.entity.monster.ZombieVillager; +import net.minecraft.world.food.FoodProperties; +import net.minecraft.world.food.Foods; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.Vec3; + +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.entity.EntityTeleportEvent; + +public interface PotatoProjectileEntityHitAction { + Codec CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION.byNameCodec() + .dispatch(PotatoProjectileEntityHitAction::codec, Function.identity()); + + enum Type { + PRE_HIT, + ON_HIT + } + + /** + * @return true if the hit should be canceled if the type is {@link Type#PRE_HIT PRE_HIT}, + * true if this shouldn't recover the projectile if the type is {@link Type#ON_HIT ON_HIT} + */ + boolean execute(ItemStack projectile, EntityHitResult ray, Type type); + + Codec codec(); + + record SetOnFire(int ticks) implements PotatoProjectileEntityHitAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(SetOnFire::ticks) + ).apply(instance, SetOnFire::new)); + + public static SetOnFire seconds(int seconds) { + return new SetOnFire(seconds * 20); + } + + @Override + public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) { + ray.getEntity() + .setRemainingFireTicks(ticks); + return false; + } + + @Override + public Codec codec() { + return CODEC; + } + } + + record PotionEffect(MobEffect effect, int level, int ticks, + boolean recoverable) implements PotatoProjectileEntityHitAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + BuiltInRegistries.MOB_EFFECT.byNameCodec().fieldOf("effect").forGetter(PotionEffect::effect), + ExtraCodecs.POSITIVE_INT.fieldOf("level").forGetter(PotionEffect::level), + ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(PotionEffect::ticks), + Codec.BOOL.fieldOf("recoverable").forGetter(PotionEffect::recoverable) + ).apply(instance, PotionEffect::new)); + + @Override + public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) { + Entity entity = ray.getEntity(); + if (entity.level().isClientSide) + return true; + if (entity instanceof LivingEntity) + applyEffect((LivingEntity) entity, new MobEffectInstance(effect, ticks, level - 1)); + return !recoverable; + } + + @Override + public Codec codec() { + return CODEC; + } + } + + record FoodEffects(FoodProperties foodProperty, boolean recoverable) implements PotatoProjectileEntityHitAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + CreateCodecs.FOOD_PROPERTIES.fieldOf("food_property").forGetter(FoodEffects::foodProperty), + Codec.BOOL.fieldOf("recoverable").forGetter(FoodEffects::recoverable) + ).apply(instance, FoodEffects::new)); + + @Override + public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) { + Entity entity = ray.getEntity(); + if (entity.level().isClientSide) + return true; + + if (entity instanceof LivingEntity livingEntity) { + for (Pair effect : foodProperty.getEffects()) { + if (livingEntity.getRandom().nextFloat() < effect.getSecond()) + applyEffect(livingEntity, new MobEffectInstance(effect.getFirst())); + } + } + return !recoverable; + } + + @Override + public Codec codec() { + return CODEC; + } + } + + record ChorusTeleport(double teleportDiameter) implements PotatoProjectileEntityHitAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + CreateCodecs.POSITIVE_DOUBLE.fieldOf("teleport_diameter").forGetter(ChorusTeleport::teleportDiameter) + ).apply(instance, ChorusTeleport::new)); + + @Override + public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) { + Entity entity = ray.getEntity(); + Level level = entity.getCommandSenderWorld(); + if (level.isClientSide) + return true; + if (!(entity instanceof LivingEntity livingEntity)) + return false; + + double entityX = livingEntity.getX(); + double entityY = livingEntity.getY(); + double entityZ = livingEntity.getZ(); + + for (int teleportTry = 0; teleportTry < 16; ++teleportTry) { + double teleportX = entityX + (livingEntity.getRandom() + .nextDouble() - 0.5D) * teleportDiameter; + double teleportY = Mth.clamp(entityY + (livingEntity.getRandom() + .nextInt((int) teleportDiameter) - (int) (teleportDiameter / 2)), 0.0D, level.getHeight() - 1); + double teleportZ = entityZ + (livingEntity.getRandom() + .nextDouble() - 0.5D) * teleportDiameter; + + EntityTeleportEvent.ChorusFruit event = + ForgeEventFactory.onChorusFruitTeleport(livingEntity, teleportX, teleportY, teleportZ); + if (event.isCanceled()) + return false; + if (livingEntity.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true)) { + if (livingEntity.isPassenger()) + livingEntity.stopRiding(); + + SoundEvent soundevent = + livingEntity instanceof Fox ? SoundEvents.FOX_TELEPORT : SoundEvents.CHORUS_FRUIT_TELEPORT; + level.playSound(null, entityX, entityY, entityZ, soundevent, SoundSource.PLAYERS, 1.0F, 1.0F); + livingEntity.playSound(soundevent, 1.0F, 1.0F); + livingEntity.setDeltaMovement(Vec3.ZERO); + return true; + } + } + + return false; + } + + @Override + public Codec codec() { + return CODEC; + } + } + + enum CureZombieVillager implements PotatoProjectileEntityHitAction { + INSTANCE; + + private static final FoodEffects EFFECT = new FoodEffects(Foods.GOLDEN_APPLE, false); + private static final GameProfile ZOMBIE_CONVERTER_NAME = + new GameProfile(UUID.fromString("be12d3dc-27d3-4992-8c97-66be53fd49c5"), "Converter"); + private static final WorldAttached ZOMBIE_CONVERTERS = + new WorldAttached<>(w -> new FakePlayer((ServerLevel) w, ZOMBIE_CONVERTER_NAME)); + + public static final Codec CODEC = Codec.unit(INSTANCE); + + @Override + public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) { + Entity entity = ray.getEntity(); + Level world = entity.level(); + + if (!(entity instanceof ZombieVillager zombieVillager) || !zombieVillager.hasEffect(MobEffects.WEAKNESS)) + return EFFECT.execute(projectile, ray, type); + if (world.isClientSide) + return false; + + FakePlayer dummy = ZOMBIE_CONVERTERS.get(world); + dummy.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.GOLDEN_APPLE, 1)); + zombieVillager.mobInteract(dummy, InteractionHand.MAIN_HAND); + return true; + } + + @Override + public Codec codec() { + return CODEC; + } + } + + enum SuspiciousStew implements PotatoProjectileEntityHitAction { + INSTANCE; + + public static final Codec CODEC = Codec.unit(INSTANCE); + + @Override + public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) { + if (ray.getEntity() instanceof LivingEntity livingEntity) + SuspiciousStewItemAccessor.create$listPotionEffects(projectile, livingEntity::addEffect); + + return true; + } + + @Override + public Codec codec() { + return CODEC; + } + } + + private static void applyEffect(LivingEntity entity, MobEffectInstance effect) { + if (effect.getEffect().isInstantenous()) { + effect.getEffect() + .applyInstantenousEffect(null, null, entity, effect.getDuration(), 1.0); + } else { + entity.addEffect(effect); + } + } +} diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileRenderMode.java b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileRenderMode.java similarity index 51% rename from src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileRenderMode.java rename to src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileRenderMode.java index cd3380d63b..19af90a785 100644 --- a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileRenderMode.java +++ b/src/main/java/com/simibubi/create/api/equipment/potatoCannon/PotatoProjectileRenderMode.java @@ -1,8 +1,14 @@ -package com.simibubi.create.content.equipment.potatoCannon; +package com.simibubi.create.api.equipment.potatoCannon; -import static com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileRenderMode.entityRandom; +import static com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.entityRandom; + +import java.util.function.Function; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.simibubi.create.api.registry.CreateBuiltInRegistries; +import com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileEntity; import dev.engine_room.flywheel.lib.transform.TransformStack; import net.createmod.catnip.math.AngleHelper; @@ -10,17 +16,23 @@ import net.minecraft.client.Minecraft; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.Vec3; + import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; public interface PotatoProjectileRenderMode { + Codec CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_RENDER_MODE.byNameCodec() + .dispatch(PotatoProjectileRenderMode::codec, Function.identity()); @OnlyIn(Dist.CLIENT) void transform(PoseStack ms, PotatoProjectileEntity entity, float pt); - public static class Billboard implements PotatoProjectileRenderMode { + Codec codec(); - public static final Billboard INSTANCE = new Billboard(); + enum Billboard implements PotatoProjectileRenderMode { + INSTANCE; + + public static final Codec CODEC = Codec.unit(INSTANCE); @Override @OnlyIn(Dist.CLIENT) @@ -37,32 +49,37 @@ public interface PotatoProjectileRenderMode { .rotateXDegrees(AngleHelper.deg(Mth.atan2(diff.y, Mth.sqrt((float) (diff.x * diff.x + diff.z * diff.z))))); } + @Override + public Codec codec() { + return CODEC; + } } - public static class Tumble extends Billboard { + enum Tumble implements PotatoProjectileRenderMode { + INSTANCE; - public static final Tumble INSTANCE = new Tumble(); + public static final Codec CODEC = Codec.unit(INSTANCE); @Override @OnlyIn(Dist.CLIENT) public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) { - super.transform(ms, entity, pt); + Billboard.INSTANCE.transform(ms, entity, pt); TransformStack.of(ms) .rotateZDegrees((entity.tickCount + pt) * 2 * entityRandom(entity, 16)) .rotateXDegrees((entity.tickCount + pt) * entityRandom(entity, 32)); } + @Override + public Codec codec() { + return CODEC; + } } - public static class TowardMotion implements PotatoProjectileRenderMode { - - private int spriteAngleOffset; - private float spin; - - public TowardMotion(int spriteAngleOffset, float spin) { - this.spriteAngleOffset = spriteAngleOffset; - this.spin = spin; - } + record TowardMotion(int spriteAngleOffset, float spin) implements PotatoProjectileRenderMode { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.INT.fieldOf("sprite_angle_offset").forGetter(i -> i.spriteAngleOffset), + Codec.FLOAT.fieldOf("spin").forGetter(i -> i.spin) + ).apply(instance, TowardMotion::new)); @Override @OnlyIn(Dist.CLIENT) @@ -77,15 +94,16 @@ public interface PotatoProjectileRenderMode { .rotateZDegrees(-spriteAngleOffset); } + @Override + public Codec codec() { + return CODEC; + } } - public static class StuckToEntity implements PotatoProjectileRenderMode { - - private Vec3 offset; - - public StuckToEntity(Vec3 offset) { - this.offset = offset; - } + record StuckToEntity(Vec3 offset) implements PotatoProjectileRenderMode { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Vec3.CODEC.fieldOf("offset").forGetter(i -> i.offset) + ).apply(instance, StuckToEntity::new)); @Override @OnlyIn(Dist.CLIENT) @@ -93,10 +111,13 @@ public interface PotatoProjectileRenderMode { TransformStack.of(ms).rotateYDegrees(AngleHelper.deg(Mth.atan2(offset.x, offset.z))); } + @Override + public Codec codec() { + return CODEC; + } } - public static int entityRandom(Entity entity, int maxValue) { + static int entityRandom(Entity entity, int maxValue) { return (System.identityHashCode(entity) * 31) % maxValue; } - } diff --git a/src/main/java/com/simibubi/create/api/registry/CreateBuiltInRegistries.java b/src/main/java/com/simibubi/create/api/registry/CreateBuiltInRegistries.java index 8e75692974..57039384c8 100644 --- a/src/main/java/com/simibubi/create/api/registry/CreateBuiltInRegistries.java +++ b/src/main/java/com/simibubi/create/api/registry/CreateBuiltInRegistries.java @@ -2,12 +2,16 @@ package com.simibubi.create.api.registry; import org.jetbrains.annotations.ApiStatus; +import com.mojang.serialization.Codec; import com.mojang.serialization.Lifecycle; import com.simibubi.create.api.behaviour.display.DisplaySource; import com.simibubi.create.api.behaviour.display.DisplayTarget; import com.simibubi.create.api.contraption.ContraptionType; import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType; import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode; import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType; import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType; import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttributeType; @@ -32,6 +36,9 @@ public class CreateBuiltInRegistries { public static final Registry> MOUNTED_ITEM_STORAGE_TYPE = withIntrusiveHolders(CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE); public static final Registry> MOUNTED_FLUID_STORAGE_TYPE = simple(CreateRegistries.MOUNTED_FLUID_STORAGE_TYPE); public static final Registry CONTRAPTION_TYPE = withIntrusiveHolders(CreateRegistries.CONTRAPTION_TYPE); + public static final Registry> POTATO_PROJECTILE_RENDER_MODE = simple(CreateRegistries.POTATO_PROJECTILE_RENDER_MODE); + public static final Registry> POTATO_PROJECTILE_ENTITY_HIT_ACTION = simple(CreateRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION); + public static final Registry> POTATO_PROJECTILE_BLOCK_HIT_ACTION = simple(CreateRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION); private static Registry simple(ResourceKey> key) { return register(key, new MappedRegistry<>(key, Lifecycle.stable(), false)); diff --git a/src/main/java/com/simibubi/create/api/registry/CreateRegistries.java b/src/main/java/com/simibubi/create/api/registry/CreateRegistries.java index ed09edcd34..981255389c 100644 --- a/src/main/java/com/simibubi/create/api/registry/CreateRegistries.java +++ b/src/main/java/com/simibubi/create/api/registry/CreateRegistries.java @@ -1,11 +1,18 @@ package com.simibubi.create.api.registry; +import org.jetbrains.annotations.ApiStatus.Internal; + +import com.mojang.serialization.Codec; import com.simibubi.create.Create; import com.simibubi.create.api.behaviour.display.DisplaySource; import com.simibubi.create.api.behaviour.display.DisplayTarget; import com.simibubi.create.api.contraption.ContraptionType; import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType; import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType; +import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode; import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType; import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType; import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttributeType; @@ -13,10 +20,17 @@ import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttribute import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; +import net.minecraftforge.registries.DataPackRegistryEvent; + /** * Keys for registries added by Create. + * * @see CreateBuiltInRegistries */ +@EventBusSubscriber(bus = Bus.MOD) public class CreateRegistries { public static final ResourceKey> ARM_INTERACTION_POINT_TYPE = key("arm_interaction_point_type"); public static final ResourceKey> FAN_PROCESSING_TYPE = key("fan_processing_type"); @@ -26,8 +40,22 @@ public class CreateRegistries { public static final ResourceKey>> MOUNTED_ITEM_STORAGE_TYPE = key("mounted_item_storage_type"); public static final ResourceKey>> MOUNTED_FLUID_STORAGE_TYPE = key("mounted_fluid_storage_type"); public static final ResourceKey> CONTRAPTION_TYPE = key("contraption_type"); + public static final ResourceKey> POTATO_PROJECTILE_TYPE = key("potato_projectile/type"); + public static final ResourceKey>> POTATO_PROJECTILE_RENDER_MODE = key("potato_projectile/render_mode"); + public static final ResourceKey>> POTATO_PROJECTILE_ENTITY_HIT_ACTION = key("potato_projectile/entity_hit_action"); + public static final ResourceKey>> POTATO_PROJECTILE_BLOCK_HIT_ACTION = key("potato_projectile/block_hit_action"); private static ResourceKey> key(String name) { return ResourceKey.createRegistryKey(Create.asResource(name)); } + + @Internal + @SubscribeEvent + public static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) { + event.dataPackRegistry( + POTATO_PROJECTILE_TYPE, + PotatoCannonProjectileType.CODEC, + PotatoCannonProjectileType.CODEC + ); + } } diff --git a/src/main/java/com/simibubi/create/content/equipment/extendoGrip/ExtendoGripItem.java b/src/main/java/com/simibubi/create/content/equipment/extendoGrip/ExtendoGripItem.java index 4873ba0976..39784615ac 100644 --- a/src/main/java/com/simibubi/create/content/equipment/extendoGrip/ExtendoGripItem.java +++ b/src/main/java/com/simibubi/create/content/equipment/extendoGrip/ExtendoGripItem.java @@ -245,6 +245,7 @@ public class ExtendoGripItem extends Item { if (lastActiveDamageSource == null) return; Entity entity = lastActiveDamageSource.getDirectEntity(); + lastActiveDamageSource = null; if (!(entity instanceof Player player)) return; if (!isHoldingExtendoGrip(player)) diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileBlockHitActions.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileBlockHitActions.java new file mode 100644 index 0000000000..2c0fc96006 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileBlockHitActions.java @@ -0,0 +1,21 @@ +package com.simibubi.create.content.equipment.potatoCannon; + +import com.mojang.serialization.Codec; +import com.simibubi.create.Create; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlaceBlockOnGround; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlantCrop; +import com.simibubi.create.api.registry.CreateBuiltInRegistries; + +import net.minecraft.core.Registry; + +public class AllPotatoProjectileBlockHitActions { + public static void init() { + register("plant_crop", PlantCrop.CODEC); + register("place_block_on_ground", PlaceBlockOnGround.CODEC); + } + + private static void register(String name, Codec codec) { + Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION, Create.asResource(name), codec); + } +} diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileEntityHitActions.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileEntityHitActions.java new file mode 100644 index 0000000000..90232dd90e --- /dev/null +++ b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileEntityHitActions.java @@ -0,0 +1,29 @@ +package com.simibubi.create.content.equipment.potatoCannon; + +import com.mojang.serialization.Codec; +import com.simibubi.create.Create; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.ChorusTeleport; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.CureZombieVillager; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.FoodEffects; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.PotionEffect; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SetOnFire; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SuspiciousStew; +import com.simibubi.create.api.registry.CreateBuiltInRegistries; + +import net.minecraft.core.Registry; + +public class AllPotatoProjectileEntityHitActions { + public static void init() { + register("set_on_fire", SetOnFire.CODEC); + register("potion_effect", PotionEffect.CODEC); + register("food_effects", FoodEffects.CODEC); + register("chorus_teleport", ChorusTeleport.CODEC); + register("cure_zombie_villager", CureZombieVillager.CODEC); + register("suspicious_stew", SuspiciousStew.CODEC); + } + + private static void register(String name, Codec codec) { + Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION, Create.asResource(name), codec); + } +} diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileRenderModes.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileRenderModes.java new file mode 100644 index 0000000000..331ea6f09f --- /dev/null +++ b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileRenderModes.java @@ -0,0 +1,25 @@ +package com.simibubi.create.content.equipment.potatoCannon; + +import com.mojang.serialization.Codec; +import com.simibubi.create.Create; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Billboard; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.StuckToEntity; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.TowardMotion; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Tumble; +import com.simibubi.create.api.registry.CreateBuiltInRegistries; + +import net.minecraft.core.Registry; + +public class AllPotatoProjectileRenderModes { + public static void init() { + register("billboard", Billboard.CODEC); + register("tumble", Tumble.CODEC); + register("toward_motion", TowardMotion.CODEC); + register("stuck_to_entity", StuckToEntity.CODEC); + } + + private static void register(String name, Codec codec) { + Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_RENDER_MODE, Create.asResource(name), codec); + } +} diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileTypes.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileTypes.java new file mode 100644 index 0000000000..38e1d1130f --- /dev/null +++ b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/AllPotatoProjectileTypes.java @@ -0,0 +1,266 @@ +package com.simibubi.create.content.equipment.potatoCannon; + +import com.simibubi.create.AllItems; +import com.simibubi.create.Create; +import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlaceBlockOnGround; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlantCrop; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.ChorusTeleport; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.CureZombieVillager; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.FoodEffects; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.PotionEffect; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SetOnFire; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SuspiciousStew; +import com.simibubi.create.api.registry.CreateRegistries; + +import net.minecraft.data.worldgen.BootstapContext; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.food.Foods; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Blocks; + +public class AllPotatoProjectileTypes { + public static ResourceKey FALLBACK = ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, Create.asResource("fallback")); + + public static void bootstrap(BootstapContext ctx) { + create("fallback") + .damage(0) + .register(ctx); + + create("potato") + .damage(5) + .reloadTicks(15) + .velocity(1.25f) + .knockback(1.5f) + .renderTumbling() + .onBlockHit(new PlantCrop(Blocks.POTATOES)) + .registerAndAssign(ctx, Items.POTATO); + + create("baked_potato") + .damage(5) + .reloadTicks(15) + .velocity(1.25f) + .knockback(0.5f) + .renderTumbling() + .preEntityHit(SetOnFire.seconds(3)) + .registerAndAssign(ctx, Items.BAKED_POTATO); + + create("carrot") + .damage(4) + .reloadTicks(12) + .velocity(1.45f) + .knockback(0.3f) + .renderTowardMotion(140, 1) + .soundPitch(1.5f) + .onBlockHit(new PlantCrop(Blocks.CARROTS)) + .registerAndAssign(ctx, Items.CARROT); + + create("golden_carrot") + .damage(12) + .reloadTicks(15) + .velocity(1.45f) + .knockback(0.5f) + .renderTowardMotion(140, 2) + .soundPitch(1.5f) + .registerAndAssign(ctx, Items.GOLDEN_CARROT); + + create("sweet_berry") + .damage(3) + .reloadTicks(10) + .knockback(0.1f) + .velocity(1.05f) + .renderTumbling() + .splitInto(3) + .soundPitch(1.25f) + .registerAndAssign(ctx, Items.SWEET_BERRIES); + + create("glow_berry") + .damage(2) + .reloadTicks(10) + .knockback(0.05f) + .velocity(1.05f) + .renderTumbling() + .splitInto(2) + .soundPitch(1.2f) + .onEntityHit(new PotionEffect(MobEffects.GLOWING, 1, 200, false)) + .registerAndAssign(ctx, Items.GLOW_BERRIES); + + create("chocolate_berry") + .damage(4) + .reloadTicks(10) + .knockback(0.2f) + .velocity(1.05f) + .renderTumbling() + .splitInto(3) + .soundPitch(1.25f) + .registerAndAssign(ctx, AllItems.CHOCOLATE_BERRIES.get()); + + create("poison_potato") + .damage(5) + .reloadTicks(15) + .knockback(0.05f) + .velocity(1.25f) + .renderTumbling() + .onEntityHit(new PotionEffect(MobEffects.POISON, 1, 160, true)) + .registerAndAssign(ctx, Items.POISONOUS_POTATO); + + create("chorus_fruit") + .damage(3) + .reloadTicks(15) + .velocity(1.20f) + .knockback(0.05f) + .renderTumbling() + .onEntityHit(new ChorusTeleport(20)) + .registerAndAssign(ctx, Items.CHORUS_FRUIT); + + create("apple") + .damage(5) + .reloadTicks(10) + .velocity(1.45f) + .knockback(0.5f) + .renderTumbling() + .soundPitch(1.1f) + .registerAndAssign(ctx, Items.APPLE); + + create("honeyed_apple") + .damage(6) + .reloadTicks(15) + .velocity(1.35f) + .knockback(0.1f) + .renderTumbling() + .soundPitch(1.1f) + .onEntityHit(new PotionEffect(MobEffects.MOVEMENT_SLOWDOWN, 2, 160, true)) + .registerAndAssign(ctx, AllItems.HONEYED_APPLE.get()); + + create("golden_apple") + .damage(1) + .reloadTicks(100) + .velocity(1.45f) + .knockback(0.05f) + .renderTumbling() + .soundPitch(1.1f) + .onEntityHit(CureZombieVillager.INSTANCE) + .registerAndAssign(ctx, Items.GOLDEN_APPLE); + + create("enchanted_golden_apple") + .damage(1) + .reloadTicks(100) + .velocity(1.45f) + .knockback(0.05f) + .renderTumbling() + .soundPitch(1.1f) + .onEntityHit(new FoodEffects(Foods.ENCHANTED_GOLDEN_APPLE, false)) + .registerAndAssign(ctx, Items.ENCHANTED_GOLDEN_APPLE); + + create("beetroot") + .damage(2) + .reloadTicks(5) + .velocity(1.6f) + .knockback(0.1f) + .renderTowardMotion(140, 2) + .soundPitch(1.6f) + .registerAndAssign(ctx, Items.BEETROOT); + + create("melon_slice") + .damage(3) + .reloadTicks(8) + .knockback(0.1f) + .velocity(1.45f) + .renderTumbling() + .soundPitch(1.5f) + .registerAndAssign(ctx, Items.MELON_SLICE); + + create("glistering_melon") + .damage(5) + .reloadTicks(8) + .knockback(0.1f) + .velocity(1.45f) + .renderTumbling() + .soundPitch(1.5f) + .onEntityHit(new PotionEffect(MobEffects.GLOWING, 1, 100, true)) + .registerAndAssign(ctx, Items.GLISTERING_MELON_SLICE); + + create("melon_block") + .damage(8) + .reloadTicks(20) + .knockback(2.0f) + .velocity(0.95f) + .renderTumbling() + .soundPitch(0.9f) + .onBlockHit(new PlaceBlockOnGround(Blocks.MELON)) + .registerAndAssign(ctx, Blocks.MELON); + + create("pumpkin_block") + .damage(6) + .reloadTicks(15) + .knockback(2.0f) + .velocity(0.95f) + .renderTumbling() + .soundPitch(0.9f) + .onBlockHit(new PlaceBlockOnGround(Blocks.PUMPKIN)) + .registerAndAssign(ctx, Blocks.PUMPKIN); + + create("pumpkin_pie") + .damage(7) + .reloadTicks(15) + .knockback(0.05f) + .velocity(1.1f) + .renderTumbling() + .sticky() + .soundPitch(1.1f) + .registerAndAssign(ctx, Items.PUMPKIN_PIE); + + create("cake") + .damage(8) + .reloadTicks(15) + .knockback(0.1f) + .velocity(1.1f) + .renderTumbling() + .sticky() + .registerAndAssign(ctx, Items.CAKE); + + create("blaze_cake") + .damage(15) + .reloadTicks(20) + .knockback(0.3f) + .velocity(1.1f) + .renderTumbling() + .sticky() + .preEntityHit(SetOnFire.seconds(12)) + .registerAndAssign(ctx, AllItems.BLAZE_CAKE.get()); + + create("fish") + .damage(4) + .knockback(0.6f) + .velocity(1.3f) + .renderTowardMotion(140, 1) + .sticky() + .soundPitch(1.3f) + .registerAndAssign(ctx, Items.COD, Items.COOKED_COD, Items.SALMON, Items.COOKED_SALMON, Items.TROPICAL_FISH); + + create("pufferfish") + .damage(4) + .knockback(0.4f) + .velocity(1.1f) + .renderTowardMotion(140, 1) + .sticky() + .onEntityHit(new FoodEffects(Foods.PUFFERFISH, false)) + .soundPitch(1.1f) + .registerAndAssign(ctx, Items.PUFFERFISH); + + create("suspicious_stew") + .damage(3) + .reloadTicks(40) + .knockback(0.2f) + .velocity(0.8f) + .renderTowardMotion(140, 1) + .dropStack(Items.BOWL.getDefaultInstance()) + .onEntityHit(SuspiciousStew.INSTANCE) + .registerAndAssign(ctx, Items.SUSPICIOUS_STEW); + } + + private static PotatoCannonProjectileType.Builder create(String name) { + return new PotatoCannonProjectileType.Builder(Create.asResource(name)); + } +} diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/BuiltinPotatoProjectileTypes.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/BuiltinPotatoProjectileTypes.java deleted file mode 100644 index eb5b87af7b..0000000000 --- a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/BuiltinPotatoProjectileTypes.java +++ /dev/null @@ -1,420 +0,0 @@ -package com.simibubi.create.content.equipment.potatoCannon; - -import java.util.UUID; -import java.util.function.BiPredicate; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import com.mojang.authlib.GameProfile; -import com.mojang.datafixers.util.Pair; -import com.simibubi.create.AllItems; -import com.simibubi.create.Create; -import com.simibubi.create.foundation.mixin.accessor.FallingBlockEntityAccessor; - -import net.createmod.catnip.data.WorldAttached; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.Mth; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.effect.MobEffect; -import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.effect.MobEffects; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.animal.Fox; -import net.minecraft.world.entity.item.FallingBlockEntity; -import net.minecraft.world.entity.monster.ZombieVillager; -import net.minecraft.world.food.FoodProperties; -import net.minecraft.world.food.Foods; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.EntityHitResult; -import net.minecraft.world.phys.Vec3; - -import net.minecraftforge.common.IPlantable; -import net.minecraftforge.common.util.FakePlayer; -import net.minecraftforge.event.ForgeEventFactory; -import net.minecraftforge.event.entity.EntityTeleportEvent; -import net.minecraftforge.registries.ForgeRegistries; - -public class BuiltinPotatoProjectileTypes { - - private static final GameProfile ZOMBIE_CONVERTER_NAME = - new GameProfile(UUID.fromString("be12d3dc-27d3-4992-8c97-66be53fd49c5"), "Converter"); - private static final WorldAttached ZOMBIE_CONVERTERS = - new WorldAttached<>(w -> new FakePlayer((ServerLevel) w, ZOMBIE_CONVERTER_NAME)); - - public static final PotatoCannonProjectileType - - FALLBACK = create("fallback").damage(0) - .register(), - - POTATO = create("potato").damage(5) - .reloadTicks(15) - .velocity(1.25f) - .knockback(1.5f) - .renderTumbling() - .onBlockHit(plantCrop(Blocks.POTATOES)) - .registerAndAssign(Items.POTATO), - - BAKED_POTATO = create("baked_potato").damage(5) - .reloadTicks(15) - .velocity(1.25f) - .knockback(0.5f) - .renderTumbling() - .preEntityHit(setFire(3)) - .registerAndAssign(Items.BAKED_POTATO), - - CARROT = create("carrot").damage(4) - .reloadTicks(12) - .velocity(1.45f) - .knockback(0.3f) - .renderTowardMotion(140, 1) - .soundPitch(1.5f) - .onBlockHit(plantCrop(Blocks.CARROTS)) - .registerAndAssign(Items.CARROT), - - GOLDEN_CARROT = create("golden_carrot").damage(12) - .reloadTicks(15) - .velocity(1.45f) - .knockback(0.5f) - .renderTowardMotion(140, 2) - .soundPitch(1.5f) - .registerAndAssign(Items.GOLDEN_CARROT), - - SWEET_BERRIES = create("sweet_berry").damage(3) - .reloadTicks(10) - .knockback(0.1f) - .velocity(1.05f) - .renderTumbling() - .splitInto(3) - .soundPitch(1.25f) - .registerAndAssign(Items.SWEET_BERRIES), - - GLOW_BERRIES = create("glow_berry").damage(2) - .reloadTicks(10) - .knockback(0.05f) - .velocity(1.05f) - .renderTumbling() - .splitInto(2) - .soundPitch(1.2f) - .onEntityHit(potion(MobEffects.GLOWING, 1, 200, false)) - .registerAndAssign(Items.GLOW_BERRIES), - - CHOCOLATE_BERRIES = create("chocolate_berry").damage(4) - .reloadTicks(10) - .knockback(0.2f) - .velocity(1.05f) - .renderTumbling() - .splitInto(3) - .soundPitch(1.25f) - .registerAndAssign(AllItems.CHOCOLATE_BERRIES.get()), - - POISON_POTATO = create("poison_potato").damage(5) - .reloadTicks(15) - .knockback(0.05f) - .velocity(1.25f) - .renderTumbling() - .onEntityHit(potion(MobEffects.POISON, 1, 160, true)) - .registerAndAssign(Items.POISONOUS_POTATO), - - CHORUS_FRUIT = create("chorus_fruit").damage(3) - .reloadTicks(15) - .velocity(1.20f) - .knockback(0.05f) - .renderTumbling() - .onEntityHit(chorusTeleport(20)) - .registerAndAssign(Items.CHORUS_FRUIT), - - APPLE = create("apple").damage(5) - .reloadTicks(10) - .velocity(1.45f) - .knockback(0.5f) - .renderTumbling() - .soundPitch(1.1f) - .registerAndAssign(Items.APPLE), - - HONEYED_APPLE = create("honeyed_apple").damage(6) - .reloadTicks(15) - .velocity(1.35f) - .knockback(0.1f) - .renderTumbling() - .soundPitch(1.1f) - .onEntityHit(potion(MobEffects.MOVEMENT_SLOWDOWN, 2, 160, true)) - .registerAndAssign(AllItems.HONEYED_APPLE.get()), - - GOLDEN_APPLE = create("golden_apple").damage(1) - .reloadTicks(100) - .velocity(1.45f) - .knockback(0.05f) - .renderTumbling() - .soundPitch(1.1f) - .onEntityHit(ray -> { - Entity entity = ray.getEntity(); - Level world = entity.level(); - - if (!(entity instanceof ZombieVillager) || !((ZombieVillager) entity).hasEffect(MobEffects.WEAKNESS)) - return foodEffects(Foods.GOLDEN_APPLE, false).test(ray); - if (world.isClientSide) - return false; - - FakePlayer dummy = ZOMBIE_CONVERTERS.get(world); - dummy.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.GOLDEN_APPLE, 1)); - ((ZombieVillager) entity).mobInteract(dummy, InteractionHand.MAIN_HAND); - return true; - }) - .registerAndAssign(Items.GOLDEN_APPLE), - - ENCHANTED_GOLDEN_APPLE = create("enchanted_golden_apple").damage(1) - .reloadTicks(100) - .velocity(1.45f) - .knockback(0.05f) - .renderTumbling() - .soundPitch(1.1f) - .onEntityHit(foodEffects(Foods.ENCHANTED_GOLDEN_APPLE, false)) - .registerAndAssign(Items.ENCHANTED_GOLDEN_APPLE), - - BEETROOT = create("beetroot").damage(2) - .reloadTicks(5) - .velocity(1.6f) - .knockback(0.1f) - .renderTowardMotion(140, 2) - .soundPitch(1.6f) - .registerAndAssign(Items.BEETROOT), - - MELON_SLICE = create("melon_slice").damage(3) - .reloadTicks(8) - .knockback(0.1f) - .velocity(1.45f) - .renderTumbling() - .soundPitch(1.5f) - .registerAndAssign(Items.MELON_SLICE), - - GLISTERING_MELON = create("glistering_melon").damage(5) - .reloadTicks(8) - .knockback(0.1f) - .velocity(1.45f) - .renderTumbling() - .soundPitch(1.5f) - .onEntityHit(potion(MobEffects.GLOWING, 1, 100, true)) - .registerAndAssign(Items.GLISTERING_MELON_SLICE), - - MELON_BLOCK = create("melon_block").damage(8) - .reloadTicks(20) - .knockback(2.0f) - .velocity(0.95f) - .renderTumbling() - .soundPitch(0.9f) - .onBlockHit(placeBlockOnGround(Blocks.MELON)) - .registerAndAssign(Blocks.MELON), - - PUMPKIN_BLOCK = create("pumpkin_block").damage(6) - .reloadTicks(15) - .knockback(2.0f) - .velocity(0.95f) - .renderTumbling() - .soundPitch(0.9f) - .onBlockHit(placeBlockOnGround(Blocks.PUMPKIN)) - .registerAndAssign(Blocks.PUMPKIN), - - PUMPKIN_PIE = create("pumpkin_pie").damage(7) - .reloadTicks(15) - .knockback(0.05f) - .velocity(1.1f) - .renderTumbling() - .sticky() - .soundPitch(1.1f) - .registerAndAssign(Items.PUMPKIN_PIE), - - CAKE = create("cake").damage(8) - .reloadTicks(15) - .knockback(0.1f) - .velocity(1.1f) - .renderTumbling() - .sticky() - .soundPitch(1.0f) - .registerAndAssign(Items.CAKE), - - BLAZE_CAKE = create("blaze_cake").damage(15) - .reloadTicks(20) - .knockback(0.3f) - .velocity(1.1f) - .renderTumbling() - .sticky() - .preEntityHit(setFire(12)) - .soundPitch(1.0f) - .registerAndAssign(AllItems.BLAZE_CAKE.get()); - - private static PotatoCannonProjectileType.Builder create(String name) { - return new PotatoCannonProjectileType.Builder(Create.asResource(name)); - } - - private static Predicate setFire(int seconds) { - return ray -> { - ray.getEntity() - .setSecondsOnFire(seconds); - return false; - }; - } - - private static Predicate potion(MobEffect effect, int level, int ticks, boolean recoverable) { - return ray -> { - Entity entity = ray.getEntity(); - if (entity.level().isClientSide) - return true; - if (entity instanceof LivingEntity) - applyEffect((LivingEntity) entity, new MobEffectInstance(effect, ticks, level - 1)); - return !recoverable; - }; - } - - private static Predicate foodEffects(FoodProperties food, boolean recoverable) { - return ray -> { - Entity entity = ray.getEntity(); - if (entity.level().isClientSide) - return true; - - if (entity instanceof LivingEntity) { - for (Pair effect : food.getEffects()) { - if (Create.RANDOM.nextFloat() < effect.getSecond()) - applyEffect((LivingEntity) entity, new MobEffectInstance(effect.getFirst())); - } - } - return !recoverable; - }; - } - - private static void applyEffect(LivingEntity entity, MobEffectInstance effect) { - if (effect.getEffect() - .isInstantenous()) - effect.getEffect() - .applyInstantenousEffect(null, null, entity, effect.getDuration(), 1.0); - else - entity.addEffect(effect); - } - - private static BiPredicate plantCrop(Supplier cropBlock) { - return (world, ray) -> { - if (world.isClientSide()) - return true; - - BlockPos hitPos = ray.getBlockPos(); - if (world instanceof Level l && !l.isLoaded(hitPos)) - return true; - Direction face = ray.getDirection(); - if (face != Direction.UP) - return false; - BlockPos placePos = hitPos.relative(face); - if (!world.getBlockState(placePos) - .canBeReplaced()) - return false; - if (!(cropBlock.get() instanceof IPlantable)) - return false; - BlockState blockState = world.getBlockState(hitPos); - if (!blockState.canSustainPlant(world, hitPos, face, (IPlantable) cropBlock.get())) - return false; - world.setBlock(placePos, cropBlock.get() - .defaultBlockState(), 3); - return true; - }; - } - - private static BiPredicate plantCrop(Block cropBlock) { - return plantCrop(ForgeRegistries.BLOCKS.getDelegateOrThrow(cropBlock)); - } - - private static BiPredicate placeBlockOnGround( - Supplier block) { - return (world, ray) -> { - if (world.isClientSide()) - return true; - - BlockPos hitPos = ray.getBlockPos(); - if (world instanceof Level l && !l.isLoaded(hitPos)) - return true; - Direction face = ray.getDirection(); - BlockPos placePos = hitPos.relative(face); - if (!world.getBlockState(placePos) - .canBeReplaced()) - return false; - - if (face == Direction.UP) { - world.setBlock(placePos, block.get() - .defaultBlockState(), 3); - } else if (world instanceof Level level) { - double y = ray.getLocation().y - 0.5; - if (!world.isEmptyBlock(placePos.above())) - y = Math.min(y, placePos.getY()); - if (!world.isEmptyBlock(placePos.below())) - y = Math.max(y, placePos.getY()); - - FallingBlockEntity falling = FallingBlockEntityAccessor.create$callInit(level, placePos.getX() + 0.5, y, - placePos.getZ() + 0.5, block.get().defaultBlockState()); - falling.time = 1; - world.addFreshEntity(falling); - } - - return true; - }; - } - - private static BiPredicate placeBlockOnGround(Block block) { - return placeBlockOnGround(ForgeRegistries.BLOCKS.getDelegateOrThrow(block)); - } - - private static Predicate chorusTeleport(double teleportDiameter) { - return ray -> { - Entity entity = ray.getEntity(); - Level world = entity.getCommandSenderWorld(); - if (world.isClientSide) - return true; - if (!(entity instanceof LivingEntity livingEntity)) - return false; - - double entityX = livingEntity.getX(); - double entityY = livingEntity.getY(); - double entityZ = livingEntity.getZ(); - - for (int teleportTry = 0; teleportTry < 16; ++teleportTry) { - double teleportX = entityX + (livingEntity.getRandom() - .nextDouble() - 0.5D) * teleportDiameter; - double teleportY = Mth.clamp(entityY + (livingEntity.getRandom() - .nextInt((int) teleportDiameter) - (int) (teleportDiameter / 2)), 0.0D, world.getHeight() - 1); - double teleportZ = entityZ + (livingEntity.getRandom() - .nextDouble() - 0.5D) * teleportDiameter; - - EntityTeleportEvent.ChorusFruit event = - ForgeEventFactory.onChorusFruitTeleport(livingEntity, teleportX, teleportY, teleportZ); - if (event.isCanceled()) - return false; - if (livingEntity.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true)) { - if (livingEntity.isPassenger()) - livingEntity.stopRiding(); - - SoundEvent soundevent = - livingEntity instanceof Fox ? SoundEvents.FOX_TELEPORT : SoundEvents.CHORUS_FRUIT_TELEPORT; - world.playSound(null, entityX, entityY, entityZ, soundevent, SoundSource.PLAYERS, 1.0F, 1.0F); - livingEntity.playSound(soundevent, 1.0F, 1.0F); - livingEntity.setDeltaMovement(Vec3.ZERO); - return true; - } - } - - return false; - }; - } - - public static void register() { - } - -} diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItem.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItem.java index 23bfe5a864..185bc0d350 100644 --- a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItem.java +++ b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItem.java @@ -9,8 +9,9 @@ import org.jetbrains.annotations.Nullable; import com.simibubi.create.AllEnchantments; import com.simibubi.create.AllEntityTypes; -import com.simibubi.create.Create; import com.simibubi.create.CreateClient; +import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType; +import com.simibubi.create.api.registry.CreateRegistries; import com.simibubi.create.content.equipment.armor.BacktankUtil; import com.simibubi.create.content.equipment.zapper.ShootableGadgetItemMethods; import com.simibubi.create.foundation.item.CustomArmPoseItem; @@ -50,6 +51,7 @@ import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.extensions.common.IClientItemExtensions; +import net.minecraftforge.server.ServerLifecycleHooks; public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmPoseItem { @@ -109,14 +111,13 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP } @Override - public InteractionResultHolder use(Level world, Player player, InteractionHand hand) { + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); - return findAmmoInInventory(world, player, stack).map(itemStack -> { - + return findAmmoInInventory(level, player, stack).map(itemStack -> { if (ShootableGadgetItemMethods.shouldSwap(player, stack, hand, this::isCannon)) return InteractionResultHolder.fail(stack); - if (world.isClientSide) { + if (level.isClientSide) { CreateClient.POTATO_CANNON_RENDER_HANDLER.dontAnimateItem(hand); return InteractionResultHolder.success(stack); } @@ -128,28 +129,33 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP .subtract(player.position() .add(0, player.getEyeHeight(), 0)); - PotatoCannonProjectileType projectileType = PotatoProjectileTypeManager.getTypeForStack(itemStack) - .orElse(BuiltinPotatoProjectileTypes.FALLBACK); + PotatoCannonProjectileType projectileType = PotatoCannonProjectileType.getTypeForStack(level, itemStack) + .orElseGet(() -> + level.registryAccess() + .lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE) + .getOrThrow(AllPotatoProjectileTypes.FALLBACK) + .value() + ); Vec3 lookVec = player.getLookAngle(); Vec3 motion = lookVec.add(correction) .normalize() .scale(2) - .scale(projectileType.getVelocityMultiplier()); + .scale(projectileType.velocityMultiplier()); - float soundPitch = projectileType.getSoundPitch() + (Create.RANDOM.nextFloat() - .5f) / 4f; + float soundPitch = projectileType.soundPitch() + (level.getRandom().nextFloat() - .5f) / 4f; - boolean spray = projectileType.getSplit() > 1; - Vec3 sprayBase = VecHelper.rotate(new Vec3(0, 0.1, 0), 360 * Create.RANDOM.nextFloat(), Axis.Z); - float sprayChange = 360f / projectileType.getSplit(); + boolean spray = projectileType.split() > 1; + Vec3 sprayBase = VecHelper.rotate(new Vec3(0, 0.1, 0), 360 * level.getRandom().nextFloat(), Axis.Z); + float sprayChange = 360f / projectileType.split(); - for (int i = 0; i < projectileType.getSplit(); i++) { - PotatoProjectileEntity projectile = AllEntityTypes.POTATO_PROJECTILE.create(world); + for (int i = 0; i < projectileType.split(); i++) { + PotatoProjectileEntity projectile = AllEntityTypes.POTATO_PROJECTILE.create(level); projectile.setItem(itemStack); projectile.setEnchantmentEffectsFromCannon(stack); Vec3 splitMotion = motion; if (spray) { - float imperfection = 40 * (Create.RANDOM.nextFloat() - 0.5f); + float imperfection = 40 * (level.getRandom().nextFloat() - 0.5f); Vec3 sprayOffset = VecHelper.rotate(sprayBase, i * sprayChange + imperfection, Axis.Z); splitMotion = splitMotion.add(VecHelper.lookAt(sprayOffset, motion)); } @@ -160,7 +166,7 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP projectile.setPos(barrelPos.x, barrelPos.y, barrelPos.z); projectile.setDeltaMovement(splitMotion); projectile.setOwner(player); - world.addFreshEntity(projectile); + level.addFreshEntity(projectile); } if (!player.isCreative()) { @@ -173,8 +179,8 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP stack.hurtAndBreak(1, player, p -> p.broadcastBreakEvent(hand)); Integer cooldown = - findAmmoInInventory(world, player, stack).flatMap(PotatoProjectileTypeManager::getTypeForStack) - .map(PotatoCannonProjectileType::getReloadTicks) + findAmmoInInventory(level, player, stack).flatMap(i -> PotatoCannonProjectileType.getTypeForStack(level, i)) + .map(potatoCannonProjectileType -> potatoCannonProjectileType.reloadTicks()) .orElse(10); ShootableGadgetItemMethods.applyCooldown(player, stack, hand, this::isCannon, cooldown); @@ -190,9 +196,9 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP return slotChanged || newStack.getItem() != oldStack.getItem(); } - private Optional findAmmoInInventory(Level world, Player player, ItemStack held) { + private Optional findAmmoInInventory(Level level, Player player, ItemStack held) { ItemStack findAmmo = player.getProjectile(held); - return PotatoProjectileTypeManager.getTypeForStack(findAmmo) + return PotatoCannonProjectileType.getTypeForStack(level, findAmmo) .map($ -> findAmmo); } @@ -207,7 +213,7 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP if (player == null) return Optional.empty(); ItemStack findAmmo = player.getProjectile(cannon); - Optional found = PotatoProjectileTypeManager.getTypeForStack(findAmmo) + Optional found = PotatoCannonProjectileType.getTypeForStack(Minecraft.getInstance().level, findAmmo) .map($ -> findAmmo); found.ifPresent(stack -> CLIENT_CURRENT_AMMO = stack); return found; @@ -215,7 +221,7 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP @Override @OnlyIn(Dist.CLIENT) - public void appendHoverText(ItemStack stack, Level world, List tooltip, TooltipFlag flag) { + public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { int power = stack.getEnchantmentLevel(Enchantments.POWER_ARROWS); int punch = stack.getEnchantmentLevel(Enchantments.PUNCH_ARROWS); final float additionalDamageMult = 1 + power * .2f; @@ -229,17 +235,17 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP tooltip.add(CommonComponents.EMPTY); tooltip.add(Component.translatable(ammo.getDescriptionId()).append(Component.literal(":")) .withStyle(ChatFormatting.GRAY)); - PotatoCannonProjectileType type = PotatoProjectileTypeManager.getTypeForStack(ammo) + PotatoCannonProjectileType type = PotatoCannonProjectileType.getTypeForStack(Minecraft.getInstance().level, ammo) .get(); MutableComponent spacing = CommonComponents.space(); ChatFormatting green = ChatFormatting.GREEN; ChatFormatting darkGreen = ChatFormatting.DARK_GREEN; - float damageF = type.getDamage() * additionalDamageMult; + float damageF = type.damage() * additionalDamageMult; MutableComponent damage = Component.literal(damageF == Mth.floor(damageF) ? "" + Mth.floor(damageF) : "" + damageF); - MutableComponent reloadTicks = Component.literal("" + type.getReloadTicks()); + MutableComponent reloadTicks = Component.literal("" + type.reloadTicks()); MutableComponent knockback = - Component.literal("" + (type.getKnockback() + additionalKnockback)); + Component.literal("" + (type.knockback() + additionalKnockback)); damage = damage.withStyle(additionalDamageMult > 1 ? green : darkGreen); knockback = knockback.withStyle(additionalKnockback > 0 ? green : darkGreen); @@ -255,12 +261,13 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP .append(CreateLang.translateDirect(_knockback, knockback) .withStyle(darkGreen))); }); - super.appendHoverText(stack, world, tooltip, flag); + super.appendHoverText(stack, level, tooltip, flag); } @Override public Predicate getAllSupportedProjectiles() { - return stack -> PotatoProjectileTypeManager.getTypeForStack(stack) + Level level = ServerLifecycleHooks.getCurrentServer().getLevel(Level.OVERWORLD); + return stack -> PotatoCannonProjectileType.getTypeForStack(level, stack) .isPresent(); } diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItemRenderer.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItemRenderer.java index 4a7c89e2b4..831a3683b9 100644 --- a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItemRenderer.java +++ b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonItemRenderer.java @@ -2,6 +2,7 @@ package com.simibubi.create.content.equipment.potatoCannon; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; +import com.simibubi.create.AllItems; import com.simibubi.create.Create; import com.simibubi.create.CreateClient; import com.simibubi.create.foundation.item.render.CustomRenderedItemModel; @@ -27,7 +28,7 @@ public class PotatoCannonItemRenderer extends CustomRenderedItemModelRenderer { @Override protected void render(ItemStack stack, CustomRenderedItemModel model, PartialItemModelRenderer renderer, - ItemDisplayContext transformType, PoseStack ms, MultiBufferSource buffer, int light, int overlay) { + ItemDisplayContext transformType, PoseStack ms, MultiBufferSource buffer, int light, int overlay) { Minecraft mc = Minecraft.getInstance(); ItemRenderer itemRenderer = mc.getItemRenderer(); renderer.render(model.getOriginalModel(), light); @@ -54,18 +55,18 @@ public class PotatoCannonItemRenderer extends CustomRenderedItemModelRenderer { ms.popPose(); if (transformType == ItemDisplayContext.GUI) { - PotatoCannonItem.getAmmoforPreview(stack) - .ifPresent(ammo -> { - PoseStack localMs = new PoseStack(); - localMs.translate(-1 / 4f, -1 / 4f, 1); - localMs.scale(.5f, .5f, .5f); - TransformStack.of(localMs) - .rotateYDegrees(-34); - itemRenderer.renderStatic(ammo, ItemDisplayContext.GUI, light, OverlayTexture.NO_OVERLAY, localMs, - buffer, mc.level, 0); - }); + PotatoCannonItem.getAmmoforPreview(stack).ifPresent(ammo -> { + if (AllItems.POTATO_CANNON.is(ammo)) return; + + PoseStack localMs = new PoseStack(); + localMs.translate(-1 / 4f, -1 / 4f, 1); + localMs.scale(.5f, .5f, .5f); + TransformStack.of(localMs) + .rotateYDegrees(-34); + itemRenderer.renderStatic(ammo, ItemDisplayContext.GUI, light, OverlayTexture.NO_OVERLAY, localMs, + buffer, mc.level, 0); + }); } - } } diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonProjectileType.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonProjectileType.java deleted file mode 100644 index 4ac551cdf4..0000000000 --- a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoCannonProjectileType.java +++ /dev/null @@ -1,298 +0,0 @@ -package com.simibubi.create.content.equipment.potatoCannon; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.BiPredicate; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; - -import net.createmod.catnip.platform.CatnipServices; -import net.minecraft.ResourceLocationException; -import net.minecraft.core.Holder; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.ItemLike; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.EntityHitResult; -import net.minecraftforge.registries.ForgeRegistries; - -public class PotatoCannonProjectileType { - - private List> items = new ArrayList<>(); - - private int reloadTicks = 10; - private int damage = 1; - private int split = 1; - private float knockback = 1; - private float drag = 0.99f; - private float velocityMultiplier = 1; - private float gravityMultiplier = 1; - private float soundPitch = 1; - private boolean sticky = false; - private PotatoProjectileRenderMode renderMode = PotatoProjectileRenderMode.Billboard.INSTANCE; - - private Predicate preEntityHit = e -> false; // True if hit should be canceled - private Predicate onEntityHit = e -> false; // True if shouldn't recover projectile - private BiPredicate onBlockHit = (w, ray) -> false; - - protected PotatoCannonProjectileType() { - } - - public List> getItems() { - return items; - } - - public int getReloadTicks() { - return reloadTicks; - } - - public int getDamage() { - return damage; - } - - public int getSplit() { - return split; - } - - public float getKnockback() { - return knockback; - } - - public float getDrag() { - return drag; - } - - public float getVelocityMultiplier() { - return velocityMultiplier; - } - - public float getGravityMultiplier() { - return gravityMultiplier; - } - - public float getSoundPitch() { - return soundPitch; - } - - public boolean isSticky() { - return sticky; - } - - public PotatoProjectileRenderMode getRenderMode() { - return renderMode; - } - - public boolean preEntityHit(EntityHitResult ray) { - return preEntityHit.test(ray); - } - - public boolean onEntityHit(EntityHitResult ray) { - return onEntityHit.test(ray); - } - - public boolean onBlockHit(LevelAccessor world, BlockHitResult ray) { - return onBlockHit.test(world, ray); - } - - public static PotatoCannonProjectileType fromJson(JsonObject object) { - PotatoCannonProjectileType type = new PotatoCannonProjectileType(); - try { - JsonElement itemsElement = object.get("items"); - if (itemsElement != null && itemsElement.isJsonArray()) { - for (JsonElement element : itemsElement.getAsJsonArray()) { - if (element.isJsonPrimitive()) { - JsonPrimitive primitive = element.getAsJsonPrimitive(); - if (primitive.isString()) { - try { - Optional> reference = ForgeRegistries.ITEMS.getDelegate(new ResourceLocation(primitive.getAsString())); - if (reference.isPresent()) { - type.items.add(reference.get()); - } - } catch (ResourceLocationException e) { - // - } - } - } - } - } - - parseJsonPrimitive(object, "reload_ticks", JsonPrimitive::isNumber, primitive -> type.reloadTicks = primitive.getAsInt()); - parseJsonPrimitive(object, "damage", JsonPrimitive::isNumber, primitive -> type.damage = primitive.getAsInt()); - parseJsonPrimitive(object, "split", JsonPrimitive::isNumber, primitive -> type.split = primitive.getAsInt()); - parseJsonPrimitive(object, "knockback", JsonPrimitive::isNumber, primitive -> type.knockback = primitive.getAsFloat()); - parseJsonPrimitive(object, "drag", JsonPrimitive::isNumber, primitive -> type.drag = primitive.getAsFloat()); - parseJsonPrimitive(object, "velocity_multiplier", JsonPrimitive::isNumber, primitive -> type.velocityMultiplier = primitive.getAsFloat()); - parseJsonPrimitive(object, "gravity_multiplier", JsonPrimitive::isNumber, primitive -> type.gravityMultiplier = primitive.getAsFloat()); - parseJsonPrimitive(object, "sound_pitch", JsonPrimitive::isNumber, primitive -> type.soundPitch = primitive.getAsFloat()); - parseJsonPrimitive(object, "sticky", JsonPrimitive::isBoolean, primitive -> type.sticky = primitive.getAsBoolean()); - } catch (Exception e) { - // - } - return type; - } - - private static void parseJsonPrimitive(JsonObject object, String key, Predicate predicate, Consumer consumer) { - JsonElement element = object.get(key); - if (element != null && element.isJsonPrimitive()) { - JsonPrimitive primitive = element.getAsJsonPrimitive(); - if (predicate.test(primitive)) { - consumer.accept(primitive); - } - } - } - - public static void toBuffer(PotatoCannonProjectileType type, FriendlyByteBuf buffer) { - buffer.writeVarInt(type.items.size()); - for (Supplier delegate : type.items) { - buffer.writeResourceLocation(CatnipServices.REGISTRIES.getKeyOrThrow(delegate.get())); - } - buffer.writeInt(type.reloadTicks); - buffer.writeInt(type.damage); - buffer.writeInt(type.split); - buffer.writeFloat(type.knockback); - buffer.writeFloat(type.drag); - buffer.writeFloat(type.velocityMultiplier); - buffer.writeFloat(type.gravityMultiplier); - buffer.writeFloat(type.soundPitch); - buffer.writeBoolean(type.sticky); - } - - public static PotatoCannonProjectileType fromBuffer(FriendlyByteBuf buffer) { - PotatoCannonProjectileType type = new PotatoCannonProjectileType(); - int size = buffer.readVarInt(); - for (int i = 0; i < size; i++) { - Optional> reference = ForgeRegistries.ITEMS.getDelegate(buffer.readResourceLocation()); - if (reference.isPresent()) { - type.items.add(reference.get()); - } - } - type.reloadTicks = buffer.readInt(); - type.damage = buffer.readInt(); - type.split = buffer.readInt(); - type.knockback = buffer.readFloat(); - type.drag = buffer.readFloat(); - type.velocityMultiplier = buffer.readFloat(); - type.gravityMultiplier = buffer.readFloat(); - type.soundPitch = buffer.readFloat(); - type.sticky = buffer.readBoolean(); - return type; - } - - public static class Builder { - - protected ResourceLocation id; - protected PotatoCannonProjectileType result; - - public Builder(ResourceLocation id) { - this.id = id; - this.result = new PotatoCannonProjectileType(); - } - - public Builder reloadTicks(int reload) { - result.reloadTicks = reload; - return this; - } - - public Builder damage(int damage) { - result.damage = damage; - return this; - } - - public Builder splitInto(int split) { - result.split = split; - return this; - } - - public Builder knockback(float knockback) { - result.knockback = knockback; - return this; - } - - public Builder drag(float drag) { - result.drag = drag; - return this; - } - - public Builder velocity(float velocity) { - result.velocityMultiplier = velocity; - return this; - } - - public Builder gravity(float modifier) { - result.gravityMultiplier = modifier; - return this; - } - - public Builder soundPitch(float pitch) { - result.soundPitch = pitch; - return this; - } - - public Builder sticky() { - result.sticky = true; - return this; - } - - public Builder renderMode(PotatoProjectileRenderMode renderMode) { - result.renderMode = renderMode; - return this; - } - - public Builder renderBillboard() { - renderMode(PotatoProjectileRenderMode.Billboard.INSTANCE); - return this; - } - - public Builder renderTumbling() { - renderMode(PotatoProjectileRenderMode.Tumble.INSTANCE); - return this; - } - - public Builder renderTowardMotion(int spriteAngle, float spin) { - renderMode(new PotatoProjectileRenderMode.TowardMotion(spriteAngle, spin)); - return this; - } - - public Builder preEntityHit(Predicate callback) { - result.preEntityHit = callback; - return this; - } - - public Builder onEntityHit(Predicate callback) { - result.onEntityHit = callback; - return this; - } - - public Builder onBlockHit(BiPredicate callback) { - result.onBlockHit = callback; - return this; - } - - public Builder addItems(ItemLike... items) { - for (ItemLike provider : items) - result.items.add(ForgeRegistries.ITEMS.getDelegateOrThrow(provider.asItem())); - return this; - } - - public PotatoCannonProjectileType register() { - PotatoProjectileTypeManager.registerBuiltinType(id, result); - return result; - } - - public PotatoCannonProjectileType registerAndAssign(ItemLike... items) { - addItems(items); - register(); - return result; - } - - } - -} diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileEntity.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileEntity.java index 38e784ebfa..16b9cd8c1c 100644 --- a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileEntity.java +++ b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileEntity.java @@ -4,6 +4,9 @@ import org.jetbrains.annotations.NotNull; import com.simibubi.create.AllEnchantments; import com.simibubi.create.AllSoundEvents; +import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType; +import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode; +import com.simibubi.create.api.registry.CreateRegistries; import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.damageTypes.CreateDamageSources; import com.simibubi.create.foundation.particle.AirParticleData; @@ -68,8 +71,13 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements public PotatoCannonProjectileType getProjectileType() { if (type == null) - type = PotatoProjectileTypeManager.getTypeForStack(stack) - .orElse(BuiltinPotatoProjectileTypes.FALLBACK); + type = PotatoCannonProjectileType.getTypeForStack(level(), stack) + .orElseGet(() -> + level().registryAccess() + .lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE) + .getOrThrow(AllPotatoProjectileTypes.FALLBACK) + .value() + ); return type; } @@ -127,7 +135,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements if (getStuckEntity() != null) return stuckRenderer; - return getProjectileType().getRenderMode(); + return getProjectileType().renderMode(); } public void tick() { @@ -139,15 +147,15 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements pop(position()); kill(); } else { - stuckFallSpeed += 0.007 * projectileType.getGravityMultiplier(); + stuckFallSpeed += 0.007 * projectileType.gravityMultiplier(); stuckOffset = stuckOffset.add(0, -stuckFallSpeed, 0); Vec3 pos = stuckEntity.position() .add(stuckOffset); setPos(pos.x, pos.y, pos.z); } } else { - setDeltaMovement(getDeltaMovement().add(0, -0.05 * projectileType.getGravityMultiplier(), 0) - .scale(projectileType.getDrag())); + setDeltaMovement(getDeltaMovement().add(0, -0.05 * projectileType.gravityMultiplier(), 0) + .scale(projectileType.drag())); } super.tick(); @@ -178,8 +186,8 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements Vec3 hit = ray.getLocation(); Entity target = ray.getEntity(); PotatoCannonProjectileType projectileType = getProjectileType(); - float damage = projectileType.getDamage() * additionalDamageMult; - float knockback = projectileType.getKnockback() + additionalKnockback; + float damage = projectileType.damage() * additionalDamageMult; + float knockback = projectileType.knockback() + additionalKnockback; Entity owner = this.getOwner(); if (!target.isAlive()) @@ -202,7 +210,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements if (target instanceof WitherBoss && ((WitherBoss) target).isPowered()) return; - if (projectileType.preEntityHit(ray)) + if (projectileType.preEntityHit(stack, ray)) return; boolean targetIsEnderman = target.getType() == EntityType.ENDERMAN; @@ -220,9 +228,12 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements if (targetIsEnderman) return; - if (!projectileType.onEntityHit(ray) && onServer) - if (random.nextDouble() <= recoveryChance) + if (!projectileType.onEntityHit(stack, ray) && onServer) + if (random.nextDouble() <= recoveryChance) { recoverItem(); + } else { + spawnAtLocation(projectileType.dropStack()); + } if (!(target instanceof LivingEntity livingentity)) { playHitSound(level(), position()); @@ -230,8 +241,8 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements return; } - if (type.getReloadTicks() < 10) - livingentity.invulnerableTime = type.getReloadTicks() + 10; + if (type.reloadTicks() < 10) + livingentity.invulnerableTime = type.reloadTicks() + 10; if (onServer && knockback > 0) { Vec3 appliedMotion = this.getDeltaMovement() @@ -259,7 +270,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements AllAdvancements.POTATO_CANNON.awardTo(serverplayerentity); } - if (type.isSticky() && target.isAlive()) { + if (type.sticky() && target.isAlive()) { setStuckEntity(target); } else { kill(); @@ -284,7 +295,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements protected void onHitBlock(BlockHitResult ray) { Vec3 hit = ray.getLocation(); pop(hit); - if (!getProjectileType().onBlockHit(level(), ray) && !level().isClientSide) + if (!getProjectileType().onBlockHit(level(), stack, ray) && !level().isClientSide) if (random.nextDouble() <= recoveryChance) recoverItem(); super.onHitBlock(ray); diff --git a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileTypeManager.java b/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileTypeManager.java deleted file mode 100644 index 09d8674bc0..0000000000 --- a/src/main/java/com/simibubi/create/content/equipment/potatoCannon/PotatoProjectileTypeManager.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.simibubi.create.content.equipment.potatoCannon; - -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.Optional; -import java.util.function.Supplier; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.simibubi.create.AllItems; -import com.simibubi.create.AllPackets; -import com.simibubi.create.foundation.networking.SimplePacketBase; - -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.network.NetworkEvent.Context; -import net.minecraftforge.network.PacketDistributor; - -public class PotatoProjectileTypeManager { - - private static final Map BUILTIN_TYPE_MAP = new HashMap<>(); - private static final Map CUSTOM_TYPE_MAP = new HashMap<>(); - private static final Map ITEM_TO_TYPE_MAP = new IdentityHashMap<>(); - - public static void registerBuiltinType(ResourceLocation id, PotatoCannonProjectileType type) { - synchronized (BUILTIN_TYPE_MAP) { - BUILTIN_TYPE_MAP.put(id, type); - } - } - - public static PotatoCannonProjectileType getBuiltinType(ResourceLocation id) { - return BUILTIN_TYPE_MAP.get(id); - } - - public static PotatoCannonProjectileType getCustomType(ResourceLocation id) { - return CUSTOM_TYPE_MAP.get(id); - } - - public static PotatoCannonProjectileType getTypeForItem(Item item) { - return ITEM_TO_TYPE_MAP.get(item); - } - - public static Optional getTypeForStack(ItemStack item) { - if (item.isEmpty()) - return Optional.empty(); - return Optional.ofNullable(getTypeForItem(item.getItem())); - } - - public static void clear() { - CUSTOM_TYPE_MAP.clear(); - ITEM_TO_TYPE_MAP.clear(); - } - - public static void fillItemMap() { - for (Map.Entry entry : BUILTIN_TYPE_MAP.entrySet()) { - PotatoCannonProjectileType type = entry.getValue(); - for (Supplier delegate : type.getItems()) { - ITEM_TO_TYPE_MAP.put(delegate.get(), type); - } - } - for (Map.Entry entry : CUSTOM_TYPE_MAP.entrySet()) { - PotatoCannonProjectileType type = entry.getValue(); - for (Supplier delegate : type.getItems()) { - ITEM_TO_TYPE_MAP.put(delegate.get(), type); - } - } - ITEM_TO_TYPE_MAP.remove(AllItems.POTATO_CANNON.get()); - } - - public static void toBuffer(FriendlyByteBuf buffer) { - buffer.writeVarInt(CUSTOM_TYPE_MAP.size()); - for (Map.Entry entry : CUSTOM_TYPE_MAP.entrySet()) { - buffer.writeResourceLocation(entry.getKey()); - PotatoCannonProjectileType.toBuffer(entry.getValue(), buffer); - } - } - - public static void fromBuffer(FriendlyByteBuf buffer) { - clear(); - - int size = buffer.readVarInt(); - for (int i = 0; i < size; i++) { - CUSTOM_TYPE_MAP.put(buffer.readResourceLocation(), PotatoCannonProjectileType.fromBuffer(buffer)); - } - - fillItemMap(); - } - - public static void syncTo(ServerPlayer player) { - AllPackets.getChannel().send(PacketDistributor.PLAYER.with(() -> player), new SyncPacket()); - } - - public static void syncToAll() { - AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new SyncPacket()); - } - - public static class ReloadListener extends SimpleJsonResourceReloadListener { - - private static final Gson GSON = new Gson(); - - public static final ReloadListener INSTANCE = new ReloadListener(); - - protected ReloadListener() { - super(GSON, "potato_cannon_projectile_types"); - } - - @Override - protected void apply(Map map, ResourceManager resourceManager, ProfilerFiller profiler) { - clear(); - - for (Map.Entry entry : map.entrySet()) { - JsonElement element = entry.getValue(); - if (element.isJsonObject()) { - ResourceLocation id = entry.getKey(); - JsonObject object = element.getAsJsonObject(); - PotatoCannonProjectileType type = PotatoCannonProjectileType.fromJson(object); - CUSTOM_TYPE_MAP.put(id, type); - } - } - - fillItemMap(); - } - - } - - public static class SyncPacket extends SimplePacketBase { - - private FriendlyByteBuf buffer; - - public SyncPacket() { - } - - public SyncPacket(FriendlyByteBuf buffer) { - this.buffer = buffer; - } - - @Override - public void write(FriendlyByteBuf buffer) { - toBuffer(buffer); - } - - @Override - public boolean handle(Context context) { - context.enqueueWork(() -> { - fromBuffer(buffer); - }); - return true; - } - - } - -} diff --git a/src/main/java/com/simibubi/create/foundation/events/CommonEvents.java b/src/main/java/com/simibubi/create/foundation/events/CommonEvents.java index 788cf9c7ee..7f94e72ec1 100644 --- a/src/main/java/com/simibubi/create/foundation/events/CommonEvents.java +++ b/src/main/java/com/simibubi/create/foundation/events/CommonEvents.java @@ -6,7 +6,6 @@ import com.simibubi.create.content.contraptions.ContraptionHandler; import com.simibubi.create.content.contraptions.actors.trainControls.ControlsServerHandler; import com.simibubi.create.content.contraptions.minecart.CouplingPhysics; import com.simibubi.create.content.contraptions.minecart.capability.CapabilityMinecartController; -import com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileTypeManager; import com.simibubi.create.content.equipment.toolbox.ToolboxHandler; import com.simibubi.create.content.equipment.wrench.WrenchItem; import com.simibubi.create.content.equipment.zapper.ZapperInteractionHandler; @@ -39,7 +38,6 @@ import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.event.AddPackFindersEvent; import net.minecraftforge.event.AddReloadListenerEvent; import net.minecraftforge.event.AttachCapabilitiesEvent; -import net.minecraftforge.event.OnDatapackSyncEvent; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.TickEvent.LevelTickEvent; import net.minecraftforge.event.TickEvent.Phase; @@ -143,20 +141,9 @@ public class CommonEvents { @SubscribeEvent public static void addReloadListeners(AddReloadListenerEvent event) { event.addListener(RecipeFinder.LISTENER); - event.addListener(PotatoProjectileTypeManager.ReloadListener.INSTANCE); event.addListener(BeltHelper.LISTENER); } - @SubscribeEvent - public static void onDatapackSync(OnDatapackSyncEvent event) { - ServerPlayer player = event.getPlayer(); - if (player != null) { - PotatoProjectileTypeManager.syncTo(player); - } else { - PotatoProjectileTypeManager.syncToAll(); - } - } - @SubscribeEvent public static void serverStopping(ServerStoppingEvent event) { Create.SCHEMATIC_RECEIVER.shutdown(); diff --git a/src/main/java/com/simibubi/create/foundation/mixin/PlayerMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/PlayerMixin.java index 4eec0a0956..f22f6131c7 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/PlayerMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/PlayerMixin.java @@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.injection.At; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.simibubi.create.content.contraptions.AbstractContraptionEntity; +import com.simibubi.create.infrastructure.config.AllConfigs; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; @@ -26,7 +27,8 @@ public abstract class PlayerMixin extends LivingEntity { ) private boolean pretendNotPassenger(boolean isPassenger) { // avoid touching all items in the contraption's massive hitbox - if (isPassenger && this.getVehicle() instanceof AbstractContraptionEntity) { + boolean shouldSync = AllConfigs.server().kinetics.syncPlayerPickupHitboxWithContraptionHitbox.get(); + if (isPassenger && !shouldSync && this.getVehicle() instanceof AbstractContraptionEntity) { return false; } diff --git a/src/main/java/com/simibubi/create/foundation/mixin/accessor/MobEffectInstanceAccessor.java b/src/main/java/com/simibubi/create/foundation/mixin/accessor/MobEffectInstanceAccessor.java new file mode 100644 index 0000000000..da9d9a6ef6 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/mixin/accessor/MobEffectInstanceAccessor.java @@ -0,0 +1,12 @@ +package com.simibubi.create.foundation.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.world.effect.MobEffectInstance; + +@Mixin(MobEffectInstance.class) +public interface MobEffectInstanceAccessor { + @Accessor("hiddenEffect") + MobEffectInstance create$getHiddenEffect(); +} diff --git a/src/main/java/com/simibubi/create/foundation/mixin/accessor/SuspiciousStewItemAccessor.java b/src/main/java/com/simibubi/create/foundation/mixin/accessor/SuspiciousStewItemAccessor.java new file mode 100644 index 0000000000..9586e69ed9 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/mixin/accessor/SuspiciousStewItemAccessor.java @@ -0,0 +1,18 @@ +package com.simibubi.create.foundation.mixin.accessor; + +import java.util.function.Consumer; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.SuspiciousStewItem; + +@Mixin(SuspiciousStewItem.class) +public interface SuspiciousStewItemAccessor { + @Invoker("listPotionEffects") + static void create$listPotionEffects(ItemStack stack, Consumer output) { + throw new AssertionError(); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java b/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java index c912a8858e..dabca29a7d 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java +++ b/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java @@ -1,10 +1,30 @@ package com.simibubi.create.foundation.utility; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import com.google.common.base.Suppliers; +import com.google.common.primitives.UnsignedBytes; +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; +import com.mojang.serialization.codecs.RecordCodecBuilder; import com.simibubi.create.foundation.item.ItemSlots; +import com.simibubi.create.foundation.mixin.accessor.MobEffectInstanceAccessor; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.util.ExtraCodecs; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffectInstance.FactorData; +import net.minecraft.world.food.FoodProperties; import net.minecraftforge.items.ItemStackHandler; @@ -30,4 +50,117 @@ public class CreateCodecs { i -> i >= min ? DataResult.success(i) : DataResult.error(() -> "Value under minimum of " + min) ); } + + public static final Codec NON_NEGATIVE_DOUBLE = doubleRangeWithMessage(0, Double.MAX_VALUE, + i -> "Value must be non-negative: " + i); + public static final Codec POSITIVE_DOUBLE = doubleRangeWithMessage(1, Double.MAX_VALUE, + i -> "Value must be positive: " + i); + + private static Codec doubleRangeWithMessage(double min, double max, Function errorMessage) { + return ExtraCodecs.validate(Codec.DOUBLE, i -> + i.compareTo(min) >= 0 && i.compareTo(max) <= 0 ? DataResult.success(i) : DataResult.error(() -> + errorMessage.apply(i) + ) + ); + } + + public static final Codec UNSIGNED_BYTE = Codec.BYTE + .flatComapMap( + UnsignedBytes::toInt, + p_324632_ -> p_324632_ > 255 + ? DataResult.error(() -> "Unsigned byte was too large: " + p_324632_ + " > 255") + : DataResult.success(p_324632_.byteValue()) + ); + + public static final MapCodec MOB_EFFECT_INSTANCE = recursive( + "MobEffectInstance", + codec -> RecordCodecBuilder.mapCodec( + instance -> instance.group( + BuiltInRegistries.MOB_EFFECT.byNameCodec().fieldOf("effect").forGetter(MobEffectInstance::getEffect), + Codec.INT.optionalFieldOf("duration", 0).forGetter(MobEffectInstance::getDuration), + UNSIGNED_BYTE.optionalFieldOf("amplifier", 0).forGetter(MobEffectInstance::getAmplifier), + Codec.BOOL.optionalFieldOf("ambient", false).forGetter(MobEffectInstance::isAmbient), + Codec.BOOL.optionalFieldOf("show_particles", true).forGetter(MobEffectInstance::isVisible), + Codec.BOOL.optionalFieldOf("show_icon", true).forGetter(MobEffectInstance::showIcon), + codec.optionalFieldOf("hidden_effect").forGetter(i -> Optional.ofNullable(((MobEffectInstanceAccessor) i).create$getHiddenEffect())), + FactorData.CODEC.optionalFieldOf("factor_data").forGetter(MobEffectInstance::getFactorData) + ) + .apply(instance, (effect, duration, amplifier, isAmbient, showParticles, showIcon, hiddenEffect, factorData) -> + new MobEffectInstance(effect, duration, amplifier, isAmbient, showParticles, showIcon, hiddenEffect.orElse(null), factorData)) + ) + ); + + public static final Codec FOOD_PROPERTIES = RecordCodecBuilder.create(instance -> instance.group( + ExtraCodecs.NON_NEGATIVE_INT.fieldOf("nutrition").forGetter(FoodProperties::getNutrition), + Codec.FLOAT.fieldOf("saturation_modifier").forGetter(FoodProperties::getSaturationModifier), + Codec.BOOL.optionalFieldOf("is_meat", false).forGetter(FoodProperties::isMeat), + Codec.BOOL.optionalFieldOf("can_always_eat", false).forGetter(FoodProperties::canAlwaysEat), + Codec.BOOL.optionalFieldOf("is_fast_food", false).forGetter(FoodProperties::isFastFood), + FoodEffect.CODEC.listOf().optionalFieldOf("effects", List.of()).forGetter(i -> { + List effects = new ArrayList<>(); + for (Pair pair : i.getEffects()) + effects.add(new FoodEffect(pair.getFirst(), pair.getSecond())); + return effects; + }) + ).apply(instance, (nutrition, saturationModifier, isMeat, canAlwaysEat, isFastFood, effects) -> { + FoodProperties.Builder builder = new FoodProperties.Builder() + .nutrition(nutrition) + .saturationMod(saturationModifier); + + if (isMeat) builder.meat(); + if (canAlwaysEat) builder.alwaysEat(); + if (isFastFood) builder.fast(); + for (FoodEffect effect : effects) builder.effect(effect.effectSupplier(), effect.probability()); + + return builder.build(); + })); + + public record FoodEffect(Supplier effectSupplier, float probability) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + MOB_EFFECT_INSTANCE.fieldOf("effect").forGetter(FoodEffect::effect), + Codec.FLOAT.fieldOf("probability").forGetter(FoodEffect::probability) + ).apply(instance, FoodEffect::new)); + + private FoodEffect(MobEffectInstance effect, float probability) { + this(() -> effect, probability); + } + + public MobEffectInstance effect() { + return new MobEffectInstance(this.effectSupplier.get()); + } + } + + public static MapCodec recursive(final String name, final Function, MapCodec> wrapped) { + return new RecursiveMapCodec<>(name, wrapped); + } + + private static class RecursiveMapCodec extends MapCodec { + private final String name; + private final Supplier> wrapped; + + private RecursiveMapCodec(final String name, final Function, MapCodec> wrapped) { + this.name = name; + this.wrapped = Suppliers.memoize(() -> wrapped.apply(codec())); + } + + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return wrapped.get().encode(input, ops, prefix); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return wrapped.get().decode(ops, input); + } + + @Override + public Stream keys(final DynamicOps ops) { + return wrapped.get().keys(ops); + } + + @Override + public String toString() { + return "RecursiveMapCodec[" + name + ']'; + } + } } diff --git a/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java b/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java index 39e6cb2d0d..b661ea3f84 100644 --- a/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java +++ b/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java @@ -51,6 +51,7 @@ public class CKinetics extends ConfigBase { public final ConfigBool minecartContraptionInContainers = b(false, "minecartContraptionInContainers", Comments.minecartContraptionInContainers); public final ConfigBool stabiliseStableContraptions = b(false, "stabiliseStableContraptions", Comments.stabiliseStableContraptions, "[Technical]"); + public final ConfigBool syncPlayerPickupHitboxWithContraptionHitbox = b(false, "syncPlayerPickupHitboxWithContraptionHitbox", Comments.syncPlayerPickupHitboxWithContraptionHitbox, "[Technical]"); public final ConfigGroup stats = group(1, "stats", Comments.stats); public final ConfigFloat mediumSpeed = f(30, 0, 4096, "mediumSpeed", Comments.rpm, Comments.mediumSpeed); @@ -120,6 +121,7 @@ public class CKinetics extends ConfigBase { static String reinforcedDeepslateMovement = "Configure how Reinforced Deepslate blocks can be moved by contraptions."; static String minecartContraptionInContainers = "Whether minecart contraptions can be placed into container items."; static String stabiliseStableContraptions = "Whether stabilised bearings create a separated entity even on non-rotating contraptions."; + static String syncPlayerPickupHitboxWithContraptionHitbox = "Whether the players hitbox should be expanded to the size of the contraption hitbox."; } public enum DeployerAggroSetting { diff --git a/src/main/java/com/simibubi/create/infrastructure/data/GeneratedEntriesProvider.java b/src/main/java/com/simibubi/create/infrastructure/data/GeneratedEntriesProvider.java index 421b39be30..93c765d1ee 100644 --- a/src/main/java/com/simibubi/create/infrastructure/data/GeneratedEntriesProvider.java +++ b/src/main/java/com/simibubi/create/infrastructure/data/GeneratedEntriesProvider.java @@ -5,25 +5,27 @@ import java.util.concurrent.CompletableFuture; import com.simibubi.create.AllDamageTypes; import com.simibubi.create.Create; +import com.simibubi.create.api.registry.CreateRegistries; +import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileTypes; import com.simibubi.create.infrastructure.worldgen.AllBiomeModifiers; import com.simibubi.create.infrastructure.worldgen.AllConfiguredFeatures; import com.simibubi.create.infrastructure.worldgen.AllPlacedFeatures; import net.minecraft.core.HolderLookup; import net.minecraft.core.RegistrySetBuilder; -import net.minecraft.core.RegistrySetBuilder.RegistryBootstrap; import net.minecraft.core.registries.Registries; import net.minecraft.data.PackOutput; + import net.minecraftforge.common.data.DatapackBuiltinEntriesProvider; import net.minecraftforge.registries.ForgeRegistries; public class GeneratedEntriesProvider extends DatapackBuiltinEntriesProvider { - @SuppressWarnings({ "rawtypes", "unchecked" }) private static final RegistrySetBuilder BUILDER = new RegistrySetBuilder() - .add(Registries.DAMAGE_TYPE, AllDamageTypes::bootstrap) - .add(Registries.CONFIGURED_FEATURE, (RegistryBootstrap) AllConfiguredFeatures::bootstrap) - .add(Registries.PLACED_FEATURE, AllPlacedFeatures::bootstrap) - .add(ForgeRegistries.Keys.BIOME_MODIFIERS, AllBiomeModifiers::bootstrap); + .add(Registries.DAMAGE_TYPE, AllDamageTypes::bootstrap) + .add(Registries.CONFIGURED_FEATURE, AllConfiguredFeatures::bootstrap) + .add(Registries.PLACED_FEATURE, AllPlacedFeatures::bootstrap) + .add(ForgeRegistries.Keys.BIOME_MODIFIERS, AllBiomeModifiers::bootstrap) + .add(CreateRegistries.POTATO_PROJECTILE_TYPE, AllPotatoProjectileTypes::bootstrap); public GeneratedEntriesProvider(PackOutput output, CompletableFuture registries) { super(output, registries, BUILDER, Set.of(Create.ID)); diff --git a/src/main/resources/create.mixins.json b/src/main/resources/create.mixins.json index 1fa07fdd31..e1d26393bd 100644 --- a/src/main/resources/create.mixins.json +++ b/src/main/resources/create.mixins.json @@ -31,9 +31,11 @@ "accessor.GameTestHelperAccessor", "accessor.ItemModelGeneratorsAccessor", "accessor.LivingEntityAccessor", + "accessor.MobEffectInstanceAccessor", "accessor.NbtAccounterAccessor", "accessor.ServerLevelAccessor", "accessor.StateHolderAccessor", + "accessor.SuspiciousStewItemAccessor", "accessor.SystemReportAccessor", "accessor.UseOnContextAccessor" ],