From 1fc16b1dc881aa618e3899a87cc29bf87d69b03d Mon Sep 17 00:00:00 2001 From: cheaterpaul Date: Fri, 20 Oct 2023 15:27:53 +0200 Subject: [PATCH] add ghost entity --- dev_resources/models/ghost.bbmodel | 1 + .../advancements/main/jump_scare.json | 40 +++++ .../vampirism/loot_tables/entities/ghost.json | 4 + .../tasks/hunter_minion_equipment.json | 18 +-- .../tasks/hunter_minion_upgrade_enhanced.json | 18 +-- .../tasks/hunter_minion_upgrade_special.json | 18 +-- .../vampirism/tasks/random_refinement2.json | 18 +-- .../vampirism/tasks/random_refinement3.json | 18 +-- .../vampirism/tasks/vampire_lord1.json | 36 ++--- .../vampirism/tasks/vampire_lord5.json | 36 ++--- .../tasks/vampire_minion_binding.json | 18 +-- .../vampire_minion_upgrade_enhanced.json | 18 +-- .../tasks/vampire_minion_upgrade_special.json | 18 +-- .../teamlapen/lib/lib/util/SpawnHelper.java | 37 ++--- .../de/teamlapen/lib/lib/util/UtilLib.java | 22 +++ .../blockentity/MotherBlockEntity.java | 87 ++++++++--- .../VulnerableRemainsBlockEntity.java | 47 +++++- .../mother/ActiveVulnerableRemainsBlock.java | 19 +++ .../client/core/ModEntitiesRender.java | 3 + .../vampirism/client/model/GhostModel.java | 78 ++++++++++ .../client/particle/FlyingBloodParticle.java | 5 +- .../client/renderer/entity/GhostRenderer.java | 32 ++++ .../entity/RemainsDefenderRenderer.java | 9 +- .../teamlapen/vampirism/core/ModEntities.java | 2 + .../de/teamlapen/vampirism/core/ModTags.java | 1 + .../vampirism/data/AdvancementGenerator.java | 5 + .../vampirism/data/LootTablesGenerator.java | 1 + .../vampirism/data/TagGenerator.java | 1 + .../vampirism/entity/GhostEntity.java | 142 ++++++++++++++++++ .../vampirism/entity/IEntityFollower.java | 13 ++ .../entity/RemainsDefenderEntity.java | 18 ++- .../entity/VulnerableRemainsDummyEntity.java | 39 +++-- .../entity/ai/goals/DefendLeaderGoal.java | 17 ++- .../entity/ai/goals/FindLeaderGoal.java | 53 +++++++ .../ai/goals/NearestTargetGoalModifier.java | 2 + .../entity/vampire/BasicVampireEntity.java | 19 ++- .../NearestAttackableTargetGoalMixin.java | 8 + .../particle/FlyingBloodParticleOptions.java | 22 ++- .../assets/vampirism/lang/en_us.json | 4 +- .../vampirism/textures/entity/ghost.png | Bin 1001 -> 4955 bytes 40 files changed, 751 insertions(+), 196 deletions(-) create mode 100644 dev_resources/models/ghost.bbmodel create mode 100644 src/generated/resources/data/vampirism/advancements/main/jump_scare.json create mode 100644 src/generated/resources/data/vampirism/loot_tables/entities/ghost.json create mode 100644 src/main/java/de/teamlapen/vampirism/client/model/GhostModel.java create mode 100644 src/main/java/de/teamlapen/vampirism/client/renderer/entity/GhostRenderer.java create mode 100644 src/main/java/de/teamlapen/vampirism/entity/GhostEntity.java create mode 100644 src/main/java/de/teamlapen/vampirism/entity/IEntityFollower.java create mode 100644 src/main/java/de/teamlapen/vampirism/entity/ai/goals/FindLeaderGoal.java diff --git a/dev_resources/models/ghost.bbmodel b/dev_resources/models/ghost.bbmodel new file mode 100644 index 0000000000..01ec868f43 --- /dev/null +++ b/dev_resources/models/ghost.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"vampirism","model_identifier":"vampirism","modded_entity_version":"1.17","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":64,"height":64},"elements":[{"name":"head","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3,13,-3],"to":[3,19,3],"autouv":0,"color":7,"origin":[0,7,0],"faces":{"north":{"uv":[6,6,12,12],"texture":0},"east":{"uv":[0,6,6,12],"texture":0},"south":{"uv":[18,6,24,12],"texture":0},"west":{"uv":[12,6,18,12],"texture":0},"up":{"uv":[12,6,6,0],"texture":0},"down":{"uv":[18,0,12,6],"texture":0}},"type":"cube","uuid":"e7826be5-a11f-32cd-c043-ddffe66ecbc6"},{"name":"body","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-2,0,-2],"to":[2,12,2],"autouv":0,"color":9,"origin":[0,0,0],"uv_offset":[16,16],"faces":{"north":{"uv":[20,20,24,32],"texture":0},"east":{"uv":[16,20,20,32],"texture":0},"south":{"uv":[28,20,32,32],"texture":0},"west":{"uv":[24,20,28,32],"texture":0},"up":{"uv":[24,20,20,16],"texture":0},"down":{"uv":[28,16,24,20],"texture":0}},"type":"cube","uuid":"098085e0-960a-4f81-9a25-6cce9c6f623d"},{"name":"arm_left","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,3,-1],"to":[5,12,1],"autouv":0,"color":2,"origin":[0,12,-4],"uv_offset":[41,17],"faces":{"north":{"uv":[43,19,45,28],"texture":0},"east":{"uv":[41,19,43,28],"texture":0},"south":{"uv":[47,19,49,28],"texture":0},"west":{"uv":[45,19,47,28],"texture":0},"up":{"uv":[45,19,43,17],"texture":0},"down":{"uv":[47,17,45,19],"texture":0}},"type":"cube","uuid":"6f74b57a-a218-39e8-9db7-da8d2e7c87d3"},{"name":"arm_right","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,3,-1],"to":[-3,12,1],"autouv":0,"color":2,"origin":[0,12,4],"uv_offset":[41,17],"faces":{"north":{"uv":[43,19,45,28],"texture":0},"east":{"uv":[41,19,43,28],"texture":0},"south":{"uv":[47,19,49,28],"texture":0},"west":{"uv":[45,19,47,28],"texture":0},"up":{"uv":[45,19,43,17],"texture":0},"down":{"uv":[47,17,45,19],"texture":0}},"type":"cube","uuid":"f2d5995d-b371-5a55-aaf0-b1b077407096"}],"outliner":[{"name":"body","origin":[0,7,0],"color":0,"nbt":"{}","uuid":"87b8cce3-9b97-75d4-c56b-68b028fad032","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["098085e0-960a-4f81-9a25-6cce9c6f623d",{"name":"head","origin":[0,13,0],"color":0,"nbt":"{}","uuid":"7fd260cd-9c76-9ec2-8e60-6b62d4424f6c","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["e7826be5-a11f-32cd-c043-ddffe66ecbc6"]},{"name":"arm_left","origin":[3,12,0],"color":0,"nbt":"{}","uuid":"68b8a470-56e2-956d-491d-b673fc2d8a95","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["6f74b57a-a218-39e8-9db7-da8d2e7c87d3"]},{"name":"arm_right","origin":[-5,12,0],"color":0,"nbt":"{}","uuid":"b85c1371-59ba-7e3e-411d-f31e2d6f9546","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["f2d5995d-b371-5a55-aaf0-b1b077407096"]}]}],"textures":[{"path":"/home/paube/Projects/Minecraft/Vampirism/1.20/src/main/resources/assets/vampirism/textures/entity/ghost.png","name":"ghost.png","folder":"block","namespace":"","id":"0","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"6a3267f5-417c-9609-2655-6756760a22b6","relative_path":"../../Projects/Minecraft/Vampirism/1.20/src/main/resources/assets/vampirism/textures/entity/ghost.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAA7ZJREFUeF7tmUtLI0EQx2vU+H4jKitB8AvsF/HixYMYxahsEBaEvexld4+7hz2GYBQxXgLBgzcP2cPe/BgeAoIsIvjCV7SXf0ENYZgxcZKGnrEHGjOtU+n616+qukeHAq5cLqfa2tqovb2d/0IpRS8vLzz85jOZjBNky+T5wEXn83nV0dHBzjqOwwI8Pz/z8JtfW1uLlwA7OzssgDgLx6vVqiuAdz6dTsdPgEQiQRhw9unpiQdE8JtfWVmJnwDvmoCtrS2FSEsNQPGTGuA3v76+Hk0C3lrtg7pAVLuDE1Ttr6+v6fb2lqamprgL3N/f08PDA3V3d/t2gah2Byeo2p+entLR0RFtbGxwGmSzWZqdnaXx8XEWIC7dgQXwq+qXl5c0//Ub/cln2dmPnzbp78/vTECcukMgAWh3yS8/6F/2F6fAh8wmVbK/A/cBkgJR2x84r1V75/MmJfI5FqCaThNtb7MAceoOkWxdrTxbWAFaqWYUbVkCWh213d1dfo8gR2jZIcp9M4emcrmssDnr6+vjTdrc3FzTAWzagFfAvb099z2C9/0B7peWlkJ/Z6lUUrCBlzT4ubCwENqWrLtpA14BCoUCb6zkCC3HZ7lfXFwM/Z3vkoDj42N1c3PDyAv6ExMT5J0Lmw6hoyGRR87L556eHv6IGjAyMkIDAwPuQi8uLkh+f3d354Kzurr66hoODg6UHMUFfRDmnQubDi0RAI7BqdHRUXYMJ0lxVhaKVIAguEQMPFNPgEgQUCsAMIWDfgQkk0lG+S0CRIIAFDhEGARAgLOzM18CJicnXQHkmVgQIAktAjRCgDxTTwDjCSgWiwq5jIgODg42RMDV1RUTg9SpV7yMrwEiAJzBaKQGQDCMRgQwnoD9/X0l+Q+sIcDJyQkNDw9TV1cXPT4+8kDxm5mZYfKlEIKa5eXlVzuR8QTUCgDncZ2fn7PTY2NjHGm0RbxKw/1bBTCeAGyEavNfilulUqGhoSEWAG+Tp6en3c0PCJA6UK8IGk8ABKjNf/ESadDb2+v+S03wFwKkDtQT4PDwUEkadXZ2Mln9/f1uaslcKpUKtakL9VDtAQgCyA5QUkDSAFHGhe4g+IsAsiOsJ0Crj+tee00LoHuBuu1bAXQrbLp9S4DpEdK9PkuAboVNt28JMD1CutdnCdCtsOn2LQGmR0j3+iwBuhU23b4lwPQI6V6fJUC3wqbbtwSYHiHd67ME6FbYdPuWANMjpHt9lgDdCptu3xJgeoR0r88SoFth0+2/ewL+A92FwG7216M2AAAAAElFTkSuQmCC"}]} \ No newline at end of file diff --git a/src/generated/resources/data/vampirism/advancements/main/jump_scare.json b/src/generated/resources/data/vampirism/advancements/main/jump_scare.json new file mode 100644 index 0000000000..4815e98ae8 --- /dev/null +++ b/src/generated/resources/data/vampirism/advancements/main/jump_scare.json @@ -0,0 +1,40 @@ +{ + "parent": "vampirism:main/vampire_forest", + "criteria": { + "main": { + "conditions": { + "entity": [ + { + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "type": "vampirism:ghost" + } + } + ] + }, + "trigger": "minecraft:entity_killed_player" + } + }, + "display": { + "announce_to_chat": true, + "description": { + "translate": "advancement.vampirism.jump_scare.desc" + }, + "frame": "task", + "hidden": true, + "icon": { + "item": "minecraft:skeleton_skull" + }, + "show_toast": true, + "title": { + "translate": "advancement.vampirism.jump_scare" + } + }, + "requirements": [ + [ + "main" + ] + ], + "sends_telemetry_event": true +} \ No newline at end of file diff --git a/src/generated/resources/data/vampirism/loot_tables/entities/ghost.json b/src/generated/resources/data/vampirism/loot_tables/entities/ghost.json new file mode 100644 index 0000000000..2d818d6d65 --- /dev/null +++ b/src/generated/resources/data/vampirism/loot_tables/entities/ghost.json @@ -0,0 +1,4 @@ +{ + "type": "minecraft:entity", + "random_sequence": "vampirism:entities/ghost" +} \ No newline at end of file diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_equipment.json b/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_equipment.json index d65776b0c1..edd49ecb60 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_equipment.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_equipment.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:entity", + "amount": 5, + "description": { + "translate": "entity.vampirism.vampire_baron" + }, + "entityType": "vampirism:vampire_baron", + "id": "vampirism:vampire_baron" + }, { "type": "vampirism:entity_type", "amount": 4, @@ -20,15 +29,6 @@ "Count": 32, "id": "minecraft:gold_ingot" } - }, - { - "type": "vampirism:entity", - "amount": 5, - "description": { - "translate": "entity.vampirism.vampire_baron" - }, - "entityType": "vampirism:vampire_baron", - "id": "vampirism:vampire_baron" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_enhanced.json b/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_enhanced.json index df21355373..6d19c850f9 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_enhanced.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_enhanced.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:entity", + "amount": 10, + "description": { + "translate": "entity.vampirism.vampire_baron" + }, + "entityType": "vampirism:vampire_baron", + "id": "vampirism:vampire_baron" + }, { "type": "vampirism:item", "description": { @@ -33,15 +42,6 @@ "Count": 3, "id": "minecraft:diamond_block" } - }, - { - "type": "vampirism:entity", - "amount": 10, - "description": { - "translate": "entity.vampirism.vampire_baron" - }, - "entityType": "vampirism:vampire_baron", - "id": "vampirism:vampire_baron" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_special.json b/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_special.json index 5f1326c390..ae990c2ad0 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_special.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/hunter_minion_upgrade_special.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:entity", + "amount": 20, + "description": { + "translate": "entity.vampirism.vampire_baron" + }, + "entityType": "vampirism:vampire_baron", + "id": "vampirism:vampire_baron" + }, { "type": "vampirism:item", "description": { @@ -33,15 +42,6 @@ "Count": 8, "id": "minecraft:diamond_block" } - }, - { - "type": "vampirism:entity", - "amount": 20, - "description": { - "translate": "entity.vampirism.vampire_baron" - }, - "entityType": "vampirism:vampire_baron", - "id": "vampirism:vampire_baron" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement2.json b/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement2.json index 702f284070..eca4c4139f 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement2.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement2.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:entity", + "amount": 3, + "description": { + "translate": "entity.vampirism.vampire_baron" + }, + "entityType": "vampirism:vampire_baron", + "id": "vampirism:vampire_baron" + }, { "type": "vampirism:item", "description": { @@ -11,15 +20,6 @@ "Count": 2, "id": "minecraft:gold_ingot" } - }, - { - "type": "vampirism:entity", - "amount": 3, - "description": { - "translate": "entity.vampirism.vampire_baron" - }, - "entityType": "vampirism:vampire_baron", - "id": "vampirism:vampire_baron" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement3.json b/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement3.json index 95c6671f46..045b35c590 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement3.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/random_refinement3.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:stat", + "amount": 15, + "description": { + "translate": "stat.minecraft.traded_with_villager" + }, + "id": "minecraft:traded_with_villager", + "stat": "minecraft:traded_with_villager" + }, { "type": "vampirism:item", "description": { @@ -11,15 +20,6 @@ "Count": 2, "id": "minecraft:gold_ingot" } - }, - { - "type": "vampirism:stat", - "amount": 15, - "description": { - "translate": "stat.minecraft.traded_with_villager" - }, - "id": "minecraft:traded_with_villager", - "stat": "minecraft:traded_with_villager" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord1.json b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord1.json index 32f902ad73..daf21266d0 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord1.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord1.json @@ -1,6 +1,24 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:stat", + "amount": 25, + "description": { + "translate": "stat.vampirism.infected_creatures" + }, + "id": "vampirism:infected_creatures", + "stat": "vampirism:infected_creatures" + }, + { + "type": "vampirism:stat", + "amount": 3, + "description": { + "translate": "stat.vampirism.win_village_capture" + }, + "id": "vampirism:win_village_capture", + "stat": "vampirism:win_village_capture" + }, { "type": "vampirism:item", "description": { @@ -22,24 +40,6 @@ "Count": 5, "id": "vampirism:pure_blood_4" } - }, - { - "type": "vampirism:stat", - "amount": 25, - "description": { - "translate": "stat.vampirism.infected_creatures" - }, - "id": "vampirism:infected_creatures", - "stat": "vampirism:infected_creatures" - }, - { - "type": "vampirism:stat", - "amount": 3, - "description": { - "translate": "stat.vampirism.win_village_capture" - }, - "id": "vampirism:win_village_capture", - "stat": "vampirism:win_village_capture" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord5.json b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord5.json index 60ec951b61..f57e4c0402 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord5.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_lord5.json @@ -1,6 +1,24 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:stat", + "amount": 50, + "description": { + "translate": "stat.vampirism.infected_creatures" + }, + "id": "vampirism:infected_creatures", + "stat": "vampirism:infected_creatures" + }, + { + "type": "vampirism:stat", + "amount": 6, + "description": { + "translate": "stat.vampirism.capture_village" + }, + "id": "vampirism:capture_village", + "stat": "vampirism:capture_village" + }, { "type": "vampirism:item", "description": { @@ -22,24 +40,6 @@ "Count": 20, "id": "vampirism:pure_blood_4" } - }, - { - "type": "vampirism:stat", - "amount": 50, - "description": { - "translate": "stat.vampirism.infected_creatures" - }, - "id": "vampirism:infected_creatures", - "stat": "vampirism:infected_creatures" - }, - { - "type": "vampirism:stat", - "amount": 6, - "description": { - "translate": "stat.vampirism.capture_village" - }, - "id": "vampirism:capture_village", - "stat": "vampirism:capture_village" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_binding.json b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_binding.json index c509ca34a3..4244320ada 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_binding.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_binding.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:entity", + "amount": 5, + "description": { + "translate": "entity.vampirism.vampire_baron" + }, + "entityType": "vampirism:vampire_baron", + "id": "vampirism:vampire_baron" + }, { "type": "vampirism:entity_type", "amount": 4, @@ -20,15 +29,6 @@ "Count": 32, "id": "minecraft:gold_ingot" } - }, - { - "type": "vampirism:entity", - "amount": 5, - "description": { - "translate": "entity.vampirism.vampire_baron" - }, - "entityType": "vampirism:vampire_baron", - "id": "vampirism:vampire_baron" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_enhanced.json b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_enhanced.json index 01b17e2451..4c34b56440 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_enhanced.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_enhanced.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:entity", + "amount": 10, + "description": { + "translate": "entity.vampirism.vampire_baron" + }, + "entityType": "vampirism:vampire_baron", + "id": "vampirism:vampire_baron" + }, { "type": "vampirism:item", "description": { @@ -33,15 +42,6 @@ "Count": 3, "id": "minecraft:diamond_block" } - }, - { - "type": "vampirism:entity", - "amount": 10, - "description": { - "translate": "entity.vampirism.vampire_baron" - }, - "entityType": "vampirism:vampire_baron", - "id": "vampirism:vampire_baron" } ] }, diff --git a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_special.json b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_special.json index 163eb3551e..9d30122568 100644 --- a/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_special.json +++ b/src/generated/resources/data/vampirism/vampirism/tasks/vampire_minion_upgrade_special.json @@ -1,6 +1,15 @@ { "requirements": { "requirements": [ + { + "type": "vampirism:entity", + "amount": 20, + "description": { + "translate": "entity.vampirism.vampire_baron" + }, + "entityType": "vampirism:vampire_baron", + "id": "vampirism:vampire_baron" + }, { "type": "vampirism:item", "description": { @@ -33,15 +42,6 @@ "Count": 8, "id": "minecraft:diamond_block" } - }, - { - "type": "vampirism:entity", - "amount": 20, - "description": { - "translate": "entity.vampirism.vampire_baron" - }, - "entityType": "vampirism:vampire_baron", - "id": "vampirism:vampire_baron" } ] }, diff --git a/src/lib/java/de/teamlapen/lib/lib/util/SpawnHelper.java b/src/lib/java/de/teamlapen/lib/lib/util/SpawnHelper.java index aca8e548e7..48b9f7efaa 100644 --- a/src/lib/java/de/teamlapen/lib/lib/util/SpawnHelper.java +++ b/src/lib/java/de/teamlapen/lib/lib/util/SpawnHelper.java @@ -1,34 +1,29 @@ package de.teamlapen.lib.lib.util; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; -import net.minecraft.world.phys.Vec3; -import java.util.Optional; +import java.util.function.Consumer; import java.util.function.Supplier; public class SpawnHelper { - public static Optional createEntity(Level level, EntityType type) { - return Optional.ofNullable(type.create(level)); + public static T spawn(EntityType entity, Level level, Consumer functions) { + T t = entity.create(level); + if (t != null) { + functions.accept(t); + level.addFreshEntity(t); + } + return t; } - public static Optional createEntity(Level level, EntityType type, Vec3 pos) { - return createEntity(level, type).map(s -> { - s.setPos(pos); - return s; - }); - } - - public static Optional createEntity(Level level, Supplier> type) { - return Optional.ofNullable(type.get().create(level)); - } - - public static Optional createEntity(Level level, Supplier> type, Vec3 pos) { - return createEntity(level, type).map(s -> { - s.setPos(pos); - return s; - }); + public static T spawn(Supplier> entity, Level level, Consumer functions) { + T t = entity.get().create(level); + if (t != null) { + functions.accept(t); + level.addFreshEntity(t); + } + return t; } } diff --git a/src/lib/java/de/teamlapen/lib/lib/util/UtilLib.java b/src/lib/java/de/teamlapen/lib/lib/util/UtilLib.java index c311fa31bc..fc67f6a102 100644 --- a/src/lib/java/de/teamlapen/lib/lib/util/UtilLib.java +++ b/src/lib/java/de/teamlapen/lib/lib/util/UtilLib.java @@ -823,4 +823,26 @@ public static boolean never(BlockState state, BlockGetter block, BlockPos pos) { public static boolean always(BlockState state, BlockGetter block, BlockPos pos) { return true; } + + @Nullable + public static Direction getDirection(BlockPos origin, BlockPos offset) { + if(origin.getX() > offset.getX()) { + return Direction.EAST; + } else if (origin.getX() < offset.getX()) { + return Direction.WEST; + } + + if(origin.getZ() > offset.getZ()) { + return Direction.SOUTH; + } else if (origin.getZ() < offset.getZ()) { + return Direction.NORTH; + } + + if(origin.getY() > offset.getY()) { + return Direction.UP; + } else if (origin.getY() < offset.getY()) { + return Direction.DOWN; + } + return null; + } } diff --git a/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java b/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java index 76cc095ac8..7dedc21117 100644 --- a/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java +++ b/src/main/java/de/teamlapen/vampirism/blockentity/MotherBlockEntity.java @@ -1,11 +1,14 @@ package de.teamlapen.vampirism.blockentity; +import de.teamlapen.lib.lib.util.SpawnHelper; import de.teamlapen.vampirism.VampirismMod; import de.teamlapen.vampirism.blocks.mother.IRemainsBlock; import de.teamlapen.vampirism.blocks.mother.MotherTreeStructure; +import de.teamlapen.vampirism.core.ModEntities; import de.teamlapen.vampirism.core.ModParticles; import de.teamlapen.vampirism.core.ModSounds; import de.teamlapen.vampirism.core.ModTiles; +import de.teamlapen.vampirism.entity.GhostEntity; import de.teamlapen.vampirism.network.ClientboundPlayEventPacket; import de.teamlapen.vampirism.particle.FlyingBloodParticleOptions; import net.minecraft.core.BlockPos; @@ -21,12 +24,15 @@ import net.minecraft.world.BossEvent; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import org.apache.commons.lang3.tuple.Triple; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,7 +45,6 @@ public class MotherBlockEntity extends BlockEntity { - private final ServerBossEvent bossEvent = new ServerBossEvent(Component.translatable("block.vampirism.mother"), BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10); private boolean isFrozen = false; @@ -65,11 +70,19 @@ public static void serverTick(Level level, BlockPos blockPos, BlockState blockSt } BlockPos p = vuls.get(e.level.getRandom().nextInt(vuls.size())).getLeft(); player.addEffect(new MobEffectInstance(MobEffects.HUNGER, 5 * 20, 2)); - ModParticles.spawnParticlesServer(player.level(), new FlyingBloodParticleOptions(100, false, p.getX() + 0.5, p.getY() + 0.5, p.getZ() + 0.5), player.getX(), player.getY() + player.getEyeHeight() / 2, player.getZ(), 10, 0.1f, 0.1f, 0.1f, 0); + ModParticles.spawnParticlesServer(player.level(), new FlyingBloodParticleOptions(100, false, p.getX() + 0.5, p.getY() + 0.5, p.getZ() + 0.5, 0.5f), player.getX(), player.getY() + player.getEyeHeight() / 2, player.getZ(), 10, 0.1f, 0.1f, 0.1f, 0); + } + } + + // todo only when not finished + if (e.level.getRandom().nextInt(3) == 0) { + if (e.level.getEntitiesOfClass(GhostEntity.class, e.getArea()).size() < Math.min(e.bossEvent.getPlayers().size(), 5)) { + BlockPos left = vuls.get(e.level.getRandom().nextInt(vuls.size())).getLeft(); + e.spawnGhost(level, left); } } - } + } } } //Handle destruction -------------------------------- @@ -94,6 +107,15 @@ public static void serverTick(Level level, BlockPos blockPos, BlockState blockSt } } } + if (level.getGameTime() % 64 == 0) { + AABB inflate = e.getArea().inflate(15); + for (ServerPlayer player : e.bossEvent.getPlayers()) { + if (inflate.distanceToSqr(player.position()) > 100) { + e.bossEvent.removePlayer(player); + } + } + level.getEntities(EntityType.PLAYER, inflate, a -> true).forEach(e::addPlayer); + } } @@ -162,9 +184,11 @@ public void onChunkUnloaded() { } private void addPlayer(Player player) { - updateFightStatus(); - if (player instanceof ServerPlayer serverPlayer) { - this.bossEvent.addPlayer(serverPlayer); + if (!this.bossEvent.getPlayers().contains(player)) { + updateFightStatus(); + if (player instanceof ServerPlayer serverPlayer) { + this.bossEvent.addPlayer(serverPlayer); + } } } @@ -190,22 +214,25 @@ public void onVulnerabilityHit(LivingEntity entity, boolean destroyed) { } public void updateFightStatus() { - List> vuls = getTreeStructure(false).getVerifiedVulnerabilities(level).toList(); - List> remaining = vuls.stream().filter(vul -> vul.getRight().isVulnerable(vul.getMiddle())).toList(); - if (!remaining.isEmpty()) { - var remainingHealth = remaining.stream().mapToInt(s -> { - var entity = level.getBlockEntity(s.getLeft()); - if (entity instanceof VulnerableRemainsBlockEntity vulnerable) { - return vulnerable.getHealth(); - } - return VulnerableRemainsBlockEntity.MAX_HEALTH; - }).sum(); - this.bossEvent.setProgress(remainingHealth / ((float) vuls.size() * VulnerableRemainsBlockEntity.MAX_HEALTH)); + if (!this.isFrozen) { + List> vuls = getTreeStructure(false).getVerifiedVulnerabilities(level).toList(); + List> remaining = vuls.stream().filter(vul -> vul.getRight().isVulnerable(vul.getMiddle())).toList(); + if (!remaining.isEmpty()) { + var remainingHealth = remaining.stream().mapToInt(s -> { + var entity = level.getBlockEntity(s.getLeft()); + if (entity instanceof VulnerableRemainsBlockEntity vulnerable) { + return vulnerable.getHealth(); + } + return VulnerableRemainsBlockEntity.MAX_HEALTH; + }).sum(); + this.bossEvent.setProgress(remainingHealth / ((float) vuls.size() * VulnerableRemainsBlockEntity.MAX_HEALTH)); + } else { + this.bossEvent.setProgress(0); + this.endFight(); + } } else { - this.bossEvent.setProgress(0); - this.endFight(); + } - this.bossEvent.getPlayers().stream().filter(player -> player.hasDisconnected() || player.distanceToSqr(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()) > 100 * 100).toList().forEach(this.bossEvent::removePlayer); } @Override @@ -223,6 +250,7 @@ private void endFight() { } private void freezeFight() { + spawnGhosts(); this.isFrozen = true; this.freezeTimer = 20 * 20; getTreeStructure(false).getVerifiedVulnerabilities(this.level).forEach(vul -> vul.getRight().freeze(level, vul.getLeft(), vul.getMiddle())); @@ -255,4 +283,23 @@ public void informAboutAttacker(ServerPlayer serverPlayer) { public Collection involvedPlayers() { return this.bossEvent.getPlayers(); } + + private void spawnGhost(Level level, BlockPos pos) { + SpawnHelper.spawn(ModEntities.GHOST, level, ghost -> { + ghost.setPos(Vec3.atCenterOf(pos)); + ghost.setHome(getArea()); + }); + } + + private AABB getArea() { + return new AABB(this.worldPosition).inflate(20, 0,20).expandTowards(0, -20, 0); + } + + private void spawnGhosts() { + Set vuls = this.getTreeStructure(false).getCachedBlocks(); + int size = this.level.getEntitiesOfClass(GhostEntity.class, this.getArea()).size(); + for(int i = size; i < 3; i++) { + vuls.stream().skip(level.getRandom().nextInt(vuls.size())).findFirst().ifPresent(pos -> this.spawnGhost(level, pos)); + } + } } diff --git a/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java b/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java index ff867ef01e..b65d3f9185 100644 --- a/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java +++ b/src/main/java/de/teamlapen/vampirism/blockentity/VulnerableRemainsBlockEntity.java @@ -1,6 +1,7 @@ package de.teamlapen.vampirism.blockentity; import de.teamlapen.lib.lib.util.SpawnHelper; +import de.teamlapen.lib.lib.util.UtilLib; import de.teamlapen.vampirism.blocks.mother.MotherTreeStructure; import de.teamlapen.vampirism.core.ModBlocks; import de.teamlapen.vampirism.core.ModEntities; @@ -8,6 +9,7 @@ import de.teamlapen.vampirism.core.ModTiles; import de.teamlapen.vampirism.entity.VulnerableRemainsDummyEntity; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerLevel; @@ -17,6 +19,7 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,10 +37,11 @@ public static void serverTick(ServerLevel level, BlockPos blockPos, BlockState b mother.updateFightStatus(); e.checkDummyEntity(level, blockPos); }); - } else if (level.getRandom().nextInt(100) == 3) { + } else if (level.getGameTime() % 128 == 3) { e.checkDummyEntity(level, blockPos); - if (e.lastDamage - level.getGameTime() > 3 * 60 * 20L) { - e.health = Math.min(e.health + 10, MAX_HEALTH); + e.health = Math.min(e.health + 1, MAX_HEALTH); + if (e.health == MAX_HEALTH) { + e.spawnedBackup = false; } } } @@ -45,6 +49,7 @@ public static void serverTick(ServerLevel level, BlockPos blockPos, BlockState b private int health = MAX_HEALTH; @Nullable private UUID dummy_entity_id; + private boolean spawnedBackup; public VulnerableRemainsBlockEntity(BlockPos pos, BlockState state) { super(ModTiles.VULNERABLE_CURSED_ROOTED_DIRT.get(), pos, state); @@ -81,6 +86,10 @@ public void onDamageDealt(DamageSource src, double damage) { if (src.getEntity() instanceof LivingEntity entity) { this.getMother().ifPresent(mother -> mother.onVulnerabilityHit(entity, this.health <= 0)); } + if (!this.spawnedBackup && this.health <= MAX_HEALTH / 2 && this.level instanceof ServerLevel serverLevel) { + checkDummyEntity(serverLevel, this.worldPosition).spawnDefenders(); + this.spawnedBackup = true; + } } @Override @@ -96,17 +105,17 @@ protected void saveAdditional(@NotNull CompoundTag tag) { } } - private void checkDummyEntity(ServerLevel level, BlockPos blockPos) { + private VulnerableRemainsDummyEntity checkDummyEntity(ServerLevel level, BlockPos blockPos) { if (dummy_entity_id != null) { Entity e = level.getEntity(dummy_entity_id); - if (e instanceof VulnerableRemainsDummyEntity) { - return; + if (e instanceof VulnerableRemainsDummyEntity dummy) { + return dummy; } dummy_entity_id = null; } - SpawnHelper.createEntity(level, ModEntities.VULNERABLE_REMAINS_DUMMY, blockPos.getCenter().add(0, -0.51f, 0)).ifPresent(entity -> { + return SpawnHelper.spawn(ModEntities.VULNERABLE_REMAINS_DUMMY, level, entity -> { + entity.setPos(Vec3.atCenterOf(blockPos).add(0, -0.51f, 0)); entity.setOwnerLocation(blockPos); - level.addFreshEntity(entity); this.dummy_entity_id = entity.getUUID(); }); } @@ -124,4 +133,26 @@ public Optional getMother() { return Optional.empty(); } + public void checkNeighbor(BlockPos pNeighborPos) { + if(level instanceof ServerLevel serverLevel && serverLevel.getBlockState(pNeighborPos).isAir()) { + Direction direction = UtilLib.getDirection(getBlockPos(), pNeighborPos); + if (direction != null) { + checkDummyEntity(serverLevel, getBlockPos()).spawnDefender(direction.getOpposite()); + } + } + } + + @Override + public void setRemoved() { + super.setRemoved(); + if (this.dummy_entity_id != null && this.level instanceof ServerLevel serverLevel) { + Optional.ofNullable(serverLevel.getEntity(dummy_entity_id)).ifPresent(entity -> entity.remove(Entity.RemovalReason.DISCARDED)); + } + } + + public void onPlaced() { + if (this.level instanceof ServerLevel serverLevel) { + checkDummyEntity(serverLevel, getBlockPos()).spawnDefenders(); + } + } } diff --git a/src/main/java/de/teamlapen/vampirism/blocks/mother/ActiveVulnerableRemainsBlock.java b/src/main/java/de/teamlapen/vampirism/blocks/mother/ActiveVulnerableRemainsBlock.java index b920a4e5a5..357dd295c7 100644 --- a/src/main/java/de/teamlapen/vampirism/blocks/mother/ActiveVulnerableRemainsBlock.java +++ b/src/main/java/de/teamlapen/vampirism/blocks/mother/ActiveVulnerableRemainsBlock.java @@ -6,6 +6,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; @@ -48,4 +50,21 @@ public BlockEntityTicker getTicker(Level level, @NotN public void freeze(Level level, BlockPos pos, BlockState state) { level.setBlock(pos, ModBlocks.VULNERABLE_REMAINS.get().defaultBlockState(), 3); } + + @Override + public void onNeighborChange(BlockState state, LevelReader level, BlockPos pos, BlockPos neighbor) { + super.onNeighborChange(state, level, pos, neighbor); + } + + @Override + public void neighborChanged(@NotNull BlockState pState, @NotNull Level pLevel, @NotNull BlockPos pPos, @NotNull Block pNeighborBlock, @NotNull BlockPos pNeighborPos, boolean pMovedByPiston) { + super.neighborChanged(pState, pLevel, pPos, pNeighborBlock, pNeighborPos, pMovedByPiston); + getBlockEntity(pLevel, pPos).ifPresent(x -> x.checkNeighbor(pNeighborPos)); + } + + @Override + public void onPlace(@NotNull BlockState pState, @NotNull Level pLevel, @NotNull BlockPos pPos, @NotNull BlockState pOldState, boolean pMovedByPiston) { + super.onPlace(pState, pLevel, pPos, pOldState, pMovedByPiston); + getBlockEntity(pLevel, pPos).ifPresent(VulnerableRemainsBlockEntity::onPlaced); + } } diff --git a/src/main/java/de/teamlapen/vampirism/client/core/ModEntitiesRender.java b/src/main/java/de/teamlapen/vampirism/client/core/ModEntitiesRender.java index f7d7222081..f46a58c6fd 100755 --- a/src/main/java/de/teamlapen/vampirism/client/core/ModEntitiesRender.java +++ b/src/main/java/de/teamlapen/vampirism/client/core/ModEntitiesRender.java @@ -56,6 +56,7 @@ public class ModEntitiesRender { public static final ModelLayerLocation GENERIC_BIPED_ARMOR_INNER = new ModelLayerLocation(new ResourceLocation("vampirism:generic_biped"), "inner_armor"); public static final ModelLayerLocation TASK_MASTER = new ModelLayerLocation(new ResourceLocation("vampirism:task_master"), "main"); public static final ModelLayerLocation REMAINS_DEFENDER = new ModelLayerLocation(new ResourceLocation("vampirism:remains_defender"), "main"); + public static final ModelLayerLocation GHOST = new ModelLayerLocation(new ResourceLocation("vampirism:ghost"), "main"); static void onRegisterRenderers(EntityRenderersEvent.@NotNull RegisterRenderers event) { @@ -97,6 +98,7 @@ static void onRegisterRenderers(EntityRenderersEvent.@NotNull RegisterRenderers event.registerEntityRenderer(ModEntities.CONVERTED_GOAT.get(), convertedRenderer(GoatRenderer::new)); event.registerEntityRenderer(ModEntities.VULNERABLE_REMAINS_DUMMY.get(), DummyRenderer::new); event.registerEntityRenderer(ModEntities.REMAINS_DEFENDER.get(), RemainsDefenderRenderer::new); + event.registerEntityRenderer(ModEntities.GHOST.get(), GhostRenderer::new); } static void onRegisterLayers(EntityRenderersEvent.@NotNull RegisterLayerDefinitions event) { @@ -123,6 +125,7 @@ static void onRegisterLayers(EntityRenderersEvent.@NotNull RegisterLayerDefiniti event.registerLayerDefinition(GENERIC_BIPED_ARMOR_OUTER, () -> LayerDefinition.create(HumanoidModel.createMesh(LayerDefinitions.OUTER_ARMOR_DEFORMATION, 0.0F), 64, 32)); event.registerLayerDefinition(TASK_MASTER, () -> LayerDefinition.create(VillagerModel.createBodyModel(), 64, 64)); event.registerLayerDefinition(REMAINS_DEFENDER, RemainsDefenderModel::createBodyLayer); + event.registerLayerDefinition(GHOST, GhostModel::createMesh); LayerDefinition boatDefinition = BoatModel.createBodyModel(); LayerDefinition chestBoatDefinition = ChestBoatModel.createBodyModel(); diff --git a/src/main/java/de/teamlapen/vampirism/client/model/GhostModel.java b/src/main/java/de/teamlapen/vampirism/client/model/GhostModel.java new file mode 100644 index 0000000000..2265bbc21d --- /dev/null +++ b/src/main/java/de/teamlapen/vampirism/client/model/GhostModel.java @@ -0,0 +1,78 @@ +package de.teamlapen.vampirism.client.model; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import de.teamlapen.vampirism.entity.GhostEntity; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.NotNull; + +public class GhostModel extends EntityModel { + + private static final String HEAD = "head"; + private static final String BODY = "body"; + private static final String RIGHT_ARM = "right_arm"; + private static final String LEFT_ARM = "left_arm"; + + public static LayerDefinition createMesh() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + PartDefinition body = partdefinition.addOrReplaceChild(BODY, CubeListBuilder.create().texOffs(16, 16).addBox(-2.0F, -5.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 17.0F, 0.0F)); + PartDefinition head = body.addOrReplaceChild(HEAD, CubeListBuilder.create().texOffs(0, 0).addBox(-3.0F, -6.0F, -3.0F, 6.0F, 6.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -6.0F, 0.0F)); + PartDefinition arm_left = body.addOrReplaceChild(LEFT_ARM, CubeListBuilder.create().texOffs(41, 17).addBox(-2.0F, 0.0F, -1.0F, 2.0F, 9.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offset(-3.0F, -5.0F, 0.0F)); + PartDefinition arm_right = body.addOrReplaceChild(RIGHT_ARM, CubeListBuilder.create().texOffs(41, 17).addBox(-2.0F, 0.0F, -1.0F, 2.0F, 9.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offset(5.0F, -5.0F, 0.0F)); + + return LayerDefinition.create(meshdefinition, 64, 64); + } + + private final ModelPart head; + private final ModelPart body; + private final ModelPart rightarm; + private final ModelPart leftarm; + + public GhostModel(@NotNull ModelPart part) { + this.body = part.getChild(BODY); + this.head = this.body.getChild(HEAD); + this.rightarm = this.body.getChild(RIGHT_ARM); + this.leftarm = this.body.getChild(LEFT_ARM); + } + @Override + public void setupAnim(@NotNull GhostEntity pEntity, float pLimbSwing, float pLimbSwingAmount, float pAgeInTicks, float pNetHeadYaw, float pHeadPitch) { + this.body.getAllParts().forEach(ModelPart::resetPose); + this.head.yRot = pNetHeadYaw / (180F / (float) Math.PI); + this.head.xRot = pHeadPitch / (180F / (float) Math.PI); + float f6 = Mth.sin(pLimbSwingAmount * (float) Math.PI); + float f7 = Mth.sin((1.0F - (1.0F - pLimbSwingAmount) * (1.0F - pLimbSwingAmount)) * (float) Math.PI); + this.rightarm.zRot = 0.0F; + this.leftarm.zRot = 0.0F; + this.rightarm.yRot = -(0.1F - f6 * 0.6F); + this.leftarm.yRot = 0.1F - f6 * 0.6F; + this.rightarm.xRot = -((float) Math.PI / 2F); + this.leftarm.xRot = -((float) Math.PI / 2F); + this.rightarm.xRot -= f6 * 1.2F - f7 * 0.4F; + this.leftarm.xRot -= f6 * 1.2F - f7 * 0.4F; + this.rightarm.zRot += Mth.cos(pLimbSwing * 0.09F) * 0.05F + 0.05F; + this.leftarm.zRot -= Mth.cos(pLimbSwing * 0.09F) * 0.05F + 0.05F; + this.rightarm.xRot += Mth.sin(pLimbSwing * 0.067F) * 0.05F; + this.leftarm.xRot -= Mth.sin(pLimbSwing * 0.067F) * 0.05F; + + float f3 = pAgeInTicks * 5.0F * ((float)Math.PI / 180F); + float f4 = Math.min(pLimbSwingAmount / 0.3F, 1.0F); + float f5 = 1.0F - f4; + + this.body.y += (float)Math.cos((double)f3) * 0.25F * f5; + } + + @Override + public void renderToBuffer(PoseStack pPoseStack, @NotNull VertexConsumer pBuffer, int pPackedLight, int pPackedOverlay, float pRed, float pGreen, float pBlue, float pAlpha) { + pPoseStack.pushPose(); + pPoseStack.scale(0.5F, 0.5F, 0.5F); + pPoseStack.translate(0.0F, 1.5F, 0.0F); + this.body.render(pPoseStack, pBuffer, pPackedLight, pPackedOverlay, pRed, pGreen, pBlue, pAlpha); + pPoseStack.popPose(); + } + +} diff --git a/src/main/java/de/teamlapen/vampirism/client/particle/FlyingBloodParticle.java b/src/main/java/de/teamlapen/vampirism/client/particle/FlyingBloodParticle.java index 191a99ac56..a1a8378e87 100755 --- a/src/main/java/de/teamlapen/vampirism/client/particle/FlyingBloodParticle.java +++ b/src/main/java/de/teamlapen/vampirism/client/particle/FlyingBloodParticle.java @@ -25,7 +25,7 @@ public class FlyingBloodParticle extends TextureSheetParticle { private final boolean direct; - public FlyingBloodParticle(@NotNull ClientLevel world, double posX, double posY, double posZ, double destX, double destY, double destZ, int maxage, boolean direct, @NotNull ResourceLocation particleId) { + public FlyingBloodParticle(@NotNull ClientLevel world, double posX, double posY, double posZ, double destX, double destY, double destZ, int maxage, boolean direct, @NotNull ResourceLocation particleId, float scale) { super(world, posX, posY, posZ); this.lifetime = maxage; this.destX = destX; @@ -48,6 +48,7 @@ public FlyingBloodParticle(@NotNull ClientLevel world, double posX, double posY, } this.setSprite(Minecraft.getInstance().particleEngine.textureAtlas.getSprite(particleId)); + this.scale(scale); this.hasPhysics = false; } @@ -89,7 +90,7 @@ public static class Factory implements ParticleProvider { + + private static final ResourceLocation TEXTURE = new ResourceLocation(REFERENCE.MODID, "textures/entity/ghost.png"); + + public GhostRenderer(EntityRendererProvider.Context pContext) { + super(pContext, new GhostModel(pContext.bakeLayer(ModEntitiesRender.GHOST)), 0.1f); + } + + @Override + public @NotNull ResourceLocation getTextureLocation(@NotNull GhostEntity pEntity) { + return TEXTURE; + } + + @Nullable + @Override + protected RenderType getRenderType(@NotNull GhostEntity pLivingEntity, boolean pBodyVisible, boolean pTranslucent, boolean pGlowing) { + return super.getRenderType(pLivingEntity, pBodyVisible, true, pGlowing); + } +} diff --git a/src/main/java/de/teamlapen/vampirism/client/renderer/entity/RemainsDefenderRenderer.java b/src/main/java/de/teamlapen/vampirism/client/renderer/entity/RemainsDefenderRenderer.java index 7d4cbb9365..60ef5700c3 100644 --- a/src/main/java/de/teamlapen/vampirism/client/renderer/entity/RemainsDefenderRenderer.java +++ b/src/main/java/de/teamlapen/vampirism/client/renderer/entity/RemainsDefenderRenderer.java @@ -6,22 +6,17 @@ import de.teamlapen.vampirism.client.model.RemainsDefenderModel; import de.teamlapen.vampirism.entity.RemainsDefenderEntity; import net.minecraft.client.renderer.entity.EntityRendererProvider; -import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.renderer.entity.MobRenderer; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.NotNull; -public class RemainsDefenderRenderer extends LivingEntityRenderer { +public class RemainsDefenderRenderer extends MobRenderer { private final ResourceLocation TEX = new ResourceLocation(REFERENCE.MODID, "textures/entity/remains_defender.png"); public RemainsDefenderRenderer(EntityRendererProvider.Context pContext) { super(pContext, new RemainsDefenderModel(pContext.bakeLayer(ModEntitiesRender.REMAINS_DEFENDER)), 0f); } - @Override - protected boolean shouldShowName(RemainsDefenderEntity pEntity) { - return false; - } - @Override public @NotNull ResourceLocation getTextureLocation(@NotNull RemainsDefenderEntity pEntity) { return TEX; diff --git a/src/main/java/de/teamlapen/vampirism/core/ModEntities.java b/src/main/java/de/teamlapen/vampirism/core/ModEntities.java index 764887f8e8..2167b190fa 100755 --- a/src/main/java/de/teamlapen/vampirism/core/ModEntities.java +++ b/src/main/java/de/teamlapen/vampirism/core/ModEntities.java @@ -79,6 +79,7 @@ public class ModEntities { public static final RegistryObject> CONVERTED_GOAT = prepareEntityType("converted_goat", () -> EntityType.Builder.of(ConvertedGoatEntity::new, MobCategory.CREATURE).sized(0.9F, 1.3F), true); public static final RegistryObject> VULNERABLE_REMAINS_DUMMY = prepareEntityType("vulnerable_remains_dummy", () -> EntityType.Builder.of(VulnerableRemainsDummyEntity::new, MobCategory.MISC).sized(1.02f, 1.02f).setTrackingRange(10).setUpdateInterval(20), false); public static final RegistryObject> REMAINS_DEFENDER = prepareEntityType("remains_defender", () -> EntityType.Builder.of(RemainsDefenderEntity::new, MobCategory.MISC).sized(0.3f, 0.3f).setTrackingRange(10).setUpdateInterval(20), false); + public static final RegistryObject> GHOST = prepareEntityType("ghost", () -> EntityType.Builder.of(GhostEntity::new, VReference.VAMPIRE_CREATURE_TYPE).sized(0.35F, 0.6F).setTrackingRange(10).setUpdateInterval(20).fireImmune(), true); public static final RegistryObject> DEFAULT_CONVERTER = CONVERTING_HELPER.register("default", () -> DefaultConverter.CODEC); public static final RegistryObject> SPECIAL_CONVERTER = CONVERTING_HELPER.register("special", () -> SpecialConverter.CODEC); @@ -149,6 +150,7 @@ static void onRegisterEntityTypeAttributes(@NotNull EntityAttributeCreationEvent event.put(CONVERTED_GOAT.get(), ConvertedGoatEntity.createAttributes().build()); event.put(VULNERABLE_REMAINS_DUMMY.get(), VulnerableRemainsDummyEntity.createAttributes().build()); event.put(REMAINS_DEFENDER.get(), RemainsDefenderEntity.createAttributes().build()); + event.put(GHOST.get(), GhostEntity.createAttributes().build()); } static void onModifyEntityTypeAttributes(@NotNull EntityAttributeModificationEvent event) { diff --git a/src/main/java/de/teamlapen/vampirism/core/ModTags.java b/src/main/java/de/teamlapen/vampirism/core/ModTags.java index 7e081278b2..9d0a53ba5a 100644 --- a/src/main/java/de/teamlapen/vampirism/core/ModTags.java +++ b/src/main/java/de/teamlapen/vampirism/core/ModTags.java @@ -185,6 +185,7 @@ public static class Professions { public static class DamageTypes { public static final TagKey ENTITY_PHYSICAL = tag("entity_physical"); + public static final TagKey REMAINS_INVULNERABLE = tag("remains_invulnerable"); private static @NotNull TagKey tag(@NotNull String name) { return TagKey.create(Registries.DAMAGE_TYPE, new ResourceLocation(REFERENCE.MODID, name)); diff --git a/src/main/java/de/teamlapen/vampirism/data/AdvancementGenerator.java b/src/main/java/de/teamlapen/vampirism/data/AdvancementGenerator.java index faadefd23e..c325684475 100644 --- a/src/main/java/de/teamlapen/vampirism/data/AdvancementGenerator.java +++ b/src/main/java/de/teamlapen/vampirism/data/AdvancementGenerator.java @@ -119,6 +119,11 @@ public void generate(@NotNull Advancement root, HolderLookup.@NotNull Provider h .parent(vampire_forest) .addCriterion("main", KilledTrigger.TriggerInstance.playerKilledEntity(EntityPredicate.Builder.entity().of(ModEntities.VAMPIRE_BARON.get()))) .save(consumer, REFERENCE.MODID + ":main/regicide"); + Advancement jumpScare = Advancement.Builder.advancement() + .display(Items.SKELETON_SKULL, Component.translatable("advancement.vampirism.jump_scare"), Component.translatable("advancement.vampirism.jump_scare.desc"), null, FrameType.TASK, true, true, true) + .parent(vampire_forest) + .addCriterion("main", KilledTrigger.TriggerInstance.entityKilledPlayer(EntityPredicate.Builder.entity().of(ModEntities.GHOST.get()))) + .save(consumer, REFERENCE.MODID + ":main/jump_scare"); } } diff --git a/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java b/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java index 034f1ed314..1c85720227 100644 --- a/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java +++ b/src/main/java/de/teamlapen/vampirism/data/LootTablesGenerator.java @@ -149,6 +149,7 @@ public void generate() { this.add(ModEntities.TASK_MASTER_HUNTER.get(), LootTable.lootTable()); this.add(ModEntities.VAMPIRE_MINION.get(), LootTable.lootTable()); this.add(ModEntities.HUNTER_MINION.get(), LootTable.lootTable()); + this.add(ModEntities.GHOST.get(), LootTable.lootTable()); } } diff --git a/src/main/java/de/teamlapen/vampirism/data/TagGenerator.java b/src/main/java/de/teamlapen/vampirism/data/TagGenerator.java index a18f392418..83e2fa0fff 100644 --- a/src/main/java/de/teamlapen/vampirism/data/TagGenerator.java +++ b/src/main/java/de/teamlapen/vampirism/data/TagGenerator.java @@ -345,6 +345,7 @@ protected void addTags(HolderLookup.@NotNull Provider pProvider) { this.tag(DamageTypeTags.WITCH_RESISTANT_TO).add(ModDamageTypes.SUN_DAMAGE, ModDamageTypes.VAMPIRE_ON_FIRE, ModDamageTypes.VAMPIRE_IN_FIRE, ModDamageTypes.NO_BLOOD, ModDamageTypes.HOLY_WATER); this.tag(DamageTypeTags.BYPASSES_ENCHANTMENTS).add(ModDamageTypes.DBNO); this.tag(ModTags.DamageTypes.ENTITY_PHYSICAL).add(DamageTypes.PLAYER_ATTACK, DamageTypes.MOB_ATTACK, DamageTypes.MOB_ATTACK_NO_AGGRO, DamageTypes.MOB_PROJECTILE, DamageTypes.ARROW, DamageTypes.STING, DamageTypes.THORNS); + this.tag(ModTags.DamageTypes.REMAINS_INVULNERABLE).add(DamageTypes.IN_WALL, DamageTypes.DROWN); } } diff --git a/src/main/java/de/teamlapen/vampirism/entity/GhostEntity.java b/src/main/java/de/teamlapen/vampirism/entity/GhostEntity.java new file mode 100644 index 0000000000..aa26fae44d --- /dev/null +++ b/src/main/java/de/teamlapen/vampirism/entity/GhostEntity.java @@ -0,0 +1,142 @@ +package de.teamlapen.vampirism.entity; + +import de.teamlapen.vampirism.api.VReference; +import de.teamlapen.vampirism.api.VampirismAPI; +import de.teamlapen.vampirism.api.entity.IEntityLeader; +import de.teamlapen.vampirism.entity.ai.goals.DefendLeaderGoal; +import de.teamlapen.vampirism.entity.ai.goals.FindLeaderGoal; +import de.teamlapen.vampirism.entity.ai.goals.NearestTargetGoalModifier; +import net.minecraft.tags.DamageTypeTags; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.control.FlyingMoveControl; +import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; +import net.minecraft.world.entity.ai.goal.MeleeAttackGoal; +import net.minecraft.world.entity.ai.goal.RandomStrollGoal; +import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; +import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; +import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation; +import net.minecraft.world.entity.ai.navigation.PathNavigation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.pathfinder.BlockPathTypes; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.ForgeMod; +import org.jetbrains.annotations.NotNull; + +public class GhostEntity extends VampirismEntity implements IRemainsEntity, IEntityFollower { + + private IEntityLeader leader; + + public GhostEntity(@NotNull EntityType type, @NotNull Level world) { + super(type, world); + this.moveControl = new FlyingMoveControl(this, 20, true); + } + + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 20.0D).add(Attributes.ARMOR, 15).add(Attributes.ARMOR_TOUGHNESS, 5).add(Attributes.ATTACK_DAMAGE, 6).add(Attributes.MOVEMENT_SPEED, 0.3).add(Attributes.FLYING_SPEED, 0.3).add(ForgeMod.ENTITY_REACH.get(), 1); + } + + @Override + protected @NotNull PathNavigation createNavigation(@NotNull Level pLevel) { + GhostPathNavigation navigation = new GhostPathNavigation(this, pLevel); + navigation.setCanOpenDoors(false); + navigation.setCanFloat(true); + navigation.setCanPassDoors(true); + return navigation; + } + + @Override + public boolean isInvulnerableTo(DamageSource pSource) { + return pSource.is(DamageTypeTags.IS_PROJECTILE) && super.isInvulnerableTo(pSource); + } + + @Override + public void playerTouch(Player pPlayer) { + if (pPlayer.canFreeze()) { + pPlayer.setTicksFrozen(Math.min(pPlayer.getTicksFrozen() + 2, pPlayer.getTicksRequiredToFreeze() + 10)); + } + } + + + + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new GhostMeleeAttackGoal(1, true)); + this.goalSelector.addGoal(3, new FindLeaderGoal<>(this, VulnerableRemainsDummyEntity.class::isInstance)); + this.goalSelector.addGoal(7, new RandomStrollGoal(this, 0.9F)); + this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 16)); + + this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); + NearestAttackableTargetGoal goal = new NearestAttackableTargetGoal<>(this, Player.class, 0, false, false, VampirismAPI.factionRegistry().getPredicate(VReference.VAMPIRE_FACTION, true, false, true, true, null)); + ((NearestTargetGoalModifier) goal).ignoreLineOfSight(); + this.targetSelector.addGoal(3, goal); + NearestAttackableTargetGoal goal2 = new NearestAttackableTargetGoal<>(this, PathfinderMob.class, 5, false, false, VampirismAPI.factionRegistry().getPredicate(VReference.VAMPIRE_FACTION, false, true, false, true, null)) { + @Override + protected double getFollowDistance() { + return super.getFollowDistance() / 2; + } + }; + ((NearestTargetGoalModifier) goal2).ignoreLineOfSight(); + this.targetSelector.addGoal(4, goal2); + this.targetSelector.addGoal(8, new DefendLeaderGoal<>(this)); + } + + @Override + public void tick() { + this.setNoGravity(true); + this.noPhysics = true; + super.tick(); + this.noPhysics = false; + this.setNoGravity(true); + } + + @Override + public boolean isFollowing() { + return this.leader != null; + } + + @Override + public T getLeader() { + return (T) this.leader; + } + + @Override + public void setLeader(T leader) { + this.leader = leader; + } + + @Override + public float getPathfindingMalus(@NotNull BlockPathTypes pNodeType) { + return 0; + } + + class GhostMeleeAttackGoal extends MeleeAttackGoal { + + public GhostMeleeAttackGoal(double pSpeedModifier, boolean pFollowingTargetEvenIfNotSeen) { + super(GhostEntity.this, pSpeedModifier, pFollowingTargetEvenIfNotSeen); + } + + @Override + protected double getAttackReachSqr(LivingEntity pAttackTarget) { + return this.mob.getBbWidth() * 3.0F * this.mob.getBbWidth() * 3.0F + pAttackTarget.getBbWidth(); + } + } + + static class GhostPathNavigation extends FlyingPathNavigation { + + public GhostPathNavigation(Mob pMob, Level pLevel) { + super(pMob, pLevel); + } + + @Override + protected boolean canMoveDirectly(@NotNull Vec3 pPosVec31, @NotNull Vec3 pPosVec32) { + return true; + } + } +} diff --git a/src/main/java/de/teamlapen/vampirism/entity/IEntityFollower.java b/src/main/java/de/teamlapen/vampirism/entity/IEntityFollower.java new file mode 100644 index 0000000000..739388c105 --- /dev/null +++ b/src/main/java/de/teamlapen/vampirism/entity/IEntityFollower.java @@ -0,0 +1,13 @@ +package de.teamlapen.vampirism.entity; + +import de.teamlapen.vampirism.api.entity.IEntityLeader; +import net.minecraft.world.entity.LivingEntity; + +public interface IEntityFollower { + + boolean isFollowing(); + + T getLeader(); + + void setLeader(T leader); +} diff --git a/src/main/java/de/teamlapen/vampirism/entity/RemainsDefenderEntity.java b/src/main/java/de/teamlapen/vampirism/entity/RemainsDefenderEntity.java index 229583150c..6ecb9bc73d 100644 --- a/src/main/java/de/teamlapen/vampirism/entity/RemainsDefenderEntity.java +++ b/src/main/java/de/teamlapen/vampirism/entity/RemainsDefenderEntity.java @@ -39,7 +39,7 @@ public class RemainsDefenderEntity extends AbstractGolem implements IRemainsEnti protected static final EntityDataAccessor DATA_ATTACH_FACE_ID = SynchedEntityData.defineId(RemainsDefenderEntity.class, EntityDataSerializers.DIRECTION); public static AttributeSupplier.Builder createAttributes() { - return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 20.0D).add(Attributes.ATTACK_DAMAGE, 3); + return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 20.0D).add(Attributes.ARMOR, 15).add(Attributes.ATTACK_DAMAGE, 5).add(Attributes.ARMOR_TOUGHNESS, 6); } public RemainsDefenderEntity(EntityType type, Level pLevel) { @@ -74,6 +74,11 @@ public void tick() { } } + @Override + public boolean isInvulnerableTo(DamageSource pSource) { + return pSource.is(ModTags.DamageTypes.REMAINS_INVULNERABLE) || super.isInvulnerableTo(pSource); + } + @Nullable @Override protected SoundEvent getAmbientSound() { @@ -119,12 +124,18 @@ public void move(@NotNull MoverType pType, @NotNull Vec3 pPos) { @Override public boolean hurt(@NotNull DamageSource pSource, float pAmount) { if(super.hurt(pSource, pAmount)) { - this.getDummy().ifPresent(vehicle -> vehicle.hurt(pSource, 0)); + this.getDummy().ifPresent(vehicle -> vehicle.childrenIsHurt(pSource, this.dead, getAttachFace())); return true; } return false; } + @Override + protected void actuallyHurt(@NotNull DamageSource pDamageSource, float pDamageAmount) { + super.actuallyHurt(pDamageSource, pDamageAmount); + this.invulnerableTime *= 2; + } + @Override public @NotNull Vec3 getDeltaMovement() { return Vec3.ZERO; @@ -211,7 +222,7 @@ public boolean canUse() { @Override public void start() { - this.attackTime = 20; + this.attackTime = 40; } @Override @@ -235,7 +246,6 @@ public void tick() { if (this.attackTime <= 0) { this.attackTime = 20 + RemainsDefenderEntity.this.random.nextInt(10) * 20 / 2; Vec3 position = RemainsDefenderEntity.this.position(); -// Vec3 direction = position.add(0, livingentity.getBbHeight() * 0.6f, 0).subtract(RemainsDefenderEntity.this.getEyePosition(1f)).normalize(); Vec3 direction = RemainsDefenderEntity.this.getViewVector(1.0f); var projectile = new DarkBloodProjectileEntity(RemainsDefenderEntity.this.level(), position.x(), position.y(), position.z(), direction.x(), direction.y(), direction.z()); projectile.setOwner(RemainsDefenderEntity.this); diff --git a/src/main/java/de/teamlapen/vampirism/entity/VulnerableRemainsDummyEntity.java b/src/main/java/de/teamlapen/vampirism/entity/VulnerableRemainsDummyEntity.java index 848fab0512..46cb5b3bc6 100644 --- a/src/main/java/de/teamlapen/vampirism/entity/VulnerableRemainsDummyEntity.java +++ b/src/main/java/de/teamlapen/vampirism/entity/VulnerableRemainsDummyEntity.java @@ -5,10 +5,11 @@ import de.teamlapen.vampirism.core.ModBlocks; import de.teamlapen.vampirism.core.ModDamageTypes; import de.teamlapen.vampirism.core.ModEntities; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.entity.*; @@ -28,6 +29,7 @@ public class VulnerableRemainsDummyEntity extends LivingEntity implements IEntityLeader, IRemainsEntity { private BlockPos ownerPos = null; + private Object2IntMap delayRespawn = new Object2IntOpenHashMap<>(); public VulnerableRemainsDummyEntity(EntityType type, Level pLevel) { super(type, pLevel); @@ -57,16 +59,24 @@ public void setDeltaMovement(@NotNull Vec3 pMotion) { } @Override - public boolean hurt(@NotNull DamageSource pSource, float pAmount) { + protected void actuallyHurt(@NotNull DamageSource pDamageSource, float pDamageAmount) { getTile().ifPresent(vr -> { - vr.onDamageDealt(pSource, pAmount); + vr.onDamageDealt(pDamageSource, pDamageAmount); }); - return false; + } + + public void childrenIsHurt(DamageSource damageSource, boolean killed, Direction direction) { + getTile().ifPresent(vr -> { + vr.onDamageDealt(damageSource, 0); + }); + if (killed) { + this.delayRespawn.put(direction, 20 * 5); + } } @Override public boolean isInvulnerableTo(@NotNull DamageSource pSource) { - return this.isRemoved() || pSource.is(DamageTypes.ON_FIRE) || pSource.is(ModDamageTypes.HOLY_WATER) || pSource.is(DamageTypes.FREEZE); + return this.isRemoved() || pSource.is(DamageTypes.ON_FIRE)|| pSource.is(DamageTypes.IN_FIRE) || pSource.is(ModDamageTypes.HOLY_WATER) || pSource.is(DamageTypes.FREEZE); } @Override @@ -105,6 +115,9 @@ public void setOwnerLocation(BlockPos pos) { @Override public void tick() { + this.noPhysics = true; + super.tick(); + this.noPhysics = false; if (!this.level().isClientSide) { BlockState block = this.level().getBlockState(ownerPos); if(this.ownerPos == null || !block.is(ModBlocks.ACTIVE_VULNERABLE_REMAINS.get()) || block.is(ModBlocks.INCAPACITATED_VULNERABLE_REMAINS.get())) { @@ -121,14 +134,22 @@ public void tick() { public void spawnDefender() { BlockPos pos = this.ownerPos; - List directionStream = Arrays.stream(Direction.values()).filter(l -> { + List directionStream = Arrays.stream(Direction.values()).filter(direction -> this.delayRespawn.getOrDefault(direction, 0) <= 0).filter(l -> { var block = this.level().getBlockState(pos.relative(l)).canBeReplaced(); return block && !hasDefender(l); }).toList(); if (!directionStream.isEmpty()) { spawnDefender(directionStream.get(this.random.nextInt(directionStream.size()))); } + } + public void spawnDefenders() { + this.delayRespawn.clear(); + BlockPos pos = this.ownerPos; + Arrays.stream(Direction.values()).filter(l -> { + var block = this.level().getBlockState(pos.relative(l)).canBeReplaced(); + return block && !hasDefender(l); + }).forEach(this::spawnDefender); } private boolean hasDefender(Direction direction) { @@ -174,10 +195,8 @@ public void addAdditionalSaveData(@NotNull CompoundTag pCompound) { @Override public void readAdditionalSaveData(@NotNull CompoundTag pCompound) { - if (pCompound.contains("ownerPos", Tag.TAG_INT_ARRAY)) { - int[] pos = pCompound.getIntArray("ownerPos"); - this.ownerPos = new BlockPos(pos[0], pos[1], pos[2]); - } + int[] pos = pCompound.getIntArray("ownerPos"); + this.ownerPos = new BlockPos(pos[0], pos[1], pos[2]); } private int followerCount = 0; diff --git a/src/main/java/de/teamlapen/vampirism/entity/ai/goals/DefendLeaderGoal.java b/src/main/java/de/teamlapen/vampirism/entity/ai/goals/DefendLeaderGoal.java index 5b5bf4f46b..22edd25cef 100644 --- a/src/main/java/de/teamlapen/vampirism/entity/ai/goals/DefendLeaderGoal.java +++ b/src/main/java/de/teamlapen/vampirism/entity/ai/goals/DefendLeaderGoal.java @@ -1,8 +1,9 @@ package de.teamlapen.vampirism.entity.ai.goals; import de.teamlapen.vampirism.api.entity.IEntityLeader; -import de.teamlapen.vampirism.entity.vampire.BasicVampireEntity; +import de.teamlapen.vampirism.entity.IEntityFollower; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.goal.target.TargetGoal; import net.minecraft.world.entity.ai.targeting.TargetingConditions; @@ -11,19 +12,19 @@ import java.util.EnumSet; -public class DefendLeaderGoal extends TargetGoal { - private final @NotNull BasicVampireEntity entity; +public class DefendLeaderGoal extends TargetGoal { + private final @NotNull T entity; private @Nullable LivingEntity attacker; private int timestamp; - public DefendLeaderGoal(@NotNull BasicVampireEntity basicVampire) { - super(basicVampire, false); - this.entity = basicVampire; + public DefendLeaderGoal(@NotNull T mob) { + super(mob, false); + this.entity = mob; this.setFlags(EnumSet.of(Goal.Flag.TARGET)); } public boolean canUse() { - IEntityLeader leader = this.entity.getAdvancedLeader(); + IEntityLeader leader = this.entity.getLeader(); if (leader == null) { return false; } else { @@ -36,7 +37,7 @@ public boolean canUse() { public void start() { this.mob.setTarget(this.attacker); - IEntityLeader leader = this.entity.getAdvancedLeader(); + IEntityLeader leader = this.entity.getLeader(); if (leader != null) { this.timestamp = leader.getRepresentingEntity().getLastHurtByMobTimestamp(); } diff --git a/src/main/java/de/teamlapen/vampirism/entity/ai/goals/FindLeaderGoal.java b/src/main/java/de/teamlapen/vampirism/entity/ai/goals/FindLeaderGoal.java new file mode 100644 index 0000000000..50316414a2 --- /dev/null +++ b/src/main/java/de/teamlapen/vampirism/entity/ai/goals/FindLeaderGoal.java @@ -0,0 +1,53 @@ +package de.teamlapen.vampirism.entity.ai.goals; + +import de.teamlapen.vampirism.api.entity.IEntityLeader; +import de.teamlapen.vampirism.entity.IEntityFollower; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; +import net.minecraft.world.phys.AABB; + +import java.util.function.Predicate; + +public class FindLeaderGoal extends Goal { + + private final T entity; + protected TargetingConditions targetConditions; + private Z leader; + protected final int randomInterval = reducedTickDelay(10); + + public FindLeaderGoal(T entity, Predicate leaderPredicate) { + this.entity = entity; + this.targetConditions = TargetingConditions.forNonCombat().ignoreLineOfSight().range(this.getFollowDistance()).selector((Predicate)(Object) leaderPredicate); + } + + protected double getFollowDistance() { + return this.entity.getAttributeValue(Attributes.FOLLOW_RANGE); + } + + @Override + public boolean canUse() { + if ((this.entity.getLeader() != null && this.entity.getLeader().isAlive()) || (this.randomInterval > 0 && this.entity.getRandom().nextInt(this.randomInterval) != 0)) { + return false; + } else { + this.findLeader(); + return this.leader != null; + } + } + + @Override + public void start() { + this.entity.setLeader(this.leader); + super.start(); + } + + protected AABB getTargetSearchArea(double pTargetDistance) { + return this.entity.getBoundingBox().inflate(pTargetDistance, 4.0D, pTargetDistance); + } + + protected void findLeader() { + this.leader = (Z) this.entity.level().getNearestEntity(this.entity.level().getEntitiesOfClass(LivingEntity.class, getTargetSearchArea(getFollowDistance()), IEntityLeader.class::isInstance), this.targetConditions, this.entity, this.entity.getX(), this.entity.getY(), this.entity.getZ()); + } +} diff --git a/src/main/java/de/teamlapen/vampirism/entity/ai/goals/NearestTargetGoalModifier.java b/src/main/java/de/teamlapen/vampirism/entity/ai/goals/NearestTargetGoalModifier.java index df1c83c30d..a708bc4d5a 100644 --- a/src/main/java/de/teamlapen/vampirism/entity/ai/goals/NearestTargetGoalModifier.java +++ b/src/main/java/de/teamlapen/vampirism/entity/ai/goals/NearestTargetGoalModifier.java @@ -5,4 +5,6 @@ public interface NearestTargetGoalModifier { void ignoreVampires(); void ignoreFactionEntities(); + + void ignoreLineOfSight(); } diff --git a/src/main/java/de/teamlapen/vampirism/entity/vampire/BasicVampireEntity.java b/src/main/java/de/teamlapen/vampirism/entity/vampire/BasicVampireEntity.java index e8f5a5b214..9b744d4448 100644 --- a/src/main/java/de/teamlapen/vampirism/entity/vampire/BasicVampireEntity.java +++ b/src/main/java/de/teamlapen/vampirism/entity/vampire/BasicVampireEntity.java @@ -18,6 +18,7 @@ import de.teamlapen.vampirism.core.ModItems; import de.teamlapen.vampirism.core.ModSounds; import de.teamlapen.vampirism.effects.BadOmenEffect; +import de.teamlapen.vampirism.entity.IEntityFollower; import de.teamlapen.vampirism.entity.action.ActionHandlerEntity; import de.teamlapen.vampirism.entity.ai.goals.*; import de.teamlapen.vampirism.entity.factions.FactionPlayerHandler; @@ -63,7 +64,7 @@ * Basic vampire mob. * Follows nearby advanced vampire */ -public class BasicVampireEntity extends VampireBaseEntity implements IBasicVampire, IEntityActionUser { +public class BasicVampireEntity extends VampireBaseEntity implements IBasicVampire, IEntityActionUser, IEntityFollower { private static final EntityDataAccessor LEVEL = SynchedEntityData.defineId(BasicVampireEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor TYPE = SynchedEntityData.defineId(BasicVampireEntity.class, EntityDataSerializers.INT); @@ -216,6 +217,22 @@ public void setAdvancedLeader(@Nullable IEntityLeader advancedLeader) { this.advancedLeader = advancedLeader; } + @Override + public boolean isFollowing() { + return this.advancedLeader != null; + } + + @SuppressWarnings("unchecked") + @Override + public T getLeader() { + return (T) this.advancedLeader; + } + + @Override + public void setLeader(T leader) { + this.advancedLeader = leader; + } + @Nullable @Override public ICaptureAttributes getCaptureInfo() { diff --git a/src/main/java/de/teamlapen/vampirism/mixin/NearestAttackableTargetGoalMixin.java b/src/main/java/de/teamlapen/vampirism/mixin/NearestAttackableTargetGoalMixin.java index 0f75be7f69..a47cd933ba 100644 --- a/src/main/java/de/teamlapen/vampirism/mixin/NearestAttackableTargetGoalMixin.java +++ b/src/main/java/de/teamlapen/vampirism/mixin/NearestAttackableTargetGoalMixin.java @@ -8,6 +8,7 @@ import net.minecraft.world.entity.ai.targeting.TargetingConditions; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import java.util.function.Predicate; @@ -15,7 +16,9 @@ public class NearestAttackableTargetGoalMixin implements NearestTargetGoalModifier { @Shadow protected TargetingConditions targetConditions; + @Unique private static final Predicate nonVampireCheck = entity -> !Helper.isVampire(entity); + @Unique private static final Predicate noFactionEntityCheck = entity -> !(entity instanceof IFactionEntity); @Override @@ -35,4 +38,9 @@ public void ignoreFactionEntities() { } this.targetConditions.selector(predicate); } + + @Override + public void ignoreLineOfSight() { + this.targetConditions.ignoreLineOfSight(); + } } diff --git a/src/main/java/de/teamlapen/vampirism/particle/FlyingBloodParticleOptions.java b/src/main/java/de/teamlapen/vampirism/particle/FlyingBloodParticleOptions.java index 5e5c0059fe..e0895a8117 100644 --- a/src/main/java/de/teamlapen/vampirism/particle/FlyingBloodParticleOptions.java +++ b/src/main/java/de/teamlapen/vampirism/particle/FlyingBloodParticleOptions.java @@ -14,7 +14,7 @@ import net.minecraftforge.registries.ForgeRegistries; import org.jetbrains.annotations.NotNull; -public record FlyingBloodParticleOptions(int maxAge, boolean direct, double targetX, double targetY, double targetZ, ResourceLocation texture ) implements ParticleOptions { +public record FlyingBloodParticleOptions(int maxAge, boolean direct, double targetX, double targetY, double targetZ, ResourceLocation texture, float scale) implements ParticleOptions { /** * CODEC appears to be an alternative to De/Serializer. Not sure why both exist @@ -26,23 +26,32 @@ public record FlyingBloodParticleOptions(int maxAge, boolean direct, double targ Codec.DOUBLE.fieldOf("x").forGetter((p_239805_0_) -> p_239805_0_.targetX), Codec.DOUBLE.fieldOf("y").forGetter((p_239804_0_) -> p_239804_0_.targetY), Codec.DOUBLE.fieldOf("z").forGetter((p_239804_0_) -> p_239804_0_.targetZ), - Codec.STRING.fieldOf("t").forGetter((p_239804_0_) -> p_239804_0_.texture.toString())) - .apply(p_239803_0_, (a, d, x, y, z, t) -> new FlyingBloodParticleOptions(a, d, x, y, z, new ResourceLocation(t)))); + Codec.STRING.fieldOf("t").forGetter((p_239804_0_) -> p_239804_0_.texture.toString()), + Codec.FLOAT.fieldOf("s").forGetter((p_239804_0_) -> p_239804_0_.scale) + ).apply(p_239803_0_, (a, d, x, y, z, t, s) -> new FlyingBloodParticleOptions(a, d, x, y, z, new ResourceLocation(t), s))); public static final ParticleOptions.Deserializer DESERIALIZER = new ParticleOptions.Deserializer<>() { @NotNull public FlyingBloodParticleOptions fromCommand(@NotNull ParticleType particleTypeIn, @NotNull StringReader reader) throws CommandSyntaxException { - return new FlyingBloodParticleOptions(reader.readInt(), reader.readBoolean(), reader.readDouble(), reader.readDouble(), reader.readDouble(), ResourceLocation.read(reader)); + return new FlyingBloodParticleOptions(reader.readInt(), reader.readBoolean(), reader.readDouble(), reader.readDouble(), reader.readDouble(), ResourceLocation.read(reader), reader.readFloat()); } @NotNull public FlyingBloodParticleOptions fromNetwork(@NotNull ParticleType particleTypeIn, @NotNull FriendlyByteBuf buffer) { - return new FlyingBloodParticleOptions(buffer.readVarInt(), buffer.readBoolean(), buffer.readDouble(), buffer.readDouble(), buffer.readDouble(), buffer.readResourceLocation()); + return new FlyingBloodParticleOptions(buffer.readVarInt(), buffer.readBoolean(), buffer.readDouble(), buffer.readDouble(), buffer.readDouble(), buffer.readResourceLocation(), buffer.readFloat()); } }; public FlyingBloodParticleOptions(int maxAgeIn, boolean direct, double targetX, double targetY, double targetZ) { - this(maxAgeIn, direct, targetX, targetY, targetZ, new ResourceLocation("minecraft", "critical_hit")); + this(maxAgeIn, direct, targetX, targetY, targetZ, 1f); + } + + public FlyingBloodParticleOptions(int maxAgeIn, boolean direct, double targetX, double targetY, double targetZ, float scale) { + this(maxAgeIn, direct, targetX, targetY, targetZ, new ResourceLocation("minecraft", "critical_hit"), scale); + } + + public FlyingBloodParticleOptions(int maxAge, boolean direct, double targetX, double targetY, double targetZ, ResourceLocation texture) { + this(maxAge, direct, targetX, targetY, targetZ, texture, 1f); } @OnlyIn(Dist.CLIENT) @@ -58,6 +67,7 @@ public void writeToNetwork(@NotNull FriendlyByteBuf buffer) { buffer.writeDouble(targetY); buffer.writeDouble(targetZ); buffer.writeResourceLocation(texture); + buffer.writeFloat(scale); } @NotNull diff --git a/src/main/resources/assets/vampirism/lang/en_us.json b/src/main/resources/assets/vampirism/lang/en_us.json index 31e7b1e11a..d2b60b5d5f 100644 --- a/src/main/resources/assets/vampirism/lang/en_us.json +++ b/src/main/resources/assets/vampirism/lang/en_us.json @@ -229,6 +229,8 @@ "advancement.vampirism.collect_blood.desc": "Send your Minions to collect Blood", "advancement.vampirism.cure_vampire_villager": "Vampire Doctor", "advancement.vampirism.cure_vampire_villager.desc": "Cure a Vampire Villager", + "advancement.vampirism.jump_scare": "Jump Scare", + "advancement.vampirism.jump_scare.desc": "Find out how it is to be scared to death", "__comment": "effect", "effect.vampirism.sunscreen": "Sunscreen", "effect.vampirism.thirst": "Thirst", @@ -741,7 +743,7 @@ "block.vampirism.vulnerable_remains": "Vulnerable Remains", "block.vampirism.incapacitated_vulnerable_remains": "Remains", "block.vampirism.active_vulnerable_remains": "Vulnerable Remains", - "block.vampirism.mother": "Mother", + "block.vampirism.mother": "Ancient Remains", "__comment": "items", "item.vampirism.hunter_intel": "Hunter Intel", "item.vampirism.pure_blood": "Pure Blood", diff --git a/src/main/resources/assets/vampirism/textures/entity/ghost.png b/src/main/resources/assets/vampirism/textures/entity/ghost.png index ce491acc8056823a8037d39ee59d67c748a0ac35..d662aa75d2f2911919eb5668451adb883810a67e 100755 GIT binary patch literal 4955 zcmeHLdsGuw8lRvD<{U&<2l>^oaE-dzVG*a z_jkYhB{yHHV0QGiI_@M0r>MBsk zwLP{@fj0fPJ6{)FAjli^`szrfO3mVP5FSS$1o}L_07ZBxB4r^WR47D4LV$fn<$y;t zlJG`F8}KimjEGPxBO+L4i%Cc72?(-pUtT1OIz7Yx%;I=OaN^pGk+DCj%i9zR|I|_I z)qz)-HR#rbuUu7r#Xe&q8;-QMH)@(2&dg7COAa6T8cIt~i=&FXx=TkdaM;biY_$D* zyzLRRGZy>0+1pKcUDWjF{H+&i)-T`h&dLv$Ba;N@w{H5QPiW&+Sx(N$4Ns;$vaTp( zgqV=}$zF}yur|k7hoJ0F4Ve{^zeFF5&WZ64`TI)8#)ZiXUY|XF?_j;)v~H4YuK%Rb z8y4-Okg41Jo(R+A&D6Ou2jL{CDXnEnNXx0oD=^;v{nM=FcY})OIlLyWQ>!CB9`*CO zzgBJ11gg4*f7HG1e(8_9zN+?^Rp^?qGd$x>`-;c4pH=fhTBDhha&L@jeCg}uK9q#I4obWWF#>r>(LV%u(y_BNND3@!q**G=<$7D(2B2uZ8%j0wT zd>BZ;)=VRX*Z zcFfF0I6SVwz#Xh%r4$(eq}QQ$YFHC8%>*}&u$s~>IHAZOjMRj|5L&!n-<)pIJC~!y zxrClD08uNjio9i#K5_jTGzBT7!R*umWWOazk-7o0-tvu}aV}@DBS5_$_butZwL67@ zluCulO?W!(o>DGj)AmuV2`9Cvv&qv630y4Y!HAeoz(NGm!BR}4fsqg$f=Do-M5hfI z1f?`uDa?oyG!y{mkN`&{67Y41R19MxNeCjN=$40C6Feh{do_gbQIQU&DuWxJWACX>>Y~Q0MeUi=(ql76S&hlQdu{ z1lMd#aZb>Lqv2|$jLqlp21e9+jM4!G8G8$1&)&z zqcAx}5dgG54=42H;i)`6%Ik5ThEtieq%QM+Nz;di6$<3RmPe6RU_aA2)N`WZiL{V<35fT*&QDhP|B8Y3Db5diUZt zv;d$#7Z#y@WqR*9W@Zi-GqN{t#XNH@cVu*D1mXeg)aUWocRDN6p}( zHB2)*QVw;~uga5qHi41h=BW8r2=Y8l7e=FLFSx2fZj@4`aO+@tdrp&FnOFfXX)Z66 z^6&(ETjy3;>a`iZjor55nLe#9d4a3K5}$JdXHSpaQXIJCV9A`%pE3?^xm>tvn~P@mgeP!*y$SheEUW)`JInhjeX6CIsOghg&W!_`|s` zva((chaLNCZ)J|px#`y7Ux4`Hm77<_Rb9T3v}|0x)UD$eLsGCS?ooi0JijP~AYa^g z=&UdAtY4=gUwr-W6L?pCOItZrelnh^j?S}s27lsuO>`W^-cP^Z!gHe(C1w`_5W zN#QjuB|UrPn=b+upZYbo#SeP?vTLp*t>DB<)$?-OjKriciKPB$Wrx?_0%}GoVmqpU zo#rDgFJxsbHM0PD?d4s$lS$Q}BZodge$FaK!@>ee$^x$iwmkvQhEGr5zkfe=N5{I~ zyJzmWeed4A=0&@^hm|*9uIA@XzFo@SHm7p)%vc|Ad;iQ<|Bvc}iMiwI)=X5Ylj>v8 z%Bqj2Y!u(QdsnxaXuZ59`{ardZOzTi_m13ZH%++PneMxW<54^@yzb`OMX%qyk^R7P z+_`fn%Kg@kFsYPW_@cA3CbeqcKdbeRs&aWAf%#_7tucF|ntaHwPplpm-v02~aU${x z;_q4R-AM+FA3yy-=Nxx=Z2rrfLP=poQ2Qqv469v6Z#Z-LSkU@tSD~zFnQK&V>Dlb; z>=NeEtldYL-yL|+^=ljR@$!JU=u&UAXvn#-IWdgzkyIo@wB`JsmyaKV|7LP>wma#C?GL^Wa^!~q(nyTaY-RkcHz?ff3bEsGzZKTO`{ul5^yXar6NZD?aU=B{{tJQ B0aO40 delta 938 zcmV;b16BOnCg}%|B$E^nEq~tu7Xk(bW3QAE000AaNklg zEfBb5a9SvIKj{B)Lzde^mJHoWr*c!d>DH2^WXO`?;K*=f$#Rr~j4kDEILg6++mM~Hce={HPzsPzf^!Zb1Q=sbO4%GxX0I!3+H60q{cEod zZdJDL_xniG6zz5!jDIl<1_KO-Lo6>ZFI08lquJi`JUEU6&N-4K!NI|SweRlkB1sZd z&9Ac8A_Oc_0w}GuwhYybc0DrIEyZ`_^`sKk)H{99T z(WA|f=@89$V`F3H+IV<)sM9n>5Cllm6zl8jbJtn-3v^avugU-}jc29T>p@D1JkPJK z!ni+v{}b-sx`#hr|Bk!2?%})J_tA|{@beE(@w)pro;`m$oun?jDjNlu@jOIPgi!@t ziv!|8jJLfG%zyXWUI#)JC}e?^rC{nCmNi48<7qiMI?_p!j4im)_|%~7@9*n*!@&)g zL##!2P>39B(H*Qsx6zGH@b1G2{!89pDdx?w@B20kq9{VM*@Suc)6-M2o`~yI%)k3G! zL6&96vJ6s6v|6q4AO1fN+_(a+qo2N5;*4XA%*%G@s(GN>?b