diff --git a/gradle.properties b/gradle.properties index 3ef01c00ac9..473d7393e7d 100755 --- a/gradle.properties +++ b/gradle.properties @@ -30,7 +30,7 @@ mappingsChannel=official mappingsVersion=1.20.1 dataGeneratorsVersion=1.19.3-0.1.54-ALPHA -blockUI_version=1.20.1-1.0.139-BETA +blockUI_version=1.20.1-1.0.155-BETA structurize_version=1.20.1-1.0.759-snapshot domumOrnamentumVersion=1.20.1-1.0.184-BETA multiPistonVersion=1.20-1.2.30-ALPHA diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/blaze.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/blaze.json new file mode 100644 index 00000000000..8e85d096993 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/blaze.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 6.0, + "entity-type": "minecraft:blaze", + "health": 20.0, + "loot-table": "minecraft:entities/blaze", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/cave_spider.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/cave_spider.json new file mode 100644 index 00000000000..18a999a1153 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/cave_spider.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 4.0, + "entity-type": "minecraft:cave_spider", + "health": 12.0, + "loot-table": "minecraft:entities/cave_spider", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/creeper.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/creeper.json new file mode 100644 index 00000000000..0b3afb256a1 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/creeper.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 22.0, + "entity-type": "minecraft:creeper", + "health": 20.0, + "loot-table": "minecraft:entities/creeper", + "reflect": 20.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/drowned.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/drowned.json new file mode 100644 index 00000000000..6eb832363f8 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/drowned.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 3.0, + "entity-type": "minecraft:drowned", + "health": 20.0, + "loot-table": "minecraft:entities/drowned", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/drowned_trident.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/drowned_trident.json new file mode 100644 index 00000000000..cc3d1a9eb17 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/drowned_trident.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 11.0, + "entity-type": "minecraft:drowned", + "health": 20.0, + "loot-table": "minecraft:entities/drowned", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/elder_guardian.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/elder_guardian.json new file mode 100644 index 00000000000..d6061ada4b7 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/elder_guardian.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 8.0, + "entity-type": "minecraft:elder_guardian", + "health": 80.0, + "loot-table": "minecraft:entities/elder_guardian", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/enderman.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/enderman.json new file mode 100644 index 00000000000..99e3aad7dd4 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/enderman.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 7.0, + "entity-type": "minecraft:enderman", + "health": 40.0, + "loot-table": "minecraft:entities/enderman", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/evoker.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/evoker.json new file mode 100644 index 00000000000..7cb767c1325 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/evoker.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 6.0, + "entity-type": "minecraft:evoker", + "health": 24.0, + "loot-table": "minecraft:entities/evoker", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/guardian.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/guardian.json new file mode 100644 index 00000000000..313d8ddc55d --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/guardian.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 6.0, + "entity-type": "minecraft:guardian", + "health": 30.0, + "loot-table": "minecraft:entities/guardian", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/hoglin.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/hoglin.json new file mode 100644 index 00000000000..2a8c2092b20 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/hoglin.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 8.0, + "entity-type": "minecraft:hoglin", + "health": 40.0, + "loot-table": "minecraft:entities/hoglin", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_large.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_large.json new file mode 100644 index 00000000000..fed913fa0e4 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_large.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 6.0, + "entity-type": "minecraft:magma_cube", + "health": 16.0, + "loot-table": "minecraft:entities/magma_cube", + "reflect": 0.0, + "xp": 4.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_medium.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_medium.json new file mode 100644 index 00000000000..ce42270324d --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_medium.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 4.0, + "entity-type": "minecraft:magma_cube", + "health": 4.0, + "loot-table": "minecraft:entities/magma_cube", + "reflect": 0.0, + "xp": 2.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_small.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_small.json new file mode 100644 index 00000000000..667a2d90d57 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/magma_cube_small.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 3.0, + "entity-type": "minecraft:magma_cube", + "health": 1.0, + "loot-table": "minecraft:entities/magma_cube", + "reflect": 0.0, + "xp": 1.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/piglin.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/piglin.json new file mode 100644 index 00000000000..661dc15fa1a --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/piglin.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 8.0, + "entity-type": "minecraft:piglin", + "health": 16.0, + "loot-table": "minecraft:entities/piglin", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/piglin_brute.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/piglin_brute.json new file mode 100644 index 00000000000..0626147253d --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/piglin_brute.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 13.0, + "entity-type": "minecraft:piglin_brute", + "health": 50.0, + "loot-table": "minecraft:entities/piglin_brute", + "reflect": 0.0, + "xp": 20.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/pillager.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/pillager.json new file mode 100644 index 00000000000..4f164007aba --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/pillager.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 8.0, + "entity-type": "minecraft:pillager", + "health": 80.0, + "loot-table": "minecraft:entities/pillager", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/pillager_captain.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/pillager_captain.json new file mode 100644 index 00000000000..4f164007aba --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/pillager_captain.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 8.0, + "entity-type": "minecraft:pillager", + "health": 80.0, + "loot-table": "minecraft:entities/pillager", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/shulker.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/shulker.json new file mode 100644 index 00000000000..84a5042aaee --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/shulker.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 4.0, + "entity-type": "minecraft:shulker", + "health": 30.0, + "loot-table": "minecraft:entities/shulker", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/skeleton.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/skeleton.json new file mode 100644 index 00000000000..efb244cd106 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/skeleton.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 3.0, + "entity-type": "minecraft:skeleton", + "health": 20.0, + "loot-table": "minecraft:entities/skeleton", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/spider.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/spider.json new file mode 100644 index 00000000000..0958652fe0d --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/spider.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 2.0, + "entity-type": "minecraft:spider", + "health": 16.0, + "loot-table": "minecraft:entities/spider", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/vex.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/vex.json new file mode 100644 index 00000000000..82bddd145c3 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/vex.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 9.0, + "entity-type": "minecraft:vex", + "health": 14.0, + "loot-table": "minecraft:entities/vex", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/vindicator.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/vindicator.json new file mode 100644 index 00000000000..84586b953af --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/vindicator.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 13.0, + "entity-type": "minecraft:vindicator", + "health": 24.0, + "loot-table": "minecraft:entities/vindicator", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/warden.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/warden.json new file mode 100644 index 00000000000..529a57a1877 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/warden.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 30.0, + "entity-type": "minecraft:warden", + "health": 500.0, + "loot-table": "minecraft:entities/warden", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/witch.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/witch.json new file mode 100644 index 00000000000..beda3e0ad89 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/witch.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 4.0, + "entity-type": "minecraft:witch", + "health": 26.0, + "loot-table": "minecraft:entities/witch", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/wither_skeleton.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/wither_skeleton.json new file mode 100644 index 00000000000..134092c7210 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/wither_skeleton.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 11.0, + "entity-type": "minecraft:wither_skeleton", + "health": 20.0, + "loot-table": "minecraft:entities/wither_skeleton", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/zombie.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/zombie.json new file mode 100644 index 00000000000..01faac520cc --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/zombie.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 2.5, + "entity-type": "minecraft:zombie", + "health": 20.0, + "loot-table": "minecraft:entities/zombie", + "reflect": 0.0, + "xp": 5.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/zombified_piglin.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/zombified_piglin.json new file mode 100644 index 00000000000..cbdd68e346a --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_encounters/zombified_piglin.json @@ -0,0 +1,9 @@ +{ + "armor": 0, + "damage": 11.0, + "entity-type": "minecraft:zombified_piglin", + "health": 20.0, + "loot-table": "minecraft:entities/zombified_piglin", + "reflect": 0.0, + "xp": 10.0 +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/end_hard.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/end_hard.json new file mode 100644 index 00000000000..cb76b64928d --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/end_hard.json @@ -0,0 +1,49 @@ +{ + "difficulty": "hard", + "dimension": "minecraft:the_end", + "guards": 6, + "loot-table": "minecolonies:expeditions/expedition_end", + "name": "com.minecolonies.core.expedition_types.end.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 128 + } + ], + "to-text": "com.minecolonies.core.expedition_types.end.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/end_medium.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/end_medium.json new file mode 100644 index 00000000000..a651b9b84e5 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/end_medium.json @@ -0,0 +1,49 @@ +{ + "difficulty": "medium", + "dimension": "minecraft:the_end", + "guards": 4, + "loot-table": "minecolonies:expeditions/expedition_end", + "name": "com.minecolonies.core.expedition_types.end.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 64 + } + ], + "to-text": "com.minecolonies.core.expedition_types.end.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_easy.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_easy.json new file mode 100644 index 00000000000..cc7390357a7 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_easy.json @@ -0,0 +1,49 @@ +{ + "difficulty": "easy", + "dimension": "minecraft:the_nether", + "guards": 2, + "loot-table": "minecolonies:expeditions/expedition_nether", + "name": "com.minecolonies.core.expedition_types.nether.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 32 + } + ], + "to-text": "com.minecolonies.core.expedition_types.nether.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_hard.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_hard.json new file mode 100644 index 00000000000..f2bec70f4e9 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_hard.json @@ -0,0 +1,49 @@ +{ + "difficulty": "hard", + "dimension": "minecraft:the_nether", + "guards": 6, + "loot-table": "minecolonies:expeditions/expedition_nether", + "name": "com.minecolonies.core.expedition_types.nether.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 128 + } + ], + "to-text": "com.minecolonies.core.expedition_types.nether.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_medium.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_medium.json new file mode 100644 index 00000000000..b9dd9661dac --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/nether_medium.json @@ -0,0 +1,49 @@ +{ + "difficulty": "medium", + "dimension": "minecraft:the_nether", + "guards": 4, + "loot-table": "minecolonies:expeditions/expedition_nether", + "name": "com.minecolonies.core.expedition_types.nether.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 64 + } + ], + "to-text": "com.minecolonies.core.expedition_types.nether.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_easy.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_easy.json new file mode 100644 index 00000000000..5b3a32ecbbf --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_easy.json @@ -0,0 +1,49 @@ +{ + "difficulty": "easy", + "dimension": "minecraft:overworld", + "guards": 1, + "loot-table": "minecolonies:expeditions/expedition_overworld", + "name": "com.minecolonies.core.expedition_types.overworld.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 32 + } + ], + "to-text": "com.minecolonies.core.expedition_types.overworld.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_hard.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_hard.json new file mode 100644 index 00000000000..2d8dd42d1ff --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_hard.json @@ -0,0 +1,49 @@ +{ + "difficulty": "hard", + "dimension": "minecraft:overworld", + "guards": 4, + "loot-table": "minecolonies:expeditions/expedition_overworld", + "name": "com.minecolonies.core.expedition_types.overworld.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 128 + } + ], + "to-text": "com.minecolonies.core.expedition_types.overworld.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_medium.json b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_medium.json new file mode 100644 index 00000000000..68347028af6 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/colony/expedition_types/overworld_medium.json @@ -0,0 +1,49 @@ +{ + "difficulty": "medium", + "dimension": "minecraft:overworld", + "guards": 2, + "loot-table": "minecolonies:expeditions/expedition_overworld", + "name": "com.minecolonies.core.expedition_types.overworld.name", + "requirements": [ + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:sword" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:pickaxe" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:shovel" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:helmet" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:chestplate" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:leggings" + }, + { + "type": "equipment", + "amount": 1, + "equipment": "minecolonies:boots" + }, + { + "type": "food", + "amount": 64 + } + ], + "to-text": "com.minecolonies.core.expedition_types.overworld.to_text" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_end.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_end.json new file mode 100644 index 00000000000..45aefb58e74 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_end.json @@ -0,0 +1,120 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:end_stone", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:obsidian", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:chorus_plant", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:chorus_flower", + "weight": 100 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/end_city", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/bastion_remnant", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:enderman\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 20.0, + "min": 10.0 + } + } + ], + "random_sequence": "minecolonies:expeditions/expedition_end" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_nether.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_nether.json new file mode 100644 index 00000000000..0b0e2d52047 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_nether.json @@ -0,0 +1,571 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:netherrack", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:blackstone", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:basalt", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:soul_sand", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:soul_soil", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gravel", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:crimson_nylium", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:warped_nylium", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:magma_block", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:obsidian", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_nugget", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:quartz", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:glowstone", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:ancient_debris", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:brown_mushroom", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:red_mushroom", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:crimson_fungus", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:warped_fungus", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:crimson_stem", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:warped_stem", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:shroomlight", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:twisting_vines", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:weeping_vines", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/nether_fossil", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/fortress", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/bastion_remnant", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombified_piglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:piglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:hoglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:wither_skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:blaze\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 20.0, + "min": 10.0 + } + } + ], + "random_sequence": "minecolonies:expeditions/expedition_nether" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_overworld.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_overworld.json new file mode 100644 index 00000000000..ddab4b56549 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/expedition_overworld.json @@ -0,0 +1,1024 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:oak_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:birch_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:spruce_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:dark_oak_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:acacia_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cherry_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:mangrove_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:coal", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:raw_copper", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:raw_iron", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:raw_gold", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:redstone", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:lapis_lazuli", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:poppy", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:rose_bush", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:dandelion", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:dandelion", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cornflower", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:azure_bluet", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:oxeye_daisy", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:lily_of_the_valley", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:red_tulip", + "weight": 80 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:pink_tulip", + "weight": 80 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:orange_tulip", + "weight": 80 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:white_tulip", + "weight": 80 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:sunflower", + "weight": 80 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:torchflower", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:allium", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:blue_orchid", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:pitcher_plant", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:brown_mushroom", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:red_mushroom", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/village_desert", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/village_plains", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/village_savanna", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/village_snowy", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/village_taiga", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/igloo", + "weight": 50 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/ruined_portal", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/pillager_outpost", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/desert_pyramid", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/jungle_temple", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "easy", + "medium" + ] + } + ], + "name": "minecolonies:expeditions/structures/swamp_hut", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/mineshaft", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/shipwreck", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/buried_treasure", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/stronghold", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/monument", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/mansion", + "weight": 25 + }, + { + "type": "minecraft:loot_table", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecolonies:expeditions/structures/ancient_city", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombie\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:creeper\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:spider\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:cave_spider\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:enderman\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 15 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 20.0, + "min": 10.0 + } + } + ], + "random_sequence": "minecolonies:expeditions/expedition_overworld" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/ancient_city.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/ancient_city.json new file mode 100644 index 00000000000..89e201a8a9b --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/ancient_city.json @@ -0,0 +1,213 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:ancient_city\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:deepslate_brick_slab", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:book", + "weight": 100 + }, + { + "type": "minecraft:item", + "name": "minecraft:name_tag", + "weight": 75 + }, + { + "type": "minecraft:item", + "name": "minecraft:lead", + "weight": 75 + }, + { + "type": "minecraft:item", + "name": "minecraft:experience_bottle", + "weight": 40 + }, + { + "type": "minecraft:item", + "name": "minecraft:sculk", + "weight": 40 + }, + { + "type": "minecraft:item", + "name": "minecraft:sculk_sensor", + "weight": 30 + }, + { + "type": "minecraft:item", + "name": "minecraft:sculk_catalyst", + "weight": 20 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:iron_leggings", + "weight": 40 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:diamond_leggings", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:enchanted_golden_apple", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "enchantments": [ + "minecraft:mending" + ], + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:enchanted_book", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:warden\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 3.0, + "min": 1.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:ancient_city\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/ancient_city" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/bastion_remnant.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/bastion_remnant.json new file mode 100644 index 00000000000..63d91ea0728 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/bastion_remnant.json @@ -0,0 +1,196 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:bastion_remnant\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:polished_blackstone_bricks", + "weight": 100 + }, + { + "type": "minecraft:item", + "name": "minecraft:gold_block", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:crying_obsidian", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:gilded_blackstone", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 100 + }, + { + "type": "minecraft:item", + "name": "minecraft:spectral_arrow", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:netherite_upgrade_smithing_template", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:crossbow", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:piglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:piglin_brute\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 150 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:hoglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:bastion_remnant\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/bastion_remnant" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/buried_treasure.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/buried_treasure.json new file mode 100644 index 00000000000..d852db4d106 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/buried_treasure.json @@ -0,0 +1,160 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:buried_treasure\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:cooked_cod", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 75 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:diamond", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:emerald", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{Potion:\"minecraft:water_breathing\"}" + } + ], + "name": "minecraft:potion", + "weight": 15 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:heart_of_the_sea", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:drowned\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:buried_treasure\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/buried_treasure" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/desert_pyramid.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/desert_pyramid.json new file mode 100644 index 00000000000..dcf4b561c70 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/desert_pyramid.json @@ -0,0 +1,169 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:desert_pyramid\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:sandstone", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cut_sandstone", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:golden_apple", + "weight": 25 + }, + { + "type": "minecraft:item", + "name": "minecraft:diamond", + "weight": 25 + }, + { + "type": "minecraft:item", + "name": "minecraft:emerald", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:enchanted_book", + "weight": 20 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombie\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:creeper\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:desert_pyramid\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/desert_pyramid" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/end_city.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/end_city.json new file mode 100644 index 00000000000..743d49de37e --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/end_city.json @@ -0,0 +1,490 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:end_city\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:end_stone_bricks", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:purpur_block", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 200 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 200 + }, + { + "type": "minecraft:item", + "name": "minecraft:saddle", + "weight": 100 + }, + { + "type": "minecraft:item", + "name": "minecraft:diamond", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:emerald", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:iron_helmet", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:iron_chestplate", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:iron_leggings", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:iron_boots", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:iron_sword", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:iron_pickaxe", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:iron_shovel", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_helmet", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_chestplate", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_leggings", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_boots", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_sword", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_pickaxe", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_shovel", + "weight": 5 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:elytra" + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:enderman\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 200 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:shulker\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 300 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:end_city\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/end_city" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/fortress.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/fortress.json new file mode 100644 index 00000000000..a6f4c8f4934 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/fortress.json @@ -0,0 +1,203 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:fortress\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:nether_bricks", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:nether_wart", + "weight": 75 + }, + { + "type": "minecraft:item", + "name": "minecraft:obsidian", + "weight": 60 + }, + { + "type": "minecraft:item", + "name": "minecraft:diamond", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:saddle", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombified_piglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 20 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 20 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:wither_skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:blaze\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:magma_cube_large\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:magma_cube_medium\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:magma_cube_small\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 20 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:fortress\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/fortress" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/igloo.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/igloo.json new file mode 100644 index 00000000000..c11dd15f312 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/igloo.json @@ -0,0 +1,116 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:igloo\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:snow", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:ice", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:snowball", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:lamb_stew", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:emerald", + "weight": 25 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:igloo\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/igloo" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/jungle_temple.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/jungle_temple.json new file mode 100644 index 00000000000..1bfc253a2b6 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/jungle_temple.json @@ -0,0 +1,209 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:jungle_temple\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cobblestone", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:mossy_cobblestone", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cobweb", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:pita_hummus", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:pepper_hummus", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:diamond", + "weight": 35 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:emerald", + "weight": 35 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:enchanted_book", + "weight": 20 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombie\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:creeper\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:jungle_temple\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/jungle_temple" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/mansion.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/mansion.json new file mode 100644 index 00000000000..7d96fdbc951 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/mansion.json @@ -0,0 +1,224 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:mansion\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 100 + }, + { + "type": "minecraft:item", + "name": "minecraft:name_tag", + "weight": 75 + }, + { + "type": "minecraft:item", + "name": "minecraft:lead", + "weight": 75 + }, + { + "type": "minecraft:item", + "name": "minecraft:redstone", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:golden_apple", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + }, + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.75, + "min": 0.5 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:diamond_chestplate", + "weight": 20 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:enchanted_book", + "weight": 15 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:enchanted_golden_apple", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:vindicator\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:evoker\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:vex\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:mansion\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/mansion" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/mineshaft.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/mineshaft.json new file mode 100644 index 00000000000..c4e5a420b99 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/mineshaft.json @@ -0,0 +1,267 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:mineshaft\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:rail", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:powered_rail", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:detector_rail", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:activator_rail", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:diamond", + "weight": 25 + }, + { + "type": "minecraft:item", + "name": "minecraft:name_tag", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:golden_apple", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:enchanted_book", + "weight": 20 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:enchanted_golden_apple", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombie\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:creeper\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:spider\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:cave_spider\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:enderman\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 15 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:mineshaft\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/mineshaft" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/monument.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/monument.json new file mode 100644 index 00000000000..9a01092712c --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/monument.json @@ -0,0 +1,108 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:monument\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:prismarine_bricks", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:dark_prismarine", + "weight": 25 + }, + { + "type": "minecraft:item", + "name": "minecraft:sea_lantern", + "weight": 15 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:wet_sponge", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:guardian\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:elder_guardian\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 5 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:monument\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/monument" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/nether_fossil.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/nether_fossil.json new file mode 100644 index 00000000000..16de36f84fb --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/nether_fossil.json @@ -0,0 +1,73 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:nether_fossil\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:air", + "weight": 25 + }, + { + "type": "minecraft:item", + "name": "minecraft:bone_block", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:bone_meal", + "weight": 10 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombified_piglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 25 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:nether_fossil\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/nether_fossil" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/pillager_outpost.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/pillager_outpost.json new file mode 100644 index 00000000000..31035ae79b9 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/pillager_outpost.json @@ -0,0 +1,125 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:pillager_outpost\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_ingot", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:experience_bottle", + "weight": 40 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + } + ], + "name": "minecraft:crossbow", + "weight": 40 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:enchanted_book", + "weight": 10 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:pillager\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:pillager_captain\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 5 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:pillager_outpost\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/pillager_outpost" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/ruined_portal.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/ruined_portal.json new file mode 100644 index 00000000000..192909a56c9 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/ruined_portal.json @@ -0,0 +1,238 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:ruined_portal\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:netherrack", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:obsidian", + "weight": 100 + }, + { + "type": "minecraft:item", + "name": "minecraft:fire_charge", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:golden_apple", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_helmet", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_chestplate", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_leggings", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_boots", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_sword", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_pickaxe", + "weight": 25 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "damage": { + "type": "minecraft:uniform", + "max": 0.95, + "min": 0.75 + }, + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_shovel", + "weight": 25 + }, + { + "type": "minecraft:item", + "name": "minecraft:gold_block", + "weight": 10 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombified_piglin\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 25 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:ruined_portal\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/ruined_portal" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/shipwreck.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/shipwreck.json new file mode 100644 index 00000000000..dd51cb9c88a --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/shipwreck.json @@ -0,0 +1,286 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:shipwreck\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:oak_planks", + "weight": 500 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:spruce_planks", + "weight": 500 + }, + { + "type": "minecraft:item", + "name": "minecraft:iron_ingot", + "weight": 150 + }, + { + "type": "minecraft:item", + "name": "minecraft:gold_ingot", + "weight": 100 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:diamond", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:drowned\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 200 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:drowned_trident\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:bell_pepper", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:cabbage", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:chickpea", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:durum", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:eggplant", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:garlic", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:onion", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:soybean", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:tomato", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:rice", + "weight": 75 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:shipwreck\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/shipwreck" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/stronghold.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/stronghold.json new file mode 100644 index 00000000000..d961fb2086b --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/stronghold.json @@ -0,0 +1,198 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:stronghold\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:stone_bricks", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cracked_stone_bricks", + "weight": 100 + }, + { + "type": "minecraft:item", + "name": "minecraft:ender_pearl", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:book", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:bookshelf", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:diamond", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "medium", + "hard", + "nightmare" + ] + } + ], + "name": "minecraft:emerald", + "weight": 25 + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecolonies:expedition_difficulty", + "difficulties": [ + "hard", + "nightmare" + ] + } + ], + "functions": [ + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:enchanted_book", + "weight": 20 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombie\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:skeleton\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 30 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:creeper\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:enderman\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 10 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 3.0, + "min": 1.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:stronghold\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/stronghold" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/swamp_hut.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/swamp_hut.json new file mode 100644 index 00000000000..71d1bc63664 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/swamp_hut.json @@ -0,0 +1,143 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:swamp_hut\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:spruce_planks", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:spruce_log", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:red_mushroom", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:brown_mushroom", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:cauldron", + "weight": 50 + }, + { + "type": "minecraft:item", + "name": "minecraft:brewing_stand", + "weight": 50 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:witch\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{amount:1,encounter:\"minecolonies:zombie\",scale:1b,type:\"encounter\"}" + } + ], + "name": "minecolonies:adventure_token", + "weight": 50 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 5.0, + "min": 3.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:swamp_hut\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/swamp_hut" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_desert.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_desert.json new file mode 100644 index 00000000000..f4ab8aed42c --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_desert.json @@ -0,0 +1,260 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_desert\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:sandstone", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:smooth_sandstone", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cactus", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:dead_bush", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:clay_ball", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:flatbread", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:pottage", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:pepper_hummus", + "weight": 150 + }, + { + "type": "minecraft:item", + "name": "minecolonies:pita_hummus", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:chickpea", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:durum", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:eggplant", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:garlic", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:onion", + "weight": 75 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_desert\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/village_desert" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_plains.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_plains.json new file mode 100644 index 00000000000..130d54fc620 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_plains.json @@ -0,0 +1,276 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_plains\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cobblestone", + "weight": 300 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:oak_planks", + "weight": 300 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:oak_sapling", + "weight": 180 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:dandelion", + "weight": 180 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:poppy", + "weight": 180 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:cheddar_cheese", + "weight": 180 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:pasta_plain", + "weight": 120 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:pasta_tomato", + "weight": 180 + }, + { + "type": "minecraft:item", + "name": "minecolonies:stuffed_pita", + "weight": 120 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:bell_pepper", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:durum", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:eggplant", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:garlic", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:onion", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:tomato", + "weight": 75 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_plains\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/village_plains" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_savanna.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_savanna.json new file mode 100644 index 00000000000..8ee291643b9 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_savanna.json @@ -0,0 +1,260 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_savanna\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:yellow_terracotta", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:acacia_planks", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:acacia_sapling", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:grass", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:tall_grass", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:tofu", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:lembas_scone", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:pepper_hummus", + "weight": 150 + }, + { + "type": "minecraft:item", + "name": "minecolonies:pita_hummus", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:chickpea", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:durum", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:eggplant", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:garlic", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:onion", + "weight": 75 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_savanna\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/village_savanna" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_snowy.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_snowy.json new file mode 100644 index 00000000000..ef73d85ac85 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_snowy.json @@ -0,0 +1,260 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_snowy\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:snow_block", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:ice", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:snowball", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:beetroot", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:beetroot_soup", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:feta_cheese", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:manchet_bread", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:cabochis", + "weight": 150 + }, + { + "type": "minecraft:item", + "name": "minecolonies:lamb_stew", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:cabbage", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:durum", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:eggplant", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:garlic", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:onion", + "weight": 75 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_snowy\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/village_snowy" +} \ No newline at end of file diff --git a/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_taiga.json b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_taiga.json new file mode 100644 index 00000000000..fd76acc18a1 --- /dev/null +++ b/src/datagen/generated/minecolonies/data/minecolonies/loot_tables/expeditions/structures/village_taiga.json @@ -0,0 +1,260 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_taiga\",type:\"structure_start\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:cobblestone", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:spruce_planks", + "weight": 250 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:fern", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:large_fern", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:sweet_berries", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:cooked_rice", + "weight": 150 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:muffin", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:cabochis", + "weight": 150 + }, + { + "type": "minecraft:item", + "name": "minecolonies:lamb_stew", + "weight": 100 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:cabbage", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:durum", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:eggplant", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:garlic", + "weight": 75 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 6.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecolonies:onion", + "weight": 75 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 5.0 + } + }, + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{structure:\"minecraft:village_taiga\",type:\"structure_end\"}" + } + ], + "name": "minecolonies:adventure_token" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecolonies:expeditions/structures/village_taiga" +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/IMinecoloniesAPI.java b/src/main/java/com/minecolonies/api/IMinecoloniesAPI.java index 8c90e379254..8c7529b8884 100755 --- a/src/main/java/com/minecolonies/api/IMinecoloniesAPI.java +++ b/src/main/java/com/minecolonies/api/IMinecoloniesAPI.java @@ -20,6 +20,7 @@ import com.minecolonies.api.crafting.registry.RecipeTypeEntry; import com.minecolonies.api.entity.mobs.registry.IMobAIRegistry; import com.minecolonies.api.entity.citizen.happiness.HappinessRegistry; +import com.minecolonies.api.entity.visitor.IVisitorType; import com.minecolonies.api.entity.pathfinding.registry.IPathNavigateRegistry; import com.minecolonies.api.equipment.registry.EquipmentTypeEntry; import com.minecolonies.api.quests.registries.QuestRegistries; @@ -98,6 +99,8 @@ static IMinecoloniesAPI getInstance() IForgeRegistry getHappinessFunctionRegistry(); + IForgeRegistry getVisitorTypeRegistry(); + void onRegistryNewRegistry(NewRegistryEvent event); IForgeRegistry getEquipmentTypeRegistry(); diff --git a/src/main/java/com/minecolonies/api/MinecoloniesAPIProxy.java b/src/main/java/com/minecolonies/api/MinecoloniesAPIProxy.java index 12d82f35011..478db9cf7d6 100755 --- a/src/main/java/com/minecolonies/api/MinecoloniesAPIProxy.java +++ b/src/main/java/com/minecolonies/api/MinecoloniesAPIProxy.java @@ -20,6 +20,7 @@ import com.minecolonies.api.crafting.registry.RecipeTypeEntry; import com.minecolonies.api.entity.mobs.registry.IMobAIRegistry; import com.minecolonies.api.entity.citizen.happiness.HappinessRegistry; +import com.minecolonies.api.entity.visitor.IVisitorType; import com.minecolonies.api.entity.pathfinding.registry.IPathNavigateRegistry; import com.minecolonies.api.equipment.registry.EquipmentTypeEntry; import com.minecolonies.api.quests.registries.QuestRegistries; @@ -224,6 +225,12 @@ public IForgeRegistry getHappinessFunc return apiInstance.getHappinessFunctionRegistry(); } + @Override + public IForgeRegistry getVisitorTypeRegistry() + { + return apiInstance.getVisitorTypeRegistry(); + } + @Override public void onRegistryNewRegistry(final NewRegistryEvent event) { diff --git a/src/main/java/com/minecolonies/api/colony/ICitizenDataView.java b/src/main/java/com/minecolonies/api/colony/ICitizenDataView.java index 269d80c9734..08abf6fcaf6 100755 --- a/src/main/java/com/minecolonies/api/colony/ICitizenDataView.java +++ b/src/main/java/com/minecolonies/api/colony/ICitizenDataView.java @@ -227,4 +227,12 @@ public interface ICitizenDataView extends ICitizen * @return the uuid. */ UUID getCustomTextureUUID(); + + /** + * Get the colony this citizen belongs to. + * + * @return the colony view. + */ + @Override + IColonyView getColony(); } diff --git a/src/main/java/com/minecolonies/api/colony/IColony.java b/src/main/java/com/minecolonies/api/colony/IColony.java index 1ac43da9e65..e04992aadd7 100755 --- a/src/main/java/com/minecolonies/api/colony/IColony.java +++ b/src/main/java/com/minecolonies/api/colony/IColony.java @@ -1,6 +1,7 @@ package com.minecolonies.api.colony; import com.minecolonies.api.colony.managers.interfaces.*; +import com.minecolonies.api.colony.managers.interfaces.expeditions.IColonyExpeditionManager; import com.minecolonies.api.colony.permissions.IPermissions; import com.minecolonies.api.colony.requestsystem.manager.IRequestManager; import com.minecolonies.api.colony.requestsystem.requester.IRequester; @@ -465,6 +466,21 @@ default List getWayPoints(@NotNull BlockPos position, @NotNull BlockPo */ IQuestManager getQuestManager(); + /** + * Get the expedition manager of the colony. + * + * @return the expedition manager. + */ + @NotNull + IColonyExpeditionManager getExpeditionManager(); + + /** + * Get the traveling manager used for traveling large distances. + * + * @return the travelling manager. + */ + ITravelingManager getTravelingManager(); + /** * Get citizen from colony. * @param id the id of the cit. diff --git a/src/main/java/com/minecolonies/api/colony/IColonyView.java b/src/main/java/com/minecolonies/api/colony/IColonyView.java index a5347b2beac..ff7d9cef23d 100755 --- a/src/main/java/com/minecolonies/api/colony/IColonyView.java +++ b/src/main/java/com/minecolonies/api/colony/IColonyView.java @@ -421,7 +421,7 @@ public interface IColonyView extends IColony * @param citizenId id to query * @return citizen data for visitor */ - ICitizenDataView getVisitor(int citizenId); + IVisitorViewData getVisitor(int citizenId); /** * Get a list of all available citizen name style options. diff --git a/src/main/java/com/minecolonies/api/colony/IVisitorData.java b/src/main/java/com/minecolonies/api/colony/IVisitorData.java index b6a5fb39240..325c5c02e97 100644 --- a/src/main/java/com/minecolonies/api/colony/IVisitorData.java +++ b/src/main/java/com/minecolonies/api/colony/IVisitorData.java @@ -1,38 +1,35 @@ package com.minecolonies.api.colony; -import net.minecraft.core.BlockPos; -import net.minecraft.world.item.ItemStack; - -import java.util.UUID; +import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import com.minecolonies.api.entity.visitor.IVisitorType; +import org.jetbrains.annotations.NotNull; /** - * Data for colony visitors, based on citizendata + * Data for colony visitors, based on citizen data */ public interface IVisitorData extends ICitizenData { /** - * Sets the recruitment cost stack - */ - void setRecruitCosts(final ItemStack cost); - - /** - * Returns the recruitment cost stack + * Get the type of the visitor. * - * @return itemstack + * @return the visitor type. */ - ItemStack getRecruitCost(); + @NotNull + IVisitorType getVisitorType(); /** - * The position the visitor is sitting on + * Get any bit of additional information for this visitor. * - * @return sitting pos + * @param extraData the extra data key. + * @return the extra data container. */ - BlockPos getSittingPosition(); + T getExtraDataValue(final IVisitorExtraData extraData); /** - * Sets the sitting position + * Set any bit of additional information for this visitor. * - * @param pos sitting pos + * @param extraData the extra data key. + * @param value the new value for the extra data key. */ - void setSittingPosition(final BlockPos pos); + void setExtraDataValue(final IVisitorExtraData extraData, final T value); } diff --git a/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java b/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java index 19df62a1976..038b3ef2698 100644 --- a/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java +++ b/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java @@ -1,6 +1,8 @@ package com.minecolonies.api.colony; -import net.minecraft.world.item.ItemStack; +import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import com.minecolonies.api.entity.visitor.IVisitorType; +import org.jetbrains.annotations.NotNull; /** * View data for visitors @@ -8,9 +10,17 @@ public interface IVisitorViewData extends ICitizenDataView { /** - * Gets the visitors recruitment cost + * Get the type of the visitor. * - * @return stack to pay + * @return the visitor type. */ - ItemStack getRecruitCost(); + @NotNull + IVisitorType getVisitorType(); + + /** + * Get any bit of additional information for this visitor. + * + * @return the extra data container. + */ + T getExtraDataValue(final IVisitorExtraData extraData); } diff --git a/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionFinishedStatus.java b/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionFinishedStatus.java new file mode 100644 index 00000000000..25686184688 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionFinishedStatus.java @@ -0,0 +1,43 @@ +package com.minecolonies.api.colony.expeditions; + +/** + * Enum describing the different finished statuses of an expedition. + */ +public enum ExpeditionFinishedStatus +{ + /** + * The expedition has returned safely to the colony. + */ + RETURNED(ExpeditionFinishedStatusType.SUCCESSFUL), + /** + * The expedition has been killed off. + */ + KILLED(ExpeditionFinishedStatusType.UNSUCCESSFUL), + /** + * The expedition has gotten lost. + */ + LOST(ExpeditionFinishedStatusType.UNSUCCESSFUL); + + /** + * The status type for this status. + */ + private final ExpeditionFinishedStatusType statusType; + + /** + * Internal constructor. + */ + ExpeditionFinishedStatus(final ExpeditionFinishedStatusType statusType) + { + this.statusType = statusType; + } + + /** + * Get the status type for this status. + * + * @return the status type enum. + */ + public ExpeditionFinishedStatusType getStatusType() + { + return statusType; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionFinishedStatusType.java b/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionFinishedStatusType.java new file mode 100644 index 00000000000..8a7bf7b0780 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionFinishedStatusType.java @@ -0,0 +1,41 @@ +package com.minecolonies.api.colony.expeditions; + +import net.minecraft.ChatFormatting; + +/** + * Enum describing the different finished status types of an expedition. + */ +public enum ExpeditionFinishedStatusType +{ + /** + * The expedition has been successfully completed. + */ + SUCCESSFUL(ChatFormatting.DARK_GREEN), + /** + * The expedition has not been successfully completed. + */ + UNSUCCESSFUL(ChatFormatting.DARK_RED); + + /** + * The display color for the status type. + */ + private final ChatFormatting displayColor; + + /** + * Internal constructor. + */ + ExpeditionFinishedStatusType(final ChatFormatting displayColor) + { + this.displayColor = displayColor; + } + + /** + * Get the display color for the status type. + * + * @return the formatting. + */ + public ChatFormatting getDisplayColor() + { + return displayColor; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java b/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java new file mode 100644 index 00000000000..df9ac7ba0f4 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java @@ -0,0 +1,53 @@ +package com.minecolonies.api.colony.expeditions; + +/** + * Enum describing the different statuses of an expedition. + */ +public enum ExpeditionStatus +{ + /** + * The expedition has been created but not accepted yet. + */ + CREATED(true), + /** + * The expedition has been accepted but has not been sent away just yet. + */ + ACCEPTED(true), + /** + * The expedition is currently active. + */ + ONGOING(false), + /** + * The expedition is finished. + */ + FINISHED(true), + /** + * The expedition does not exist. + */ + UNKNOWN(true); + + /** + * Whether the tied visitor may be removed during the given phase of the expedition. + */ + private final boolean mayRemoveVisitor; + + /** + * Internal constructor. + * + * @param mayRemoveVisitor whether the tied visitor may be removed during the given phase of the expedition. + */ + ExpeditionStatus(boolean mayRemoveVisitor) + { + this.mayRemoveVisitor = mayRemoveVisitor; + } + + /** + * Check whether the tied visitor may be removed during the given phase of the expedition. + * + * @return true if so. + */ + public boolean mayRemoveVisitor() + { + return mayRemoveVisitor; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/colony/expeditions/IExpedition.java b/src/main/java/com/minecolonies/api/colony/expeditions/IExpedition.java new file mode 100644 index 00000000000..e623cf4efbc --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/expeditions/IExpedition.java @@ -0,0 +1,84 @@ +package com.minecolonies.api.colony.expeditions; + +import com.minecolonies.api.colony.colonyEvents.EventStatus; +import com.minecolonies.core.colony.expeditions.ExpeditionStage; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Main interface for an expedition instance. + */ +public interface IExpedition +{ + /** + * Get all the members of the expedition. + * + * @return the expedition members. + */ + @NotNull + Collection> getMembers(); + + /** + * Get the equipment given to the expedition at start time. + * + * @return the equipment list. + */ + Collection getEquipment(); + + /** + * Get the currently active members of the expedition. + * + * @return the expedition members. + */ + @NotNull + Collection> getActiveMembers(); + + /** + * The results of this expedition. + * Yields null as long as the stage of the expedition is not {@link EventStatus#DONE}. + * + * @return a list of stages containing results per expedition stage. + */ + Collection getResults(); + + /** + * Advances the current stage of the expedition. + * Adding a new stage on top of the current one. + * + * @param header the header for this new stage. + */ + void advanceStage(final Component header); + + /** + * Adds a reward to the current newest stage. + * + * @param itemStack the item to add to the stage. + */ + void rewardFound(final ItemStack itemStack); + + /** + * Adds a kill to the current newest stage. + * + * @param encounterId the type of the encounter. + */ + void mobKilled(final ResourceLocation encounterId); + + /** + * Adds a member that got lost during the current newest stage. + * + * @param member the member that were lost. + */ + void memberLost(final IExpeditionMember member); + + /** + * Write this expedition to compound data. + * + * @param compound the compound tag. + */ + void write(final CompoundTag compound); +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java b/src/main/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java new file mode 100644 index 00000000000..8302dcc4352 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java @@ -0,0 +1,108 @@ +package com.minecolonies.api.colony.expeditions; + +import com.minecolonies.api.colony.ICivilianData; +import com.minecolonies.api.colony.IColony; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Interface for expedition members. + */ +public interface IExpeditionMember +{ + /** + * Get the id of the expedition member. + * + * @return the civilian id. + */ + int getId(); + + /** + * Get the name of the expedition member. + * + * @return the name of the civilian. + */ + String getName(); + + /** + * Get the health this member has. + * + * @return the current health. + */ + float getHealth(); + + /** + * Set the new health for this member. + * + * @param health the new health. + */ + void setHealth(float health); + + /** + * Get the max health this member has. + * + * @return the max health. + */ + float getMaxHealth(); + + /** + * Get whether this expedition member died during the expedition. + * + * @return true if so. + */ + boolean isDead(); + + /** + * Get the weapon this member is carrying. + * + * @return the weapon item stack. + */ + ItemStack getPrimaryWeapon(); + + /** + * Set the weapon this member is carrying. + * + * @param itemStack the weapon item stack. + */ + void setPrimaryWeapon(final ItemStack itemStack); + + /** + * Get the armor pieces this member is wearing. + * + * @return the armor piece. + */ + @NotNull + ItemStack getArmor(final EquipmentSlot slot); + + /** + * Set the armor in a given slot. + * + * @param slot which slow to set the armor for. + * @param itemStack the item stack to set it to. + */ + void setArmor(final EquipmentSlot slot, final @NotNull ItemStack itemStack); + + /** + * Attempt to resolve the civilian data for this expedition member. + * May return null for multiple reasons. + * + * @param colony the colony where this member is from. + * @return the civilian data, or null. + */ + @Nullable T resolveCivilianData(final IColony colony); + + /** + * Write this member to compound data. + * + * @param compound the compound tag. + */ + void write(final CompoundTag compound); + + /** + * Indicates to the colony the member should be removed because they died during the expedition. + */ + void removeFromColony(final IColony colony); +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/colony/expeditions/MobKill.java b/src/main/java/com/minecolonies/api/colony/expeditions/MobKill.java new file mode 100644 index 00000000000..c245a1dd8b1 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/expeditions/MobKill.java @@ -0,0 +1,13 @@ +package com.minecolonies.api.colony.expeditions; + +import net.minecraft.resources.ResourceLocation; + +/** + * Record for recording a kill during an expedition stage. + * + * @param encounterId the entity that was killed. + * @param count how many of said entity were killed. + */ +public record MobKill(ResourceLocation encounterId, int count) +{ +} diff --git a/src/main/java/com/minecolonies/api/colony/interactionhandling/AbstractInteractionResponseHandler.java b/src/main/java/com/minecolonies/api/colony/interactionhandling/AbstractInteractionResponseHandler.java index 3b8f5c33f50..87fd4311f78 100755 --- a/src/main/java/com/minecolonies/api/colony/interactionhandling/AbstractInteractionResponseHandler.java +++ b/src/main/java/com/minecolonies/api/colony/interactionhandling/AbstractInteractionResponseHandler.java @@ -1,6 +1,7 @@ package com.minecolonies.api.colony.interactionhandling; import com.google.common.collect.ImmutableList; +import com.minecolonies.api.colony.ICitizen; import com.minecolonies.api.colony.ICitizenData; import com.minecolonies.api.util.Tuple; import com.minecolonies.api.util.constant.NbtTagConstants; @@ -89,7 +90,7 @@ public Component getResponseResult(final Component response) } @Override - public List getPossibleResponses() + public List getPossibleResponses(final ICitizen data) { return ImmutableList.copyOf(responses.keySet()); } diff --git a/src/main/java/com/minecolonies/api/colony/interactionhandling/IInteractionResponseHandler.java b/src/main/java/com/minecolonies/api/colony/interactionhandling/IInteractionResponseHandler.java index e74cfd89a50..d409a904234 100644 --- a/src/main/java/com/minecolonies/api/colony/interactionhandling/IInteractionResponseHandler.java +++ b/src/main/java/com/minecolonies/api/colony/interactionhandling/IInteractionResponseHandler.java @@ -1,6 +1,7 @@ package com.minecolonies.api.colony.interactionhandling; import com.ldtteam.blockui.views.BOWindow; +import com.minecolonies.api.colony.ICitizen; import com.minecolonies.api.colony.ICitizenData; import com.minecolonies.api.colony.ICitizenDataView; import net.minecraft.nbt.CompoundTag; @@ -30,9 +31,11 @@ public interface IInteractionResponseHandler extends INBTSerializable getPossibleResponses(); + List getPossibleResponses(final ICitizen data); /** * Get possible further interaction from the GUI on response. diff --git a/src/main/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java b/src/main/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java index 098afc2d877..309769715b8 100644 --- a/src/main/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java +++ b/src/main/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java @@ -20,6 +20,7 @@ public final class ModInteractionResponseHandlers public static final ResourceLocation RECRUITMENT = new ResourceLocation(Constants.MOD_ID, "recruitment"); public static final ResourceLocation QUEST = new ResourceLocation(Constants.MOD_ID, "quest"); public static final ResourceLocation QUEST_ACTION = new ResourceLocation(Constants.MOD_ID, "questaction"); + public static final ResourceLocation EXPEDITION = new ResourceLocation(Constants.MOD_ID, "expedition"); /** * List of entries. @@ -31,9 +32,10 @@ public final class ModInteractionResponseHandlers public static RegistryObject recruitment; public static RegistryObject quest; public static RegistryObject questAction; + public static RegistryObject expedition; private ModInteractionResponseHandlers() { - throw new IllegalStateException("Tried to initialize: ModJobs but this is a Utility class."); + throw new IllegalStateException("Tried to initialize: ModInteractionResponseHandlers but this is a Utility class."); } } diff --git a/src/main/java/com/minecolonies/api/colony/jobs/IJob.java b/src/main/java/com/minecolonies/api/colony/jobs/IJob.java index 419b68c9d29..cd0d8edc669 100755 --- a/src/main/java/com/minecolonies/api/colony/jobs/IJob.java +++ b/src/main/java/com/minecolonies/api/colony/jobs/IJob.java @@ -284,13 +284,23 @@ default void triggerActivityChangeAction(boolean newState) /** * Whether the job is a guard * - * @return + * @return true if so. */ default boolean isGuard() { return false; } + /** + * Whether the job is a guard that actively engages in combat + * + * @return true if so. + */ + default boolean isCombatGuard() + { + return false; + } + /** * Gets the position of the building the job is assigned to * @return diff --git a/src/main/java/com/minecolonies/api/colony/jobs/IJobView.java b/src/main/java/com/minecolonies/api/colony/jobs/IJobView.java index 7ddbdde8077..e16e7414f1d 100644 --- a/src/main/java/com/minecolonies/api/colony/jobs/IJobView.java +++ b/src/main/java/com/minecolonies/api/colony/jobs/IJobView.java @@ -34,6 +34,16 @@ public interface IJobView */ JobEntry getEntry(); + /** + * Get whether this job is a guard job. + */ + boolean isGuard(); + + /** + * Get whether this job is a guard job that actively engages in combat. + */ + boolean isCombatGuard(); + /** * Set the job entry of the view. * @param jobEntry the entry to set. diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java index f91502da6dc..06f3144c55b 100644 --- a/src/main/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java @@ -13,8 +13,25 @@ /** * The interface of the citizen manager. */ -public interface ICitizenManager extends IEntityManager +public interface ICitizenManager extends IEntityManager { + /** + * Spawns a civilian with the specific civilian data. + * + * @param data Data to use when spawn, null when new generation. + * @param world THe world. + * @param spawnPos the pos to spawn it at. + * @param force True to skip max civilian test, false when not. + * @return the new civilian. + */ + ICitizenData spawnOrCreateCitizen(ICitizenData data, Level world, BlockPos spawnPos, boolean force); + + /** + * Creates citizen data for a new citizen + * + * @return new ICitizenData + */ + ICitizenData createAndRegisterCitizenData(); /** * Spawn a brand new Citizen. @@ -30,7 +47,7 @@ public interface ICitizenManager extends IEntityManager */ default ICitizenData spawnOrCreateCitizen(final ICitizenData data, @NotNull final Level world) { - return this.spawnOrCreateCivilian(data, world, null, false); + return this.spawnOrCreateCitizen(data, world, null, false); } /** @@ -43,7 +60,7 @@ default ICitizenData spawnOrCreateCitizen(final ICitizenData data, @NotNull fina */ default ICitizenData spawnOrCreateCitizen(final ICitizenData data, @NotNull final Level world, final BlockPos spawnPos) { - return this.spawnOrCreateCivilian(data, world, spawnPos, false); + return this.spawnOrCreateCitizen(data, world, spawnPos, false); } /** @@ -59,9 +76,6 @@ default ICitizenData spawnOrCreateCitizen(final ICitizenData data, @NotNull fina */ void calculateMaxCitizens(); - @Override - ICitizenData createAndRegisterCivilianData(); - /** * Resurrect a citizen from its saved NBT. * diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java index 59053d44fcb..a94955e6786 100644 --- a/src/main/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java @@ -3,10 +3,8 @@ import com.minecolonies.api.colony.ICivilianData; import com.minecolonies.api.colony.IColony; import com.minecolonies.api.entity.citizen.AbstractCivilianEntity; -import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -15,7 +13,7 @@ /** * Manager interface for managing entities for a colony */ -public interface IEntityManager +public interface IEntityManager { /** * Register a civilian entity with the colony @@ -61,7 +59,7 @@ void sendPackets( * @return Map of civilian in the colony, with as key the civilian ID, and as value the civilian data. */ @NotNull - Map getCivilianDataMap(); + Map getCivilianDataMap(); /** * Get civilian by ID. @@ -69,32 +67,14 @@ void sendPackets( * @param civilianId ID of the civilian. * @return ICivilianData associated with the ID, or null if it was not found. */ - T getCivilian(int civilianId); - - /** - * Spawns a civilian with the specific civilian data. - * - * @param data Data to use when spawn, null when new generation. - * @param world THe world. - * @param spawnPos the pos to spawn it at. - * @param force True to skip max civilian test, false when not. - * @return the new civilian. - */ - T spawnOrCreateCivilian(T data, Level world, BlockPos spawnPos, boolean force); - - /** - * Creates Civilian Data for a new civilian - * - * @return new ICivilianData - */ - ICivilianData createAndRegisterCivilianData(); + T getCivilian(int civilianId); /** * Removes a civilian from the colony. * * @param civilian data to remove. */ - void removeCivilian(@NotNull ICivilianData civilian); + void removeCivilian(@NotNull T civilian); /** * Marks civilian data dirty. diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/ITravelingManager.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/ITravelingManager.java new file mode 100644 index 00000000000..8b21c6963e2 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/ITravelingManager.java @@ -0,0 +1,111 @@ +package com.minecolonies.api.colony.managers.interfaces; + +import com.minecolonies.api.colony.ICitizenData; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraftforge.common.util.INBTSerializable; + +import java.util.Optional; + +/** + * Manages the traveling system for a given colony. + */ +public interface ITravelingManager extends INBTSerializable +{ + /** + * Get whether a citizen is currently travelling. + * + * @param citizenData the citizen data. + * @return true if so. + */ + default boolean isTravelling(final ICitizenData citizenData) + { + return isTravelling(citizenData.getId()); + } + + /** + * Get whether a citizen is currently travelling. + * + * @param citizenId the id of the citizen. + * @return true if so. + */ + boolean isTravelling(final int citizenId); + + /** + * Get the target the citizen is travelling to. + * + * @param citizenData the citizen data. + * @return an optional position. + */ + default Optional getTravellingTargetFor(final ICitizenData citizenData) + { + return getTravellingTargetFor(citizenData.getId()); + } + + /** + * Get the target the citizen is travelling to. + * + * @param citizenId the id of the citizen. + * @return an optional position. + */ + Optional getTravellingTargetFor(final int citizenId); + + /** + * Tells the given citizen to start travelling to a given location. + * + * @param citizenData the citizen data. + * @param target the target position. + * @param travelTimeInTicks the time it will take to travel to the given location. + * @param canRecall whether the given citizen is allowed to be recalled or not. + */ + default void startTravellingTo(final ICitizenData citizenData, final BlockPos target, final int travelTimeInTicks, final boolean canRecall) + { + startTravellingTo(citizenData.getId(), target, travelTimeInTicks, canRecall); + } + + /** + * Start travelling to a given location for a citizen. + * + * @param citizenId the id of the citizen. + * @param target the target position. + * @param travelTimeInTicks the time it will take to travel to the given location. + * @param canRecall whether the given citizen is allowed to be recalled or not. + */ + void startTravellingTo(final int citizenId, final BlockPos target, final int travelTimeInTicks, final boolean canRecall); + + /** + * Finish travelling for a citizen. + * + * @param citizenData the citizen data. + */ + default void finishTravellingFor(final ICitizenData citizenData) + { + finishTravellingFor(citizenData.getId()); + } + + /** + * Finish travelling for a citizen. + * + * @param citizenId the id of the citizen. + */ + void finishTravellingFor(final int citizenId); + + /** + * Finishes travelling for all citizens currently away travelling. + */ + void recallAllTravellingCitizens(); + + /** + * Whether the expedition manager class is dirty and the client needs to be updated. + * + * @return true if so. + */ + boolean isDirty(); + + /** + * Update the dirty flag of the expedition manager. + * + * @param dirty the new dirty state. + */ + void setDirty(boolean dirty); +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java index fbf69d763d8..ca79b93016c 100644 --- a/src/main/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java @@ -1,18 +1,36 @@ package com.minecolonies.api.colony.managers.interfaces; import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.entity.visitor.IVisitorType; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; /** * Visitor manager to manage visiting entities */ -public interface IVisitorManager extends IEntityManager +public interface IVisitorManager extends IEntityManager { /** - * Gets the visitor data for the given citizen + * Tick the visitor data of all active citizens. + */ + boolean tickVisitorData(int tickRate); + + /** + * Spawns a civilian with the specific civilian data. + * + * @param visitorType the visitor type. + * @param data Data to use when spawn, null when new generation. + * @param world THe world. + * @param spawnPos the pos to spawn it at. + * @return the new civilian. + */ + IVisitorData spawnOrCreateVisitor(IVisitorType visitorType, IVisitorData data, Level world, BlockPos spawnPos); + + /** + * Creates visitor data for a new visitor * - * @param citizenId id to get data for - * @param data type - * @return visitor data + * @param visitorType the visitor type. + * @return new IVisitorData */ - T getVisitor(int citizenId); + IVisitorData createAndRegisterVisitorData(final IVisitorType visitorType); } diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/ColonyExpedition.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/ColonyExpedition.java new file mode 100644 index 00000000000..9e16aaaeecb --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/ColonyExpedition.java @@ -0,0 +1,183 @@ +package com.minecolonies.api.colony.managers.interfaces.expeditions; + +import com.minecolonies.api.colony.ICitizenDataView; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import com.minecolonies.core.colony.expeditions.AbstractExpedition; +import com.minecolonies.core.colony.expeditions.ExpeditionStage; +import com.minecolonies.core.colony.expeditions.ExpeditionVisitorMember; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; + +/** + * Class for a colony expedition instance. + */ +public final class ColonyExpedition extends AbstractExpedition +{ + /** + * NBT tags. + */ + private static final String TAG_EXPEDITION_TYPE = "expeditionType"; + + /** + * The id for this expedition. + */ + private final int id; + + /** + * The expedition type. + */ + @NotNull + private final ResourceLocation expeditionTypeId; + + /** + * Default constructor. + * + * @param id the id of the expedition. + * @param expeditionTypeId the expedition type. + * @param members the members for this expedition. + * @param equipment the list of equipment for this expedition. + */ + public ColonyExpedition( + final int id, + final @NotNull ResourceLocation expeditionTypeId, + final @NotNull Map> members, + final @NotNull List equipment) + { + super(members, equipment, List.of()); + this.id = id; + this.expeditionTypeId = expeditionTypeId; + } + + /** + * Deserialization constructor. + * + * @param members the members for this expedition. + * @param equipment the list of equipment for this expedition. + * @param results the results for this expedition. + * @param id the id of the expedition. + * @param expeditionTypeId the expedition type. + */ + private ColonyExpedition( + final int id, + final @NotNull ResourceLocation expeditionTypeId, + final @NotNull List> members, + final @NotNull List equipment, + final @NotNull List results) + { + super(members.stream().collect(Collectors.toMap(IExpeditionMember::getId, v -> v)), equipment, results); + this.id = id; + this.expeditionTypeId = expeditionTypeId; + } + + /** + * Create a colony expedition instance from compound data. + * + * @param compound the compound data. + * @return the expedition instance. + */ + @NotNull + public static ColonyExpedition loadFromNBT(final CompoundTag compound) + { + final int id = compound.getInt(TAG_ID); + final ResourceLocation expeditionTypeId = new ResourceLocation(compound.getString(TAG_EXPEDITION_TYPE)); + + return AbstractExpedition.loadFromNBT(compound, (members, equipment, results) -> new ColonyExpedition(id, expeditionTypeId, members, equipment, results)); + } + + /** + * Unique id for this expedition instance. + * + * @return the id. + */ + public int getId() + { + return id; + } + + /** + * Get the expedition type id for this expedition. + * + * @return the expedition type id. + */ + @NotNull + public ResourceLocation getExpeditionTypeId() + { + return expeditionTypeId; + } + + @Override + public void write(final CompoundTag compound) + { + super.write(compound); + compound.putInt(TAG_ID, id); + compound.putString(TAG_EXPEDITION_TYPE, expeditionTypeId.toString()); + } + + /** + * Get the expeditionary visitor that is leading this expedition. + * + * @return the member instance. + */ + @NotNull + public ExpeditionVisitorMember getLeader() + { + for (final IExpeditionMember member : members.values()) + { + if (member instanceof ExpeditionVisitorMember visitorMember) + { + return visitorMember; + } + } + throw new IllegalStateException("Visitor leader from expedition is missing."); + } + + /** + * Comparator class for sorting guards in a predictable order in the window. + */ + public static class GuardsComparator implements Comparator + { + /** + * The set of active members. + */ + private final Set activeMembers; + + /** + * Default constructor. + * + * @param activeMembers the set of active members. + */ + public GuardsComparator(final Set activeMembers) + { + this.activeMembers = activeMembers; + } + + @Override + public int compare(final ICitizenDataView guard1, final ICitizenDataView guard2) + { + if (activeMembers.contains(guard1.getId()) && activeMembers.contains(guard2.getId())) + { + return guard1.getName().compareTo(guard2.getName()); + } + else if (activeMembers.contains(guard1.getId())) + { + return -1; + } + else if (activeMembers.contains(guard2.getId())) + { + return 1; + } + + return guard1.getName().compareTo(guard2.getName()); + } + } +} diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/CreatedExpedition.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/CreatedExpedition.java new file mode 100644 index 00000000000..e4930a38d27 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/CreatedExpedition.java @@ -0,0 +1,35 @@ +package com.minecolonies.api.colony.managers.interfaces.expeditions; + +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionBuilder; +import net.minecraft.resources.ResourceLocation; + +/** + * Container class for created expedition instances. + * + * @param id the id of the expedition. + * @param expeditionTypeId the expedition type for this expedition. + * @param accepted whether the expedition has already been accepted and is waiting for resources. + */ +public record CreatedExpedition(int id, ResourceLocation expeditionTypeId, boolean accepted) +{ + /** + * Accepts this expedition instance. + * + * @return a copy of this instance with {@link CreatedExpedition#accepted} set to true. + */ + public CreatedExpedition accept() + { + return new CreatedExpedition(id, expeditionTypeId, true); + } + + /** + * Turn this created expedition into a full-fledged expedition instance. + * + * @param builder the builder containing additional information. + * @return the complete expedition instance. + */ + public ColonyExpedition createExpedition(final ColonyExpeditionBuilder builder) + { + return builder.build(id, expeditionTypeId); + } +} diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/FinishedExpedition.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/FinishedExpedition.java new file mode 100644 index 00000000000..362bb4955c7 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/FinishedExpedition.java @@ -0,0 +1,13 @@ +package com.minecolonies.api.colony.managers.interfaces.expeditions; + +import com.minecolonies.api.colony.expeditions.ExpeditionFinishedStatus; + +/** + * Container class for finished expedition instances. + * + * @param expedition the completed expedition object. + * @param status the status which the expedition was finished with. + */ +public record FinishedExpedition(ColonyExpedition expedition, ExpeditionFinishedStatus status) +{ +} diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/IColonyExpeditionManager.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/IColonyExpeditionManager.java new file mode 100644 index 00000000000..e21d9edf177 --- /dev/null +++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/expeditions/IColonyExpeditionManager.java @@ -0,0 +1,171 @@ +package com.minecolonies.api.colony.managers.interfaces.expeditions; + +import com.minecolonies.api.colony.expeditions.ExpeditionFinishedStatus; +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionBuilder; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.items.ItemExpeditionSheet.ExpeditionSheetContainerManager; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.minecraftforge.common.util.INBTSerializable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Interface for the colony expedition manager. From here all outgoing expeditions to external places are managed. + */ +public interface IColonyExpeditionManager extends INBTSerializable +{ + /** + * Get a list of all the active expeditions in the colony. + * + * @return an unmodifiable list. + */ + List getActiveExpeditions(); + + /** + * Get a list of all the finished expeditions in the colony. + * + * @return an unmodifiable list. + */ + List getFinishedExpeditions(); + + /** + * Get the created expedition with the given id. + * + * @param id the expedition id. + * @return the expedition instance or null. + */ + @Nullable + CreatedExpedition getCreatedExpedition(int id); + + /** + * Get the active expedition with the given id. + * + * @param id the expedition id. + * @return the expedition instance or null. + */ + @Nullable + ColonyExpedition getActiveExpedition(final int id); + + /** + * Get the finished expedition with the given id. + * + * @param id the expedition id. + * @return the expedition instance or null. + */ + @Nullable + FinishedExpedition getFinishedExpedition(int id); + + /** + * Get the status of a given expedition. + * + * @param id the expedition id. + * @return the expedition status. + */ + @NotNull + ExpeditionStatus getExpeditionStatus(final int id); + + /** + * Register a new expedition to the manager. + * + * @param id the expedition id. + * @param expeditionTypeId the expedition type id. + * @return true if successfully added. + */ + boolean addExpedition(final int id, final ResourceLocation expeditionTypeId); + + /** + * Mark as expedition as accepted. + * + * @param id the expedition id. + * @return true if the expedition exists. + */ + boolean acceptExpedition(final int id); + + /** + * Mark as expedition as finished. + * + * @param id the expedition id. + * @param builder the builder used for getting extra expedition information. + * @return true if the expedition exists. + */ + boolean startExpedition(final int id, final ColonyExpeditionBuilder builder); + + /** + * Mark as expedition as finished. + * + * @param id the expedition id. + * @param status the status of how the expedition finished. + * @return true if the expedition exists. + */ + boolean finishExpedition(final int id, final ExpeditionFinishedStatus status); + + /** + * Removes a previously created expedition, in case the expedition was requested to be cancelled. + * + * @param id the expedition id. + */ + void removeCreatedExpedition(int id); + + /** + * Get whether another expedition may be started (determines if another visitor may spawn). + * + * @return true if so. + */ + boolean canStartNewExpedition(); + + /** + * Check that determines if expeditions to a given dimension may be sent or not. + * + * @param dimension the target dimension. + * @return whether the target dimension is allowed or not. + */ + boolean canGoToDimension(final ResourceKey dimension); + + /** + * Check if all requirements for a given expedition type are met. + * + * @param expeditionTypeId the expedition type id. + * @param inventory the inventory to check requirements against. + * @return true if so. + */ + boolean meetsRequirements(final ResourceLocation expeditionTypeId, final ExpeditionSheetContainerManager inventory); + + /** + * Check if all requirements for a given expedition type are met. + * + * @param expeditionType the expedition type instance. + * @param inventory the inventory to check requirements against. + * @return true if so. + */ + boolean meetsRequirements(final ColonyExpeditionType expeditionType, final ExpeditionSheetContainerManager inventory); + + /** + * Unlock nether expeditions. + */ + void unlockNether(); + + /** + * Unlock end expeditions. + */ + void unlockEnd(); + + /** + * Whether the expedition manager class is dirty and the client needs to be updated. + * + * @return true if so. + */ + boolean isDirty(); + + /** + * Update the dirty flag of the expedition manager. + * + * @param dirty the new dirty state. + */ + void setDirty(boolean dirty); +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/entity/ModEntities.java b/src/main/java/com/minecolonies/api/entity/ModEntities.java index fec751955b4..df9762aea28 100755 --- a/src/main/java/com/minecolonies/api/entity/ModEntities.java +++ b/src/main/java/com/minecolonies/api/entity/ModEntities.java @@ -1,6 +1,7 @@ package com.minecolonies.api.entity; import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; +import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; import com.minecolonies.api.entity.mobs.AbstractEntityRaiderMob; import com.minecolonies.api.entity.mobs.amazons.AbstractEntityAmazon; import com.minecolonies.api.entity.mobs.barbarians.AbstractEntityBarbarian; @@ -20,7 +21,9 @@ public class ModEntities { public static EntityType CITIZEN; - public static EntityType VISITOR; + public static EntityType VISITOR; + + public static EntityType EXPEDITIONARY; public static EntityType FISHHOOK; diff --git a/src/main/java/com/minecolonies/api/entity/ai/statemachine/tickratestatemachine/TickRateStateMachine.java b/src/main/java/com/minecolonies/api/entity/ai/statemachine/tickratestatemachine/TickRateStateMachine.java index 5176f51fade..4b694eee3ae 100755 --- a/src/main/java/com/minecolonies/api/entity/ai/statemachine/tickratestatemachine/TickRateStateMachine.java +++ b/src/main/java/com/minecolonies/api/entity/ai/statemachine/tickratestatemachine/TickRateStateMachine.java @@ -141,6 +141,9 @@ public void setTickRate(final int tickRate) @Override public void setCurrentDelay(final int ticksToNext) { - executedTransition.setTicksToUpdate(ticksToNext); + if (executedTransition != null) + { + executedTransition.setTicksToUpdate(ticksToNext); + } } } diff --git a/src/main/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java b/src/main/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java new file mode 100644 index 00000000000..41bf9c88898 --- /dev/null +++ b/src/main/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java @@ -0,0 +1,23 @@ +package com.minecolonies.api.entity.visitor; + +import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.level.Level; + +/** + * Abstract class for visitor entities. + */ +public abstract class AbstractEntityVisitor extends AbstractEntityCitizen +{ + /** + * Constructor for a new citizen typed entity. + * + * @param type the Entity type. + * @param world the world. + */ + protected AbstractEntityVisitor(final EntityType type, final Level world) + { + super(type, world); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java b/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java new file mode 100644 index 00000000000..e49489ef83d --- /dev/null +++ b/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java @@ -0,0 +1,78 @@ +package com.minecolonies.api.entity.visitor; + +import org.jetbrains.annotations.NotNull; + +/** + * Base class for extra visitor data. + * + * @param the data type of the stored data. + */ +public abstract class AbstractVisitorExtraData implements IVisitorExtraData +{ + /** + * The NBT key under which the data is stored. + */ + @NotNull + private final String key; + + /** + * The current value of the extra data. + */ + @NotNull + private S value; + + /** + * Internal constructor. + * + * @param key the NBT key. + * @param defaultValue the default value that the extra data initially has. + */ + protected AbstractVisitorExtraData(@NotNull final String key, @NotNull final S defaultValue) + { + this.key = key; + this.value = defaultValue; + } + + @Override + @NotNull + public final String getKey() + { + return key; + } + + @Override + @NotNull + public final S getValue() + { + return value; + } + + @Override + public final void setValue(@NotNull final S value) + { + this.value = value; + } + + @Override + public int hashCode() + { + return key.hashCode(); + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final AbstractVisitorExtraData that = (AbstractVisitorExtraData) o; + + return key.equals(that.key); + } +} diff --git a/src/main/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java new file mode 100644 index 00000000000..75c72c5fc40 --- /dev/null +++ b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java @@ -0,0 +1,36 @@ +package com.minecolonies.api.entity.visitor; + +import net.minecraft.nbt.CompoundTag; +import net.minecraftforge.common.util.INBTSerializable; +import org.jetbrains.annotations.NotNull; + +/** + * Interface for extra visitor data. + * + * @param the data type of the stored data. + */ +public interface IVisitorExtraData extends INBTSerializable +{ + /** + * The unique key which the data gets stored under. + * + * @return the key. + */ + @NotNull + String getKey(); + + /** + * Get the visitor data value. + * + * @return the value. + */ + @NotNull + S getValue(); + + /** + * Set the new value for this extra data key. + * + * @param value the new value. + */ + void setValue(S value); +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java new file mode 100644 index 00000000000..3149b4118be --- /dev/null +++ b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java @@ -0,0 +1,86 @@ +package com.minecolonies.api.entity.visitor; + +import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.core.entity.visitor.VisitorCitizen; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Function; + +import static com.minecolonies.api.util.constant.Constants.TICKS_SECOND; + +/** + * Specific handler actions for interacting with different types of visitors. + */ +public interface IVisitorType +{ + /** + * The id for this visitor type. + * + * @return the resloc. + */ + ResourceLocation getId(); + + /** + * Get the generator function for creating the entity for this given visitor. + * + * @return the entity creator. + */ + Function getEntityCreator(); + + /** + * Creates the state machine for this specific visitor. + * + * @param visitor the current visitor. + */ + void createStateMachine(final AbstractEntityVisitor visitor); + + /** + * Get the list of extra data keys to support for this visitor type. + * + * @return a list of extra data keys. + */ + default List> getExtraDataKeys() + { + return List.of(); + } + + /** + * Direct interaction on right click. + * + * @param visitor the visitor that was clicked on. + * @param player the player who clicked the visitor. + * @param level the level of the visitor. + * @param hand the hand which was used to interact. + */ + @NotNull + default InteractionResult onPlayerInteraction(final AbstractEntityVisitor visitor, final Player player, final Level level, final InteractionHand hand) + { + // Pause the visitor and temporarily look at the player + visitor.getEntityStateController().setCurrentDelay(TICKS_SECOND * 3); + visitor.getNavigation().stop(); + visitor.getLookControl().setLookAt(player); + return InteractionResult.PASS; + } + + /** + * Fired upon death of a visitor. + * + * @param visitor the visitor that has died. + * @param cause the damage source that caused them to die. + */ + default void onDied(final VisitorCitizen visitor, DamageSource cause) {} + + /** + * Tick the data to update values. + * + * @param visitor the visitor the update call is called for. + */ + default void update(final IVisitorData visitor) {} +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java b/src/main/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java new file mode 100644 index 00000000000..d82be82d53f --- /dev/null +++ b/src/main/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java @@ -0,0 +1,23 @@ +package com.minecolonies.api.entity.visitor; + +import com.minecolonies.api.util.constant.Constants; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.RegistryObject; + +/** + * Registry holder for the visitor types. + */ +public class ModVisitorTypes +{ + /** + * Resource ids. + */ + public static final ResourceLocation VISITOR_TYPE_ID = new ResourceLocation(Constants.MOD_ID, "visitor"); + public static final ResourceLocation EXPEDITIONARY_VISITOR_TYPE_ID = new ResourceLocation(Constants.MOD_ID, "expeditionary"); + + /** + * Registry objects. + */ + public static RegistryObject visitor; + public static RegistryObject expeditionary; +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/inventory/ModContainers.java b/src/main/java/com/minecolonies/api/inventory/ModContainers.java index f9fdc8a34b8..2c0b190dcb5 100755 --- a/src/main/java/com/minecolonies/api/inventory/ModContainers.java +++ b/src/main/java/com/minecolonies/api/inventory/ModContainers.java @@ -19,4 +19,6 @@ public class ModContainers public static RegistryObject> craftingGrid; public static RegistryObject> craftingBrewingstand; + + public static RegistryObject> expeditionSheet; } diff --git a/src/main/java/com/minecolonies/api/inventory/container/ContainerExpeditionSheet.java b/src/main/java/com/minecolonies/api/inventory/container/ContainerExpeditionSheet.java new file mode 100644 index 00000000000..66973a6d9af --- /dev/null +++ b/src/main/java/com/minecolonies/api/inventory/container/ContainerExpeditionSheet.java @@ -0,0 +1,145 @@ +package com.minecolonies.api.inventory.container; + +import com.minecolonies.api.inventory.ModContainers; +import com.minecolonies.api.util.ItemStackUtils; +import com.minecolonies.core.items.ItemExpeditionSheet.ExpeditionSheetContainerManager; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import static com.minecolonies.api.util.constant.InventoryConstants.*; + +/** + * Container for the expedition sheet item inventory. + */ +public class ContainerExpeditionSheet extends AbstractContainerMenu +{ + /** + * Amount of rows. + */ + private final int inventoryRows; + + /** + * The container constructor. + * + * @param windowId the window id. + * @param playerInventory the player inventory. + * @param stack the item stack containing the expedition sheet. + */ + public ContainerExpeditionSheet(final int windowId, final Inventory playerInventory, final ItemStack stack) + { + super(ModContainers.expeditionSheet.get(), windowId); + final ExpeditionSheetContainerManager inventory = new ExpeditionSheetContainerManager(stack); + + inventoryRows = inventory.getContainerSize() / INVENTORY_COLUMNS; + final int size = inventory.getContainerSize(); + + final int columns = inventoryRows <= INVENTORY_BAR_SIZE ? INVENTORY_COLUMNS : ((size / INVENTORY_BAR_SIZE) + 1); + final int extraOffset = inventoryRows <= INVENTORY_BAR_SIZE ? 0 : 2; + int index = 0; + + for (int j = 0; j < Math.min(this.inventoryRows, INVENTORY_BAR_SIZE); ++j) + { + for (int k = 0; k < columns; ++k) + { + if (index < size) + { + this.addSlot(new Slot(inventory, + index, + INVENTORY_BAR_SIZE + k * PLAYER_INVENTORY_OFFSET_EACH, + PLAYER_INVENTORY_OFFSET_EACH + j * PLAYER_INVENTORY_OFFSET_EACH)); + index++; + } + } + } + + // Player inventory slots + // Note: The slot numbers are within the player inventory and may be the same as the field inventory. + int i; + for (i = 0; i < INVENTORY_ROWS; i++) + { + for (int j = 0; j < INVENTORY_COLUMNS; j++) + { + addSlot(new Slot( + playerInventory, + j + i * INVENTORY_COLUMNS + INVENTORY_COLUMNS, + PLAYER_INVENTORY_INITIAL_X_OFFSET + j * PLAYER_INVENTORY_OFFSET_EACH, + PLAYER_INVENTORY_INITIAL_Y_OFFSET + extraOffset + PLAYER_INVENTORY_OFFSET_EACH * Math.min(this.inventoryRows, INVENTORY_BAR_SIZE) + + i * PLAYER_INVENTORY_OFFSET_EACH + )); + } + } + + for (i = 0; i < INVENTORY_COLUMNS; i++) + { + addSlot(new Slot( + playerInventory, i, + PLAYER_INVENTORY_INITIAL_X_OFFSET + i * PLAYER_INVENTORY_OFFSET_EACH, + PLAYER_INVENTORY_HOTBAR_OFFSET + extraOffset + PLAYER_INVENTORY_OFFSET_EACH * Math.min(this.inventoryRows, INVENTORY_BAR_SIZE) + )); + } + } + + /** + * Deserialize packet buffer to container instance. + * + * @param windowId the id of the window. + * @param inv the player inventory. + * @param packetBuffer network buffer + * @return new instance + */ + public static ContainerExpeditionSheet fromFriendlyByteBuf(final int windowId, final Inventory inv, final FriendlyByteBuf packetBuffer) + { + return new ContainerExpeditionSheet(windowId, inv, inv.player.getItemInHand(packetBuffer.readEnum(InteractionHand.class))); + } + + @Override + @NotNull + public ItemStack quickMoveStack(final @NotNull Player player, final int index) + { + final Slot slot = this.slots.get(index); + + if (!slot.hasItem()) + { + return ItemStackUtils.EMPTY; + } + + final ItemStack stackCopy = slot.getItem().copy(); + + final int maxIndex = this.inventoryRows * INVENTORY_COLUMNS; + + if (index < maxIndex) + { + if (!this.moveItemStackTo(stackCopy, maxIndex, this.slots.size(), true)) + { + return ItemStackUtils.EMPTY; + } + } + else if (!this.moveItemStackTo(stackCopy, 0, maxIndex, false)) + { + return ItemStackUtils.EMPTY; + } + + if (ItemStackUtils.getSize(stackCopy) == 0) + { + slot.set(ItemStackUtils.EMPTY); + } + else + { + slot.set(stackCopy); + } + + return stackCopy; + } + + @Override + public boolean stillValid(final @NotNull Player player) + { + return true; + } +} diff --git a/src/main/java/com/minecolonies/api/items/AbstractItemExpeditionSheet.java b/src/main/java/com/minecolonies/api/items/AbstractItemExpeditionSheet.java new file mode 100644 index 00000000000..b0265951a44 --- /dev/null +++ b/src/main/java/com/minecolonies/api/items/AbstractItemExpeditionSheet.java @@ -0,0 +1,49 @@ +package com.minecolonies.api.items; + +import com.minecolonies.core.items.AbstractItemMinecolonies; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Base class for the expedition sheet item. + */ +public abstract class AbstractItemExpeditionSheet extends AbstractItemMinecolonies +{ + /** + * Container class for expedition sheet information. + * + * @param colonyId the id of the colony this expedition is for. + * @param expeditionId the id of the expedition instance. + */ + public record ExpeditionSheetInfo(int colonyId, int expeditionId) {} + + /** + * Sets the name, creative tab, and registers the item. + * + * @param name The name of this item + * @param properties the properties. + */ + public AbstractItemExpeditionSheet(final String name, final Properties properties) + { + super(name, properties); + } + + /** + * Get the expedition sheet info on a given item stack. + * + * @param stack the item stack to check for. + * @return the expedition sheet info or null. + */ + @Nullable + public abstract ExpeditionSheetInfo getExpeditionSheetInfo(final ItemStack stack); + + /** + * Generate an item stack for the given expedition. + * + * @param expeditionSheetInfo the sheet info. + * @return the generated item stack. + */ + @NotNull + public abstract ItemStack createItemStackForExpedition(final ExpeditionSheetInfo expeditionSheetInfo); +} diff --git a/src/main/java/com/minecolonies/api/items/ModItems.java b/src/main/java/com/minecolonies/api/items/ModItems.java index 1a8921402ef..b9fc491b22c 100755 --- a/src/main/java/com/minecolonies/api/items/ModItems.java +++ b/src/main/java/com/minecolonies/api/items/ModItems.java @@ -27,6 +27,8 @@ public final class ModItems public static Item mistletoe; public static Item spear; + public static AbstractItemExpeditionSheet expeditionSheet; + public static Item clipboard; public static Item compost; public static Item resourceScroll; diff --git a/src/main/java/com/minecolonies/api/loot/ModLootConditions.java b/src/main/java/com/minecolonies/api/loot/ModLootConditions.java index a019dad11cd..7539f7fb1c8 100644 --- a/src/main/java/com/minecolonies/api/loot/ModLootConditions.java +++ b/src/main/java/com/minecolonies/api/loot/ModLootConditions.java @@ -1,6 +1,8 @@ package com.minecolonies.api.loot; import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeDifficulty; +import com.minecolonies.core.loot.ExpeditionDifficultyCondition; import net.minecraft.advancements.critereon.EnchantmentPredicate; import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.advancements.critereon.MinMaxBounds; @@ -9,6 +11,8 @@ import net.minecraft.tags.ItemTags; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.storage.loot.parameters.LootContextParam; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; import net.minecraft.world.level.storage.loot.predicates.MatchTool; @@ -17,29 +21,34 @@ import static com.minecolonies.api.util.constant.Constants.MOD_ID; -/** Container class for registering custom loot conditions */ +/** + * Container class for registering custom loot conditions + */ public final class ModLootConditions { public final static DeferredRegister DEFERRED_REGISTER = DeferredRegister.create(Registries.LOOT_CONDITION_TYPE, Constants.MOD_ID); - public static final ResourceLocation ENTITY_IN_BIOME_TAG_ID = new ResourceLocation(MOD_ID, "entity_in_biome_tag"); - public static final ResourceLocation RESEARCH_UNLOCKED_ID = new ResourceLocation(MOD_ID, "research_unlocked"); + public static final ResourceLocation ENTITY_IN_BIOME_TAG_ID = new ResourceLocation(MOD_ID, "entity_in_biome_tag"); + public static final ResourceLocation RESEARCH_UNLOCKED_ID = new ResourceLocation(MOD_ID, "research_unlocked"); + public static final ResourceLocation EXPEDITION_DIFFICULTY_ID = new ResourceLocation(MOD_ID, "expedition_difficulty"); + + public static final LootContextParam EXPEDITION_DIFFICULTY_PARAM = new LootContextParam<>(EXPEDITION_DIFFICULTY_ID); + public static final LootContextParamSet EXPEDITION_PARAMS = LootContextParamSet.builder() + .required(EXPEDITION_DIFFICULTY_PARAM) + .build(); public static final RegistryObject entityInBiomeTag; public static final RegistryObject researchUnlocked; // also some convenience definitions for existing conditions; some stolen from BlockLootSubProvider - public static final LootItemCondition.Builder HAS_SILK_TOUCH = MatchTool.toolMatches(ItemPredicate.Builder.item().hasEnchantment(new EnchantmentPredicate(Enchantments.SILK_TOUCH, MinMaxBounds.Ints.atLeast(1)))); - public static final LootItemCondition.Builder HAS_SHEARS = MatchTool.toolMatches(ItemPredicate.Builder.item().of(Items.SHEARS)); - public static final LootItemCondition.Builder HAS_SHEARS_OR_SILK_TOUCH = HAS_SHEARS.or(HAS_SILK_TOUCH); + public static final LootItemCondition.Builder HAS_SILK_TOUCH = + MatchTool.toolMatches(ItemPredicate.Builder.item().hasEnchantment(new EnchantmentPredicate(Enchantments.SILK_TOUCH, MinMaxBounds.Ints.atLeast(1)))); + public static final LootItemCondition.Builder HAS_SHEARS = MatchTool.toolMatches(ItemPredicate.Builder.item().of(Items.SHEARS)); + public static final LootItemCondition.Builder HAS_SHEARS_OR_SILK_TOUCH = HAS_SHEARS.or(HAS_SILK_TOUCH); public static final LootItemCondition.Builder HAS_NO_SHEARS_OR_SILK_TOUCH = HAS_SHEARS_OR_SILK_TOUCH.invert(); - public static final LootItemCondition.Builder HAS_HOE = MatchTool.toolMatches(ItemPredicate.Builder.item().of(ItemTags.HOES)); - - public static void init() - { - // just for classloading - } + public static final LootItemCondition.Builder HAS_HOE = MatchTool.toolMatches(ItemPredicate.Builder.item().of(ItemTags.HOES)); + public static final RegistryObject expeditionDifficulty; static { entityInBiomeTag = DEFERRED_REGISTER.register(ModLootConditions.ENTITY_IN_BIOME_TAG_ID.getPath(), @@ -47,11 +56,17 @@ public static void init() researchUnlocked = DEFERRED_REGISTER.register(ModLootConditions.RESEARCH_UNLOCKED_ID.getPath(), () -> new LootItemConditionType(new ResearchUnlocked.Serializer())); - } - + expeditionDifficulty = DEFERRED_REGISTER.register(ModLootConditions.EXPEDITION_DIFFICULTY_ID.getPath(), + () -> new LootItemConditionType(new ExpeditionDifficultyCondition.Serializer())); + } private ModLootConditions() { throw new IllegalStateException("Tried to initialize: ModLootConditions but this is a Utility class."); } + + public static void init() + { + // just for classloading + } } diff --git a/src/main/java/com/minecolonies/api/util/InventoryUtils.java b/src/main/java/com/minecolonies/api/util/InventoryUtils.java index 1d74bb31b49..044730fb423 100755 --- a/src/main/java/com/minecolonies/api/util/InventoryUtils.java +++ b/src/main/java/com/minecolonies/api/util/InventoryUtils.java @@ -2921,9 +2921,29 @@ public static boolean attemptReduceStackInItemHandler(final IItemHandler invWrap * @param ignoreNBT ignore NBT values. * @return true if successfully. */ - public static boolean attemptReduceStackInItemHandler(final IItemHandler invWrapper, final ItemStack itemStack, final int quantity, final boolean ignoreDamage, final boolean ignoreNBT) + public static boolean attemptReduceStackInItemHandler( + final IItemHandler invWrapper, + final ItemStack itemStack, + final int quantity, + final boolean ignoreDamage, + final boolean ignoreNBT) { - if (getItemCountInItemHandler(invWrapper, stack -> !stack.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(stack, itemStack, !ignoreDamage, !ignoreNBT)) < quantity) + return attemptReduceStackInItemHandler(invWrapper, + stack -> !stack.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(stack, itemStack, !ignoreDamage, !ignoreNBT), + quantity); + } + + /** + * Search for a certain itemStack in the inventory and decrease it by a certain quantity. + * + * @param invWrapper the inventory item handler. + * @param itemStackSelectionPredicate the predicate to match. + * @param quantity the quantity. + * @return true if successfully. + */ + public static boolean attemptReduceStackInItemHandler(final IItemHandler invWrapper, final Predicate itemStackSelectionPredicate, final int quantity) + { + if (getItemCountInItemHandler(invWrapper, itemStackSelectionPredicate) < quantity) { return false; } @@ -2932,7 +2952,7 @@ public static boolean attemptReduceStackInItemHandler(final IItemHandler invWrap for (int i = 0; i < invWrapper.getSlots(); i++) { final ItemStack stack = invWrapper.getStackInSlot(i); - if (ItemStackUtils.compareItemStacksIgnoreStackSize(stack, itemStack, !ignoreDamage, !ignoreNBT)) + if (itemStackSelectionPredicate.test(stack)) { if (stack.getCount() >= qty) { diff --git a/src/main/java/com/minecolonies/api/util/MathUtils.java b/src/main/java/com/minecolonies/api/util/MathUtils.java index 6229ea561d9..9e26bd93c0a 100755 --- a/src/main/java/com/minecolonies/api/util/MathUtils.java +++ b/src/main/java/com/minecolonies/api/util/MathUtils.java @@ -50,17 +50,4 @@ public static double twoDimDistance(@NotNull final BlockPos position, @NotNull f //Hypot returns sqrt(x²+ y²) without intermediate overflow or underflow. return Math.hypot((double) x2 - x1, (double) z2 - z1); } - - /** - * Clamped a value between a lower and upper bound. - * - * @param value the input value to clamp. - * @param min the lower bound. - * @param max the upper bound. - * @return the value, clamped between the lower and upper bound. - */ - public static int clamp(int value, int min, int max) - { - return Math.max(min, Math.min(max, value)); - } } diff --git a/src/main/java/com/minecolonies/api/util/constant/ColonyConstants.java b/src/main/java/com/minecolonies/api/util/constant/ColonyConstants.java index da97168a7e9..3685ee511f1 100755 --- a/src/main/java/com/minecolonies/api/util/constant/ColonyConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/ColonyConstants.java @@ -50,6 +50,11 @@ public final class ColonyConstants */ public static final int UPDATE_SUBSCRIBERS_INTERVAL = 20; + /** + * How often the travelers get updated in ticks. + */ + public static final int UPDATE_TRAVELING_INTERVAL = 20; + /** * How often the colony state gets updated in ticks. */ diff --git a/src/main/java/com/minecolonies/api/util/constant/ExpeditionConstants.java b/src/main/java/com/minecolonies/api/util/constant/ExpeditionConstants.java new file mode 100644 index 00000000000..5a559a5744c --- /dev/null +++ b/src/main/java/com/minecolonies/api/util/constant/ExpeditionConstants.java @@ -0,0 +1,83 @@ +package com.minecolonies.api.util.constant; + +import org.jetbrains.annotations.NonNls; + +/** + * Some constants needed for expeditions. + */ +public class ExpeditionConstants +{ + @NonNls + public static final String EXPEDITION_SHEET_DESCRIPTION_VISITOR = "com.minecolonies.core.expedition_sheet.description.visitor"; + @NonNls + public static final String EXPEDITION_SHEET_DESCRIPTION_VISITOR_LEFT = "com.minecolonies.core.expedition_sheet.description.visitor.left"; + @NonNls + public static final String EXPEDITION_SHEET_DESCRIPTION_TIMEOUT = "com.minecolonies.core.expedition_sheet.description.timeout"; + @NonNls + public static final String EXPEDITION_SHEET_DESCRIPTION_TIMEOUT_MINUTES = "com.minecolonies.core.expedition_sheet.description.timeout.minutes"; + @NonNls + public static final String EXPEDITION_SHEET_DESCRIPTION_TIMEOUT_EXPIRED = "com.minecolonies.core.expedition_sheet.description.timeout.expired"; + @NonNls + public static final String EXPEDITION_SHEET_INVENTORY = "com.minecolonies.core.expedition_sheet.inventory"; + + @NonNls + public static final String EXPEDITION_INTERACTION_ICON = "textures/icons/expedition.png"; + @NonNls + public static final String EXPEDITION_INTERACTION_VALIDATOR_ID = "com.minecolonies.core.expedition_interaction_validator_id"; + @NonNls + public static final String EXPEDITION_INTERACTION_INQUIRY_ACCEPT = "com.minecolonies.core.expedition.interaction.inquiry.accept"; + @NonNls + public static final String EXPEDITION_INTERACTION_INQUIRY_PREPARE = "com.minecolonies.core.expedition.interaction.inquiry.prepare"; + @NonNls + public static final String EXPEDITION_INTERACTION_INQUIRY_FINISHED = "com.minecolonies.core.expedition.interaction.inquiry.finished"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_CREATED_ACCEPT = "com.minecolonies.core.expedition.interaction.response.created.accept"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_CREATED_NOT_INTERESTED = "com.minecolonies.core.expedition.interaction.response.created.not_interested"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_START = "com.minecolonies.core.expedition.interaction.response.accepted.start"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_GET_SHEET = "com.minecolonies.core.expedition.interaction.response.accepted.get_sheet"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_NOT_INTERESTED = "com.minecolonies.core.expedition.interaction.response.accepted.not_interested"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_NOT_NOW = "com.minecolonies.core.expedition.interaction.response.accepted.not_now"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_FINISHED_VIEW_RESULTS = "com.minecolonies.core.expedition.interaction.response.finished.view_results"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_FINISHED_NOT_INTERESTED = "com.minecolonies.core.expedition.interaction.response.finished.not_interested"; + @NonNls + public static final String EXPEDITION_INTERACTION_RESPONSE_FINISHED_NOT_NOW = "com.minecolonies.core.expedition.interaction.response.finished.not_now"; + + @NonNls + public static final String EXPEDITION_TOWNHALL_LIST_STATUS = "com.minecolonies.core.expedition.gui.townhall.expedition_list.status."; + @NonNls + public static final String EXPEDITIONARY_DIFFICULTY = "com.minecolonies.core.expedition.gui.difficulty"; + @NonNls + public static final String EXPEDITIONARY_DIFFICULTY_PREFIX = "com.minecolonies.core.expedition.gui.difficulty."; + @NonNls + public static final String EXPEDITIONARY_ITEMS_HEADER = "com.minecolonies.core.expedition.gui.items.header"; + @NonNls + public static final String EXPEDITIONARY_ITEMS_SUBHEADER_MET = "com.minecolonies.core.expedition.gui.items.subheader.met"; + @NonNls + public static final String EXPEDITIONARY_ITEMS_SUBHEADER_NOT_MET = "com.minecolonies.core.expedition.gui.items.subheader.not_met"; + @NonNls + public static final String EXPEDITIONARY_GUARDS_HEADER = "com.minecolonies.core.expedition.gui.guards.header"; + @NonNls + public static final String EXPEDITIONARY_GUARDS_SUBHEADER_MET = "com.minecolonies.core.expedition.gui.guards.subheader.met"; + @NonNls + public static final String EXPEDITIONARY_GUARDS_SUBHEADER_NOT_MET = "com.minecolonies.core.expedition.gui.guards.subheader.not_met"; + + @NonNls + public static final String EXPEDITION_START_MESSAGE = "com.minecolonies.core.expedition.start"; + @NonNls + public static final String EXPEDITION_FINISH_MESSAGE = "com.minecolonies.core.expedition.finish"; + @NonNls + public static final String EXPEDITION_FINISHED_LEAVING_MESSAGE = "com.minecolonies.core.expedition.finished.leaving"; + @NonNls + public static final String EXPEDITION_FAILURE_MESSAGE = "com.minecolonies.core.expedition.failure"; + @NonNls + public static final String EXPEDITION_STAGE_WILDERNESS = "com.minecolonies.core.expedition.stages.wilderness"; + @NonNls + public static final String EXPEDITION_STAGE_STRUCTURE = "com.minecolonies.core.expedition.stages.structures."; +} diff --git a/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java b/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java index 909cd5730d5..09ed69b5483 100755 --- a/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/NbtTagConstants.java @@ -54,6 +54,8 @@ public final class NbtTagConstants public static final String TAG_EVENT_DESC_MANAGER = "event_desc_manager"; public static final String TAG_QUEST_MANAGER = "quest_manager"; public static final String TAG_GRAVE_MANAGER = "graveManager"; + public static final String TAG_EXPEDITION_MANAGER = "expedition_manager"; + public static final String TAG_TRAVELING_MANAGER = "traveling_manager"; public static final String TAG_COLONY_ID = "colony"; public static final String TAG_CITIZEN = "citizen"; public static final String TAG_HELD_ITEM_SLOT = "HeldItemSlot"; @@ -730,6 +732,14 @@ public final class NbtTagConstants public static final String TAG_WEIGHT = "weight"; public static final String TAG_SUPPLIER = "supplier"; + /** + * Traveling nbt tags. + */ + public static final String TAG_INITIAL_TRAVEL_TIME = "initialTravelTime"; + public static final String TAG_REMAINING_TRAVEL_TIME = "remainingTravelTime"; + public static final String TAG_CAN_RECALL = "canRecall"; + public static final String TRAVELER_DATA = "travelerData"; + /** * Supplycamp related things. */ diff --git a/src/main/java/com/minecolonies/api/util/constant/TranslationConstants.java b/src/main/java/com/minecolonies/api/util/constant/TranslationConstants.java index a9409737970..50b60def2aa 100755 --- a/src/main/java/com/minecolonies/api/util/constant/TranslationConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/TranslationConstants.java @@ -803,6 +803,13 @@ public final class TranslationConstants // + // + + @NonNls + public static final String INTERACTION_EXPEDITIONARY = "com.minecolonies.coremod.interaction.visitor.expeditionary"; + + // + private TranslationConstants() { // empty default diff --git a/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java b/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java index 5c1bafafe4f..b91c4c6bb5f 100755 --- a/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java @@ -84,6 +84,11 @@ public final class WindowConstants */ public static final String BUTTON_MERCENARY = "mercenaries"; + /** + * Id of the mercenary button in the GUI. + */ + public static final String BUTTON_EXPEDITIONS = "expeditions"; + /** * Id of the mercenary button in the GUI. */ @@ -610,35 +615,40 @@ public final class WindowConstants public static final String LABEL_PAGE_NUMBER = "pageNum"; /** - * The label to find the gui of the citizen. + * The id for the citizen main page. */ public static final String CITIZEN_MAIN_RESOURCE_SUFFIX = ":gui/citizen/main.xml"; /** - * The label to find the gui of the citizen. + * The id for the citizen requests page. */ public static final String CITIZEN_REQ_RESOURCE_SUFFIX = ":gui/citizen/requests.xml"; /** - * The label to find the gui of the citizen. + * The id for the citizen request detail page. */ public static final String CITIZEN_REQ_DETAIL_SUFFIX = ":gui/windowrequestdetail.xml"; /** - * The label to find the gui of the citizen. + * The id for the citizen happiness page. */ public static final String CITIZEN_HAP_RESOURCE_SUFFIX = ":gui/citizen/happiness.xml"; /** - * The label to find the gui of the citizen. + * The id for the citizen job page. */ public static final String CITIZEN_JOB_RESOURCE_SUFFIX = ":gui/citizen/job.xml"; /** - * The label to find the gui of the citizen. + * The id for the citizen family page. */ public static final String CITIZEN_FAM_RESOURCE_SUFFIX = ":gui/citizen/family.xml"; + /** + * The id for the expeditionary visitor main page. + */ + public static final String EXPEDITIONARY_MAIN_RESOURCE_SUFFIX = ":gui/visitor/expeditionary/main.xml"; + /** * Id of the resource add button. */ diff --git a/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java b/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java index 7400bf0fa5e..9178cc75d01 100755 --- a/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java +++ b/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java @@ -22,6 +22,7 @@ import com.minecolonies.api.crafting.registry.RecipeTypeEntry; import com.minecolonies.api.entity.mobs.registry.IMobAIRegistry; import com.minecolonies.api.entity.citizen.happiness.HappinessRegistry; +import com.minecolonies.api.entity.visitor.IVisitorType; import com.minecolonies.api.entity.pathfinding.registry.IPathNavigateRegistry; import com.minecolonies.api.equipment.registry.EquipmentTypeEntry; import com.minecolonies.api.quests.registries.QuestRegistries; @@ -80,6 +81,7 @@ public class CommonMinecoloniesAPIImpl implements IMinecoloniesAPI private IForgeRegistry questDialogueAnswerRegistry; private IForgeRegistry happinessFactorTypeRegistry; private IForgeRegistry happinessFunctionRegistry; + private IForgeRegistry visitorTypeRegistry; @Override @NotNull @@ -202,6 +204,12 @@ public IForgeRegistry getResearchCostRegistry() return researchCostRegistry; } + @Override + public IForgeRegistry getVisitorTypeRegistry() + { + return visitorTypeRegistry; + } + @Override public void onRegistryNewRegistry(final NewRegistryEvent event) { @@ -326,6 +334,12 @@ public void onRegistryNewRegistry(final NewRegistryEvent event) .setDefaultKey(new ResourceLocation(Constants.MOD_ID, "null")) .disableSaving().allowModification() .setIDRange(0, Integer.MAX_VALUE - 1), (b) -> happinessFunctionRegistry = b); + + event.create(new RegistryBuilder() + .setName(new ResourceLocation(Constants.MOD_ID, "visitortypes")) + .setDefaultKey(new ResourceLocation(Constants.MOD_ID, "null")) + .disableSaving().allowModification() + .setIDRange(0, Integer.MAX_VALUE - 1), (b) -> visitorTypeRegistry = b); } @Override diff --git a/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java index 373e54b1bea..8d3ee0a3383 100644 --- a/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java @@ -24,6 +24,8 @@ import com.minecolonies.core.entity.mobs.pirates.EntityArcherPirate; import com.minecolonies.core.entity.mobs.pirates.EntityCaptainPirate; import com.minecolonies.core.entity.mobs.pirates.EntityPirate; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType; +import com.minecolonies.core.entity.visitor.RegularVisitorType; import com.minecolonies.core.entity.other.*; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; @@ -64,11 +66,19 @@ public static void setupEntities(RegisterEvent event) .setShouldReceiveVelocityUpdates(true) .setCustomClientFactory(NewBobberEntity::new)); - ModEntities.VISITOR = build(registry, "visitor", EntityType.Builder.of(VisitorCitizen::new, MobCategory.CREATURE) - .setTrackingRange(ENTITY_TRACKING_RANGE) - .setUpdateInterval(ENTITY_UPDATE_FREQUENCY) - .sized((float) CITIZEN_WIDTH, (float) CITIZEN_HEIGHT) - .setShouldReceiveVelocityUpdates(true)); + ModEntities.VISITOR = build(registry, "visitor", + EntityType.Builder.of(VisitorCitizen.forVisitorType(new RegularVisitorType()), MobCategory.CREATURE) + .setTrackingRange(ENTITY_TRACKING_RANGE) + .setUpdateInterval(ENTITY_UPDATE_FREQUENCY) + .sized((float) CITIZEN_WIDTH, (float) CITIZEN_HEIGHT) + .setShouldReceiveVelocityUpdates(true)); + + ModEntities.EXPEDITIONARY = build(registry, "expeditionary", + EntityType.Builder.of(VisitorCitizen.forVisitorType(new ExpeditionaryVisitorType()), MobCategory.CREATURE) + .setTrackingRange(ENTITY_TRACKING_RANGE) + .setUpdateInterval(ENTITY_UPDATE_FREQUENCY) + .sized((float) CITIZEN_WIDTH, (float) CITIZEN_HEIGHT) + .setShouldReceiveVelocityUpdates(true)); ModEntities.MERCENARY = build(registry, "mercenary", EntityType.Builder.of(EntityMercenary::new, MobCategory.CREATURE) diff --git a/src/main/java/com/minecolonies/apiimp/initializer/InteractionValidatorInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/InteractionValidatorInitializer.java index 61b4d86329c..4c23b0dc161 100755 --- a/src/main/java/com/minecolonies/apiimp/initializer/InteractionValidatorInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/InteractionValidatorInitializer.java @@ -2,12 +2,14 @@ import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.IColonyManager; +import com.minecolonies.api.colony.IVisitorData; import com.minecolonies.api.colony.buildings.IBuilding; import com.minecolonies.api.colony.interactionhandling.InteractionValidatorRegistry; import com.minecolonies.api.colony.requestsystem.request.RequestUtils; import com.minecolonies.api.crafting.ItemStorage; import com.minecolonies.api.entity.citizen.citizenhandlers.ICitizenFoodHandler; import com.minecolonies.api.entity.citizen.happiness.ITimeBasedHappinessModifier; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; import com.minecolonies.api.items.ModTags; import com.minecolonies.api.util.FoodUtils; import com.minecolonies.api.util.InventoryUtils; @@ -30,6 +32,7 @@ import static com.minecolonies.api.util.ItemStackUtils.*; import static com.minecolonies.api.util.constant.BuildingConstants.BUILDING_FLOWER_LIST; import static com.minecolonies.api.util.constant.CitizenConstants.LOW_SATURATION; +import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITION_INTERACTION_VALIDATOR_ID; import static com.minecolonies.api.util.constant.HappinessConstants.*; import static com.minecolonies.api.util.constant.TranslationConstants.*; import static com.minecolonies.api.util.constant.translation.RequestSystemTranslationConstants.REQUEST_RESOLVER_NORMAL; @@ -353,5 +356,8 @@ public static void init() InteractionValidatorRegistry.registerStandardPredicate(Component.translatable(QUARRY_MINER_FINISHED_QUARRY), citizen -> citizen.getJob() instanceof JobQuarrier && ((JobQuarrier) citizen.getJob()).findQuarry() != null && ((JobQuarrier) citizen.getJob()).findQuarry().getFirstModuleOccurance(QuarryModule.class).isFinished()); + + InteractionValidatorRegistry.registerStandardPredicate(Component.translatable(EXPEDITION_INTERACTION_VALIDATOR_ID), + data -> data instanceof IVisitorData visitorData && visitorData.getVisitorType().equals(ModVisitorTypes.expeditionary.get())); } } diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java index 6b3b984d77f..ed8e5a34575 100755 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java @@ -10,6 +10,7 @@ import com.minecolonies.core.colony.events.raid.pirateEvent.DrownedPirateRaidEvent; import com.minecolonies.core.colony.events.raid.pirateEvent.PirateGroundRaidEvent; import com.minecolonies.core.colony.events.raid.pirateEvent.PirateRaidEvent; +import com.minecolonies.core.colony.events.ColonyExpeditionEvent; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.registries.DeferredRegister; @@ -35,5 +36,6 @@ private ModColonyEventTypeInitializer() DEFERRED_REGISTER.register(NorsemenShipRaidEvent.NORSEMEN_RAID_EVENT_TYPE_ID.getPath(), () -> new ColonyEventTypeRegistryEntry(NorsemenShipRaidEvent::loadFromNBT, NorsemenShipRaidEvent.NORSEMEN_RAID_EVENT_TYPE_ID)); DEFERRED_REGISTER.register(PirateGroundRaidEvent.PIRATE_GROUND_RAID_EVENT_TYPE_ID.getPath(), () -> new ColonyEventTypeRegistryEntry(PirateGroundRaidEvent::loadFromNBT, PirateGroundRaidEvent.PIRATE_GROUND_RAID_EVENT_TYPE_ID)); DEFERRED_REGISTER.register(DrownedPirateRaidEvent.PIRATE_RAID_EVENT_TYPE_ID.getPath(), () -> new ColonyEventTypeRegistryEntry(DrownedPirateRaidEvent::loadFromNBT, DrownedPirateRaidEvent.PIRATE_RAID_EVENT_TYPE_ID)); + DEFERRED_REGISTER.register(ColonyExpeditionEvent.COLONY_EXPEDITION_EVENT_TYPE_ID.getPath(), () -> new ColonyEventTypeRegistryEntry(ColonyExpeditionEvent::loadFromNBT, ColonyExpeditionEvent.COLONY_EXPEDITION_EVENT_TYPE_ID)); } } diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModContainerInitializers.java b/src/main/java/com/minecolonies/apiimp/initializer/ModContainerInitializers.java index 09292d9ab0e..f1b4f293202 100755 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModContainerInitializers.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModContainerInitializers.java @@ -26,6 +26,7 @@ public class ModContainerInitializers ModContainers.rackInv = CONTAINERS.register("rack_inv", () -> IForgeMenuType.create(ContainerRack::fromFriendlyByteBuf)); ModContainers.graveInv = CONTAINERS.register("grave_inv", () -> IForgeMenuType.create(ContainerGrave::fromFriendlyByteBuf)); ModContainers.craftingBrewingstand = CONTAINERS.register("crafting_brewingstand", () -> IForgeMenuType.create(ContainerCraftingBrewingstand::fromFriendlyByteBuf)); + ModContainers.expeditionSheet = CONTAINERS.register("expedition_sheet", () -> IForgeMenuType.create(ContainerExpeditionSheet::fromFriendlyByteBuf)); } @SubscribeEvent public static void doClientStuff(final FMLClientSetupEvent event) @@ -38,5 +39,6 @@ public static void doClientStuff(final FMLClientSetupEvent event) MenuScreens.register(ModContainers.citizenInv.get(), WindowCitizenInventory::new); MenuScreens.register(ModContainers.rackInv.get(), WindowRack::new); MenuScreens.register(ModContainers.graveInv.get(), WindowGrave::new); + MenuScreens.register(ModContainers.expeditionSheet.get(), WindowExpeditionSheet::new); } } diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModInteractionsInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModInteractionsInitializer.java index bfbe9af63a5..25ba6dad9e2 100755 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModInteractionsInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModInteractionsInitializer.java @@ -52,5 +52,11 @@ private ModInteractionsInitializer() .setResponseHandlerProducer(QuestDeliveryInteraction::new) .setRegistryName(ModInteractionResponseHandlers.QUEST_ACTION) .createEntry()); + + ModInteractionResponseHandlers.expedition = DEFERRED_REGISTER.register(ModInteractionResponseHandlers.EXPEDITION.getPath(), + () -> new InteractionResponseHandlerEntry.Builder() + .setResponseHandlerProducer(ExpeditionInteraction::new) + .setRegistryName(ModInteractionResponseHandlers.EXPEDITION) + .createEntry()); } } diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModItemsInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModItemsInitializer.java index bd3c64a992e..6a39f3771a4 100755 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModItemsInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModItemsInitializer.java @@ -76,6 +76,7 @@ public static void init(final IForgeRegistry registry) ModItems.mistletoe = new ItemMistletoe(new Item.Properties()); ModItems.spear = new ItemSpear(new Item.Properties()); ModItems.questLog = new ItemQuestLog(new Item.Properties()); + ModItems.expeditionSheet = new ItemExpeditionSheet(new Item.Properties()); ModItems.breadDough = new ItemBreadDough(new Item.Properties()); ModItems.cookieDough = new ItemCookieDough(new Item.Properties()); @@ -262,6 +263,7 @@ public static void init(final IForgeRegistry registry) registry.register(new ResourceLocation(Constants.MOD_ID, "mistletoe"), ModItems.mistletoe); registry.register(new ResourceLocation(Constants.MOD_ID, "spear"), ModItems.spear); registry.register(new ResourceLocation(Constants.MOD_ID, "questlog"), ModItems.questLog); + registry.register(new ResourceLocation(Constants.MOD_ID, "expedition_sheet"), ModItems.expeditionSheet); registry.register(new ResourceLocation(Constants.MOD_ID, "bread_dough"), ModItems.breadDough); registry.register(new ResourceLocation(Constants.MOD_ID, "cookie_dough"), ModItems.cookieDough); diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java new file mode 100644 index 00000000000..1609338145b --- /dev/null +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java @@ -0,0 +1,27 @@ +package com.minecolonies.apiimp.initializer; + +import com.minecolonies.api.entity.visitor.IVisitorType; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType; +import com.minecolonies.core.entity.visitor.RegularVisitorType; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.DeferredRegister; + +/** + * Initializer for the {@link ModVisitorTypes}. + */ +public class ModVisitorTypesInitializer +{ + public static final DeferredRegister + DEFERRED_REGISTER = DeferredRegister.create(new ResourceLocation(Constants.MOD_ID, "visitortypes"), Constants.MOD_ID); + static + { + ModVisitorTypes.visitor = DEFERRED_REGISTER.register(ModVisitorTypes.VISITOR_TYPE_ID.getPath(), RegularVisitorType::new); + ModVisitorTypes.expeditionary = DEFERRED_REGISTER.register(ModVisitorTypes.EXPEDITIONARY_VISITOR_TYPE_ID.getPath(), ExpeditionaryVisitorType::new); + } + private ModVisitorTypesInitializer() + { + throw new IllegalStateException("Tried to initialize: ModVisitorTypesInitializer but this is a Utility class."); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/MineColonies.java b/src/main/java/com/minecolonies/core/MineColonies.java index c599e54483d..1e078c9ab05 100755 --- a/src/main/java/com/minecolonies/core/MineColonies.java +++ b/src/main/java/com/minecolonies/core/MineColonies.java @@ -97,6 +97,7 @@ public MineColonies() ModLootConditions.DEFERRED_REGISTER.register(FMLJavaModLoadingContext.get().getModEventBus()); SupplyLoot.GLM.register(FMLJavaModLoadingContext.get().getModEventBus()); ModBannerPatterns.BANNER_PATTERNS.register(FMLJavaModLoadingContext.get().getModEventBus()); + ModVisitorTypesInitializer.DEFERRED_REGISTER.register(FMLJavaModLoadingContext.get().getModEventBus()); ModQuestInitializer.DEFERRED_REGISTER_OBJECTIVE.register(FMLJavaModLoadingContext.get().getModEventBus()); ModQuestInitializer.DEFERRED_REGISTER_TRIGGER.register(FMLJavaModLoadingContext.get().getModEventBus()); @@ -178,6 +179,7 @@ public static void createEntityAttribute(final EntityAttributeCreationEvent even { event.put(ModEntities.CITIZEN, AbstractEntityCitizen.getDefaultAttributes().build()); event.put(ModEntities.VISITOR, AbstractEntityCitizen.getDefaultAttributes().build()); + event.put(ModEntities.EXPEDITIONARY, AbstractEntityCitizen.getDefaultAttributes().build()); event.put(ModEntities.MERCENARY, EntityMercenary.getDefaultAttributes().build()); event.put(ModEntities.BARBARIAN, AbstractEntityRaiderMob.getDefaultAttributes().build()); event.put(ModEntities.ARCHERBARBARIAN, AbstractEntityRaiderMob.getDefaultAttributes().build()); diff --git a/src/main/java/com/minecolonies/core/client/gui/WindowHireWorker.java b/src/main/java/com/minecolonies/core/client/gui/WindowHireWorker.java index 52bc89a8f82..3a878d3f56e 100755 --- a/src/main/java/com/minecolonies/core/client/gui/WindowHireWorker.java +++ b/src/main/java/com/minecolonies/core/client/gui/WindowHireWorker.java @@ -38,7 +38,7 @@ import static com.minecolonies.api.util.constant.TranslationConstants.*; import static com.minecolonies.api.util.constant.WindowConstants.*; -import static com.minecolonies.core.client.gui.modules.WindowBuilderResModule.BLACK; +import static com.minecolonies.core.client.gui.generic.ResourceItem.BLACK; /** * BOWindow for the hiring or firing of a worker. diff --git a/src/main/java/com/minecolonies/core/client/gui/WindowInteraction.java b/src/main/java/com/minecolonies/core/client/gui/WindowInteraction.java index b9655d18fc4..b59ea052c98 100755 --- a/src/main/java/com/minecolonies/core/client/gui/WindowInteraction.java +++ b/src/main/java/com/minecolonies/core/client/gui/WindowInteraction.java @@ -108,9 +108,9 @@ private void setupInteraction() final Text chatText = findPaneOfTypeByID(CHAT_LABEL_ID, Text.class); chatText.setTextAlignment(Alignment.TOP_LEFT); chatText.setAlignment(Alignment.TOP_LEFT); - chatText.setText(Component.literal(citizen.getName() + ": " + handler.getInquiry(Minecraft.getInstance().player).getString())); + chatText.setText(Component.literal(citizen.getName() + ": " + handler.getInquiry(Minecraft.getInstance().player, citizen).getString())); int responseIndex = 1; - for (final Component component : handler.getPossibleResponses()) + for (final Component component : handler.getPossibleResponses(citizen)) { final ButtonImage button = new ButtonImage(); button.setImage(new ResourceLocation(Constants.MOD_ID, MEDIUM_SIZED_BUTTON_RES), false); diff --git a/src/main/java/com/minecolonies/core/client/gui/WindowResourceList.java b/src/main/java/com/minecolonies/core/client/gui/WindowResourceList.java index b1d4b32ae76..4a5e42f7b01 100755 --- a/src/main/java/com/minecolonies/core/client/gui/WindowResourceList.java +++ b/src/main/java/com/minecolonies/core/client/gui/WindowResourceList.java @@ -16,6 +16,7 @@ import com.minecolonies.api.util.ItemStackUtils; import com.minecolonies.api.util.constant.Constants; import com.minecolonies.core.Network; +import com.minecolonies.core.client.gui.generic.ResourceItem.ResourceComparator; import com.minecolonies.core.colony.buildings.moduleviews.BuildingResourcesModuleView; import com.minecolonies.core.colony.buildings.utils.BuildingBuilderResource; import com.minecolonies.core.colony.buildings.workerbuildings.BuildingBuilder; @@ -32,8 +33,7 @@ import java.util.Map; import static com.minecolonies.api.util.constant.WindowConstants.*; -import static com.minecolonies.core.client.gui.modules.WindowBuilderResModule.*; -import static com.minecolonies.core.colony.buildings.utils.BuildingBuilderResource.RessourceAvailability.*; +import static com.minecolonies.core.client.gui.generic.ResourceItem.*; /** * BOWindow for the resource list item. @@ -115,7 +115,7 @@ private void pullResourcesFromHut() resource.setAmountInDelivery(resource.getAmountInDelivery() + delivery.getStack().getCount()); } } - supplied += Math.min(resource.getAvailable(), resource.getAmount()); + supplied += Math.min(resource.getAmountAvailable(), resource.getAmount()); total += resource.getAmount(); } @@ -127,7 +127,7 @@ private void pullResourcesFromHut() moduleView.getProgress() + "%")); } - resources.sort(new BuildingBuilderResource.ResourceComparator(NOT_NEEDED, HAVE_ENOUGH, IN_DELIVERY, NEED_MORE, DONT_HAVE)); + resources.sort(new ResourceComparator()); } /** @@ -254,8 +254,8 @@ else if (warehouseAmount > 0) break; } - resourceLabel.setText(Component.literal(resource.getName())); - final int missing = resource.getMissingFromPlayer(); + resourceLabel.setText(resource.getName()); + final int missing = resource.getAmountPlayer() + resource.getAmountAvailable() - resource.getAmount(); if (missing < 0) { resourceMissingLabel.setText(Component.literal(Integer.toString(missing))); @@ -265,9 +265,9 @@ else if (warehouseAmount > 0) resourceMissingLabel.clearText(); } - neededLabel.setText(Component.literal(resource.getAvailable() + " / " + resource.getAmount())); + neededLabel.setText(Component.literal(resource.getAmountAvailable() + " / " + resource.getAmount())); rowPane.findPaneOfTypeByID(RESOURCE_ID, Text.class).setText(Component.literal(Integer.toString(index))); - rowPane.findPaneOfTypeByID(RESOURCE_QUANTITY_MISSING, Text.class).setText(Component.literal(Integer.toString(resource.getAmount() - resource.getAvailable()))); + rowPane.findPaneOfTypeByID(RESOURCE_QUANTITY_MISSING, Text.class).setText(Component.literal(Integer.toString(resource.getAmount() - resource.getAmountAvailable()))); final ItemStack stack = new ItemStack(resource.getItem(), 1); stack.setTag(resource.getItemStack().getTag()); diff --git a/src/main/java/com/minecolonies/core/client/gui/blockui/RotatingItemIcon.java b/src/main/java/com/minecolonies/core/client/gui/blockui/RotatingItemIcon.java index 52037ccefb2..843ff3ee7df 100644 --- a/src/main/java/com/minecolonies/core/client/gui/blockui/RotatingItemIcon.java +++ b/src/main/java/com/minecolonies/core/client/gui/blockui/RotatingItemIcon.java @@ -55,8 +55,13 @@ public void setItems(@NotNull final List items) throw new IllegalArgumentException("Items list must contain at least one item."); } + final boolean hasChanged = !this.items.equals(items); this.items = items; - resetState(); + + if (hasChanged) + { + resetState(); + } } /** diff --git a/src/main/java/com/minecolonies/core/client/gui/citizen/CitizenWindowUtils.java b/src/main/java/com/minecolonies/core/client/gui/citizen/CitizenWindowUtils.java index 0099e642df2..c7c8530116e 100644 --- a/src/main/java/com/minecolonies/core/client/gui/citizen/CitizenWindowUtils.java +++ b/src/main/java/com/minecolonies/core/client/gui/citizen/CitizenWindowUtils.java @@ -29,7 +29,7 @@ import static com.minecolonies.api.util.constant.TranslationConstants.*; import static com.minecolonies.api.util.constant.WindowConstants.*; -import static com.minecolonies.core.client.gui.modules.WindowBuilderResModule.BLACK; +import static com.minecolonies.core.client.gui.generic.ResourceItem.BLACK; import static com.minecolonies.core.entity.citizen.citizenhandlers.CitizenExperienceHandler.PRIMARY_DEPENDENCY_SHARE; import static com.minecolonies.core.entity.citizen.citizenhandlers.CitizenExperienceHandler.SECONDARY_DEPENDENCY_SHARE; import static net.minecraft.client.gui.Gui.GUI_ICONS_LOCATION; diff --git a/src/main/java/com/minecolonies/core/client/gui/containers/WindowExpeditionSheet.java b/src/main/java/com/minecolonies/core/client/gui/containers/WindowExpeditionSheet.java new file mode 100644 index 00000000000..2ed1514c3e2 --- /dev/null +++ b/src/main/java/com/minecolonies/core/client/gui/containers/WindowExpeditionSheet.java @@ -0,0 +1,88 @@ +package com.minecolonies.core.client.gui.containers; + +import com.minecolonies.api.inventory.container.ContainerExpeditionSheet; +import com.minecolonies.api.util.constant.Constants; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import org.jetbrains.annotations.NotNull; + +import static com.minecolonies.api.util.constant.InventoryConstants.*; + +/** + * The window shown when opening the expedition sheet. + */ +public class WindowExpeditionSheet extends AbstractContainerScreen +{ + /** + * The resource LOCATION of the texture. + */ + private static final ResourceLocation CHEST_GUI_TEXTURE = new ResourceLocation(Constants.MOD_ID, "textures/gui/generic_108.png"); + + /** + * Size of the custom texture. + */ + private static final int TEXTURE_SIZE = 350; + + /** + * General y offset. + */ + private static final int Y_OFFSET = 114; + + /** + * Offset of the screen for the texture. + */ + private static final int TEXTURE_HEIGHT = 96; + + /** + * Offset inside the texture to use. + */ + private static final int TEXTURE_OFFSET = 126 * 2 - 17; + + /** + * Used to calculate the window height. + */ + private final int inventoryRows; + + /** + * Factory constructor. + * + * @param container the container instance. + * @param playerInventory the player inventory instance. + * @param component the header component. + */ + public WindowExpeditionSheet(final ContainerExpeditionSheet container, final Inventory playerInventory, final Component component) + { + super(container, playerInventory, component); + this.inventoryRows = (container.getItems().size() - 36) / 9; + + this.imageHeight = Y_OFFSET + Math.min(INVENTORY_COLUMNS, this.inventoryRows) * INVENTORY_OFFSET_EACH; + this.imageWidth = 245; + } + + @Override + public void render(@NotNull final GuiGraphics stack, int x, int y, float z) + { + this.renderBackground(stack); + super.render(stack, x, y, z); + this.renderTooltip(stack, x, y); + } + + @Override + protected void renderLabels(@NotNull final GuiGraphics stack, int mouseX, int mouseY) + { + stack.drawString(this.font, this.title.getString(), 8, 6, 4210752, false); + stack.drawString(this.font, this.playerInventoryTitle.getString(), 8, (this.imageHeight - 94), 4210752, false); + } + + @Override + protected void renderBg(@NotNull final GuiGraphics stack, final float partialTicks, final int mouseX, final int mouseY) + { + final int rowsHeight = this.inventoryRows * PLAYER_INVENTORY_OFFSET_EACH + PLAYER_INVENTORY_OFFSET_EACH - 1; + stack.blit(CHEST_GUI_TEXTURE, this.leftPos, this.topPos, 0, 0, this.imageWidth, rowsHeight, TEXTURE_SIZE, TEXTURE_SIZE); + stack.blit(CHEST_GUI_TEXTURE, this.leftPos, this.topPos + rowsHeight, 0, + TEXTURE_OFFSET, this.imageWidth, TEXTURE_HEIGHT, TEXTURE_SIZE, TEXTURE_SIZE); + } +} diff --git a/src/main/java/com/minecolonies/core/client/gui/expedition_sheet/WindowExpeditionSheet.java b/src/main/java/com/minecolonies/core/client/gui/expedition_sheet/WindowExpeditionSheet.java new file mode 100644 index 00000000000..d4fc157880f --- /dev/null +++ b/src/main/java/com/minecolonies/core/client/gui/expedition_sheet/WindowExpeditionSheet.java @@ -0,0 +1,378 @@ +package com.minecolonies.core.client.gui.expedition_sheet; + +import com.ldtteam.blockui.Pane; +import com.ldtteam.blockui.PaneBuilders; +import com.ldtteam.blockui.controls.Button; +import com.ldtteam.blockui.controls.Image; +import com.ldtteam.blockui.controls.ItemIcon; +import com.ldtteam.blockui.controls.Text; +import com.ldtteam.blockui.support.DataProviders.CheckListDataProvider; +import com.ldtteam.blockui.views.ScrollingList; +import com.ldtteam.blockui.views.ScrollingList.DataProvider; +import com.ldtteam.blockui.views.View; +import com.minecolonies.api.colony.ICitizenDataView; +import com.minecolonies.api.colony.IColonyView; +import com.minecolonies.api.colony.managers.interfaces.expeditions.ColonyExpedition.GuardsComparator; +import com.minecolonies.api.equipment.ModEquipmentTypes; +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.Log; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.Network; +import com.minecolonies.core.client.gui.AbstractWindowSkeleton; +import com.minecolonies.core.client.gui.generic.ResourceItem; +import com.minecolonies.core.client.gui.generic.ResourceItem.ResourceAvailability; +import com.minecolonies.core.client.gui.generic.ResourceItem.ResourceComparator; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement.RequirementHandler; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeDifficulty; +import com.minecolonies.core.items.ItemExpeditionSheet; +import com.minecolonies.core.network.messages.server.OpenExpeditionSheetInventoryMessage; +import com.minecolonies.core.network.messages.server.colony.visitor.expeditionary.AssignGuardMessage; +import com.minecolonies.core.network.messages.server.colony.visitor.expeditionary.TransferItemsMessage; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.wrapper.InvWrapper; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static com.minecolonies.api.util.constant.ExpeditionConstants.*; +import static com.minecolonies.api.util.constant.WindowConstants.*; + +/** + * Main window for the expedition sheet GUI. + */ +public class WindowExpeditionSheet extends AbstractWindowSkeleton +{ + /** + * Window constants. + */ + private static final String ID_EXPEDITION_NAME = "expedition_name"; + private static final String ID_EXPEDITION_DIFFICULTY = "expedition_difficulty"; + private static final String ID_EXPEDITION_ITEMS = "expedition_items"; + private static final String ID_EXPEDITION_ITEMS_HEADER = "expedition_items_header"; + private static final String ID_EXPEDITION_ITEMS_INVENTORY = "expedition_inventory"; + private static final String ID_EXPEDITION_ITEMS_SUBHEADER = "expedition_items_subheader"; + private static final String ID_EXPEDITION_GUARDS = "expedition_guards"; + private static final String ID_EXPEDITION_GUARDS_HEADER = "expedition_guards_header"; + private static final String ID_EXPEDITION_GUARDS_SUBHEADER = "expedition_guards_subheader"; + private static final String ID_EXPEDITION_GUARDS_NAME = "citizen"; + private static final String ID_EXPEDITION_GUARDS_WEAPON = "weapon"; + private static final String ID_EXPEDITION_GUARDS_HELMET = "helmet"; + private static final String ID_EXPEDITION_GUARDS_CHESTPLATE = "chestplate"; + private static final String ID_EXPEDITION_GUARDS_LEGGINGS = "leggings"; + private static final String ID_EXPEDITION_GUARDS_BOOTS = "boots"; + private static final String ID_EXPEDITION_GUARDS_ASSIGN = "guardAssign"; + + /** + * The colony view. + */ + @NotNull + private final IColonyView colonyView; + + /** + * The current expedition type. + */ + @NotNull + private final ColonyExpeditionType expeditionType; + + /** + * Which hand the player used to open the GUI with from item. + */ + @NotNull + private final InteractionHand hand; + + /** + * The container surrounding the item stack for the expedition. + */ + @NotNull + private final ItemExpeditionSheet.ExpeditionSheetContainerManager container; + + /** + * The comparator instance for the resources list. + */ + private final ResourceComparator resourceComparator; + + /** + * The comparator instance for the guards list. + */ + private final GuardsComparator guardsComparator; + + /** + * The requirements for this expedition type. + */ + private final List requirements; + + /** + * The list of guards in the colony. + */ + private final List guards; + + private final ScrollingList itemsList; + private final ScrollingList guardsList; + + /** + * Default constructor. + */ + public WindowExpeditionSheet( + final @NotNull IColonyView colonyView, + final @NotNull ColonyExpeditionType expeditionType, + final @NotNull InteractionHand hand, + final @NotNull ItemExpeditionSheet.ExpeditionSheetContainerManager container) + { + super(Constants.MOD_ID + EXPEDITIONARY_MAIN_RESOURCE_SUFFIX); + this.colonyView = colonyView; + this.expeditionType = expeditionType; + this.hand = hand; + this.container = container; + + resourceComparator = new ResourceComparator(); + requirements = expeditionType.requirements().stream().map(m -> m.createHandler(new InvWrapper(container))).collect(Collectors.toList()); + requirements.sort(resourceComparator); + + guardsComparator = new GuardsComparator(container.getMembers()); + guards = colonyView.getCitizens().values().stream() + .filter(f -> f.getJobView() != null && f.getJobView().isGuard() && f.getJobView().isCombatGuard() && !f.getColony() + .getTravelingManager() + .isTravelling(f.getId())) + .collect(Collectors.toList()); + guards.sort(guardsComparator); + + itemsList = findPaneOfTypeByID(ID_EXPEDITION_ITEMS, ScrollingList.class); + guardsList = findPaneOfTypeByID(ID_EXPEDITION_GUARDS, ScrollingList.class); + + registerButton(RESOURCE_ADD, this::transferItems); + registerButton(ID_EXPEDITION_ITEMS_INVENTORY, this::openVisitorInventory); + } + + /** + * Find the first possible weapon in the inventory (swords or bows) + * + * @param inventory the target inventory. + * @return the item stack containing the weapon or empty. + */ + public static ItemStack getFirstWeapon(final IItemHandler inventory) + { + final int swordSlot = InventoryUtils.getFirstSlotOfItemHandlerContainingEquipment(inventory, ModEquipmentTypes.sword.get(), 0, 5); + if (swordSlot != -1) + { + return inventory.getStackInSlot(swordSlot); + } + + final int bowSlot = InventoryUtils.getFirstSlotOfItemHandlerContainingEquipment(inventory, ModEquipmentTypes.bow.get(), 0, 5); + if (bowSlot != -1) + { + return inventory.getStackInSlot(bowSlot); + } + + return ItemStack.EMPTY; + } + + /** + * Open visitor inventory for providing tools. + */ + private void openVisitorInventory() + { + Network.getNetwork().sendToServer(new OpenExpeditionSheetInventoryMessage(hand)); + } + + /** + * Render the difficulty icons. + */ + private void renderDifficulty() + { + final int maxDifficulty = Arrays.stream(ColonyExpeditionTypeDifficulty.values()) + .filter(m -> m.equals(expeditionType.difficulty()) || !m.isHidden()) + .mapToInt(ColonyExpeditionTypeDifficulty::getLevel) + .max() + .orElse(0); + final ColonyExpeditionTypeDifficulty currentDifficulty = expeditionType.difficulty(); + + for (int i = currentDifficulty.getLevel(); i <= maxDifficulty; i++) + { + findPaneOfTypeByID("diff_" + i, Image.class).setVisible(true); + } + + for (int i = 1; i <= currentDifficulty.getLevel(); i++) + { + final Image iconPane = findPaneOfTypeByID("diff_" + i, Image.class); + iconPane.setVisible(true); + iconPane.setImage(new ResourceLocation("textures/item/" + currentDifficulty.getIcon().toString() + ".png"), false); + } + + PaneBuilders.tooltipBuilder() + .append(Component.translatable(EXPEDITIONARY_DIFFICULTY, Component.translatable(EXPEDITIONARY_DIFFICULTY_PREFIX + currentDifficulty.getKey())) + .withStyle(currentDifficulty.getStyle())) + .hoverPane(findPaneOfTypeByID(ID_EXPEDITION_DIFFICULTY, View.class)) + .build(); + } + + @Override + public void onOpened() + { + super.onOpened(); + + renderHeaders(); + renderDifficulty(); + renderItemsList(); + renderGuardsList(); + } + + @Override + public void onUpdate() + { + super.onUpdate(); + requirements.sort(resourceComparator); + } + + /** + * Render the different textual headers. + */ + private void renderHeaders() + { + findPaneOfTypeByID(ID_EXPEDITION_NAME, Text.class).setText(expeditionType.name()); + + findPaneOfTypeByID(ID_EXPEDITION_ITEMS_HEADER, Text.class).setText(Component.translatable(EXPEDITIONARY_ITEMS_HEADER)); + findPaneOfTypeByID(ID_EXPEDITION_GUARDS_HEADER, Text.class).setText(Component.translatable(EXPEDITIONARY_GUARDS_HEADER, expeditionType.guards())); + + final boolean itemRequirementsMet = requirements.stream().allMatch(m -> m.getAvailabilityStatus().equals(ResourceAvailability.NOT_NEEDED)); + findPaneOfTypeByID(ID_EXPEDITION_ITEMS_SUBHEADER, Text.class) + .setText(itemRequirementsMet + ? Component.translatable(EXPEDITIONARY_ITEMS_SUBHEADER_MET) + : Component.translatable(EXPEDITIONARY_ITEMS_SUBHEADER_NOT_MET)); + + final int currentGuardCount = container.getMembers().size(); + final boolean guardRequirementMet = currentGuardCount >= expeditionType.guards(); + findPaneOfTypeByID(ID_EXPEDITION_GUARDS_SUBHEADER, Text.class) + .setText(guardRequirementMet + ? Component.translatable(EXPEDITIONARY_GUARDS_SUBHEADER_MET) + : Component.translatable(EXPEDITIONARY_GUARDS_SUBHEADER_NOT_MET, expeditionType.guards() - currentGuardCount)); + } + + /** + * Render the item requirement list. + */ + private void renderItemsList() + { + itemsList.setDataProvider(new DataProvider() + { + @Override + public int getElementCount() + { + return requirements.size(); + } + + @Override + public void updateElement(final int index, final Pane rowPane) + { + ResourceItem.updateResourcePane(requirements.get(index), mc.player, index, rowPane); + } + }); + } + + /** + * Render the guards list. + */ + private void renderGuardsList() + { + guardsList.setDataProvider(new CheckListDataProvider() + { + @Override + public int getElementCount() + { + return guards.size(); + } + + @Override + public String getCheckboxId() + { + return ID_EXPEDITION_GUARDS_ASSIGN; + } + + @Override + public boolean isChecked(final int index) + { + return container.getMembers().contains(guards.get(index).getId()); + } + + @Override + public void setChecked(final int index, final boolean checked) + { + final ICitizenDataView guard = guards.get(index); + Network.getNetwork().sendToServer(new AssignGuardMessage(guard, checked, hand)); + container.toggleMember(guard.getId(), checked); + guards.sort(guardsComparator); + renderHeaders(); + } + + @Override + public void updateElement(final int index, final Pane rowPane, final boolean checked) + { + final ICitizenDataView guard = guards.get(index); + + rowPane.findPaneOfTypeByID(ID_EXPEDITION_GUARDS_NAME, Text.class).setText(guard.getJobComponent().append(": ").append(Component.literal(guard.getName()))); + + renderGuardEquipment(getFirstWeapon(guard.getInventory()), ID_EXPEDITION_GUARDS_WEAPON, rowPane); + renderGuardEquipment(guard.getInventory().getArmorInSlot(EquipmentSlot.HEAD), ID_EXPEDITION_GUARDS_HELMET, rowPane); + renderGuardEquipment(guard.getInventory().getArmorInSlot(EquipmentSlot.CHEST), ID_EXPEDITION_GUARDS_CHESTPLATE, rowPane); + renderGuardEquipment(guard.getInventory().getArmorInSlot(EquipmentSlot.LEGS), ID_EXPEDITION_GUARDS_LEGGINGS, rowPane); + renderGuardEquipment(guard.getInventory().getArmorInSlot(EquipmentSlot.FEET), ID_EXPEDITION_GUARDS_BOOTS, rowPane); + } + }); + } + + /** + * Render one of the inventory slots for the given guard. + * + * @param itemStack the item stack to show in the slot. + * @param id the id of what pane to load. + * @param rowPane the row pane. + */ + private void renderGuardEquipment(final ItemStack itemStack, final String id, final Pane rowPane) + { + rowPane.findPaneOfTypeByID(id, ItemIcon.class).setItem(itemStack); + rowPane.findPaneOfTypeByID(id + "_back", Image.class).setVisible(itemStack.equals(ItemStack.EMPTY)); + } + + /** + * Transfers the items from player inventory to target inventory. + * + * @param button the clicked button. + */ + private void transferItems(@NotNull final Button button) + { + final Pane pane = button.getParent(); + button.disable(); + final Text idLabel = pane.findPaneOfTypeByID(RESOURCE_ID, Text.class); + final int index = Integer.parseInt(idLabel.getTextAsString()); + final RequirementHandler requirement = requirements.get(index); + if (requirement == null) + { + Log.getLogger().warn("MainWindowExpeditionary.transferItems: Error - Could not find the resource."); + return; + } + + Network.getNetwork().sendToServer(new TransferItemsMessage(colonyView, expeditionType.id(), requirement.getId(), hand)); + + final int needed = requirement.getAmount() - requirement.getAmountAvailable(); + if (mc.player.isCreative()) + { + InventoryUtils.addItemStackToItemHandler(new InvWrapper(container), requirement.getDefaultItemStack().copyWithCount(needed)); + } + else + { + InventoryUtils.transferItemStackIntoNextFreeSlotFromItemHandler(new InvWrapper(mc.player.getInventory()), + requirement.getItemPredicate(), + needed, + new InvWrapper(container)); + } + + renderHeaders(); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/client/gui/generic/ResourceItem.java b/src/main/java/com/minecolonies/core/client/gui/generic/ResourceItem.java new file mode 100644 index 00000000000..f2223fba4f1 --- /dev/null +++ b/src/main/java/com/minecolonies/core/client/gui/generic/ResourceItem.java @@ -0,0 +1,266 @@ +package com.minecolonies.core.client.gui.generic; + +import com.ldtteam.blockui.Color; +import com.ldtteam.blockui.Pane; +import com.ldtteam.blockui.controls.Button; +import com.ldtteam.blockui.controls.Text; +import com.ldtteam.blockui.views.View; +import com.minecolonies.core.client.gui.blockui.RotatingItemIcon; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import static com.minecolonies.api.util.constant.WindowConstants.*; + +/** + * Rendering a resource item in a list. + */ +public class ResourceItem +{ + /** + * Color constants for resources list. + */ + public static final int RED = Color.getByName("red", 0); + public static final int DARKGREEN = Color.getByName("darkgreen", 0); + public static final int BLACK = Color.getByName("black", 0); + public static final int ORANGE = Color.getByName("orange", 0); + + /** + * Default constructor. + */ + private ResourceItem() + { + } + + /** + * Update method to render a single resource item. + * If the + * + * @param resource the resource to update the pane with. + * @param index the item index. + * @param rowPane the parenting pane. + * @param the generic type of the resource instance. + */ + public static void updateResourcePane(final T resource, final Player player, final int index, @NotNull final Pane rowPane) + { + final Text resourceLabel = rowPane.findPaneOfTypeByID(RESOURCE_NAME, Text.class); + final Text resourceMissingLabel = rowPane.findPaneOfTypeByID(RESOURCE_MISSING, Text.class); + final Text neededLabel = rowPane.findPaneOfTypeByID(RESOURCE_AVAILABLE_NEEDED, Text.class); + final Button addButton = rowPane.findPaneOfTypeByID(RESOURCE_ADD, Button.class); + + ResourceAvailability availabilityStatus = resource.getAvailabilityStatus(); + if (!availabilityStatus.equals(ResourceAvailability.NOT_NEEDED) && player.isCreative()) + { + availabilityStatus = ResourceAvailability.HAVE_ENOUGH; + } + + switch (availabilityStatus) + { + case DONT_HAVE: + addButton.disable(); + resourceLabel.setColors(RED); + resourceMissingLabel.setColors(RED); + neededLabel.setColors(RED); + break; + case NEED_MORE: + addButton.enable(); + resourceLabel.setColors(ORANGE); + resourceMissingLabel.setColors(ORANGE); + neededLabel.setColors(ORANGE); + break; + case HAVE_ENOUGH: + addButton.enable(); + resourceLabel.setColors(DARKGREEN); + resourceMissingLabel.setColors(DARKGREEN); + neededLabel.setColors(DARKGREEN); + break; + case NOT_NEEDED: + default: + addButton.disable(); + resourceLabel.setColors(BLACK); + resourceMissingLabel.setColors(BLACK); + neededLabel.setColors(BLACK); + break; + } + + // Position the addResource Button to the right + final int buttonX = rowPane.getWidth() - addButton.getWidth() - (rowPane.getHeight() - addButton.getHeight()) / 2; + final int buttonY = rowPane.getHeight() - addButton.getHeight() - 2; + addButton.setPosition(buttonX, buttonY); + + resourceLabel.setText(resource.getName()); + final int missing = resource.getAmountPlayer() + resource.getAmountAvailable() - resource.getAmount(); + if (missing < 0) + { + resourceMissingLabel.setText(Component.literal(Integer.toString(missing))); + } + else + { + resourceMissingLabel.clearText(); + } + + neededLabel.setText(Component.literal(resource.getAmountAvailable() + " / " + resource.getAmount())); + rowPane.findPaneOfTypeByID(RESOURCE_ID, Text.class).setText(Component.literal(Integer.toString(index))); + rowPane.findPaneOfTypeByID(RESOURCE_QUANTITY_MISSING, Text.class).setText(Component.literal(Integer.toString(resource.getAmount() - resource.getAmountAvailable()))); + + RotatingItemIcon icon = rowPane.findPaneOfTypeByID(RESOURCE_ICON, RotatingItemIcon.class); + if (icon == null) + { + icon = new RotatingItemIcon(); + icon.setID(RESOURCE_ICON); + icon.setPosition(1, 1); + icon.setSize(16, 16); + icon.putInside((View) rowPane); + } + icon.setItems(resource.getIcon()); + } + + /** + * Availability status of the resource. according to the builder's chest, inventory and the player's inventory + */ + public enum ResourceAvailability + { + NOT_NEEDED(5), + IN_DELIVERY(4), + DONT_HAVE(3), + NEED_MORE(2), + HAVE_ENOUGH(1); + + /** + * The order this availability item should show up in a GUI. + */ + private final int order; + + /** + * Default constructor. + * + * @param order the order this availability item should show up in a GUI. + */ + ResourceAvailability(final int order) + { + this.order = order; + } + + /** + * Get the order this availability item should show up in a GUI. + */ + public Integer getOrder() + { + return order; + } + } + + /** + * Defines a basic resource class, intended for providing all the info on how to render a resource. + */ + public interface Resource + { + /** + * Get the name of the requested items. + * + * @return the component. + */ + Component getName(); + + /** + * Get the item stacks to show for the icon. + * + * @return the item stacks. + */ + List getIcon(); + + /** + * Get the availability status for this resource. + * + * @return the amount. + */ + default ResourceAvailability getAvailabilityStatus() + { + if (getAmount() > getAmountAvailable()) + { + if (getAmountPlayer() == 0) + { + if (getAmountInDelivery() > 0) + { + return ResourceAvailability.IN_DELIVERY; + } + return ResourceAvailability.DONT_HAVE; + } + if (getAmountPlayer() < (getAmount() - getAmountAvailable())) + { + if (getAmountInDelivery() > 0) + { + return ResourceAvailability.IN_DELIVERY; + } + return ResourceAvailability.NEED_MORE; + } + return ResourceAvailability.HAVE_ENOUGH; + } + return ResourceAvailability.NOT_NEEDED; + } + + /** + * Get how many items are needed. + * + * @return the amount. + */ + int getAmount(); + + /** + * Get how many items are currently already available. + * + * @return the amount. + */ + int getAmountAvailable(); + + /** + * Get how many items the player has in its inventory. + * + * @return the amount. + */ + int getAmountPlayer(); + + /** + * Get how many items are currently in delivery. + * + * @return the amount. + */ + int getAmountInDelivery(); + } + + /** + * Comparator class for resources. + *

+ * This is use in the gui to order the list of resources needed. + */ + public static class ResourceComparator implements Comparator, Serializable + { + @Serial + private static final long serialVersionUID = 1; + + /** + * Compare to resource together. + *

+ * We want the item available in the player inventory first and the one not needed last In alphabetical order otherwise + */ + @Override + public int compare(final Resource resource1, final Resource resource2) + { + final ResourceAvailability status1 = resource1.getAvailabilityStatus(); + final ResourceAvailability status2 = resource2.getAvailabilityStatus(); + if (Objects.equals(status1, status2)) + { + return resource1.getName().toString().compareTo(resource2.getName().toString()); + } + + return status1.getOrder().compareTo(status2.getOrder()); + } + } +} diff --git a/src/main/java/com/minecolonies/core/client/gui/modules/WarehouseOptionsModuleWindow.java b/src/main/java/com/minecolonies/core/client/gui/modules/WarehouseOptionsModuleWindow.java index c9cfebff172..36c01ec024d 100644 --- a/src/main/java/com/minecolonies/core/client/gui/modules/WarehouseOptionsModuleWindow.java +++ b/src/main/java/com/minecolonies/core/client/gui/modules/WarehouseOptionsModuleWindow.java @@ -1,9 +1,7 @@ package com.minecolonies.core.client.gui.modules; import com.ldtteam.blockui.PaneBuilders; -import com.ldtteam.blockui.controls.Button; import com.ldtteam.blockui.controls.ButtonImage; -import com.ldtteam.blockui.controls.ItemIcon; import com.ldtteam.blockui.controls.Text; import com.minecolonies.api.colony.buildings.views.IBuildingView; import com.minecolonies.api.util.InventoryUtils; @@ -11,25 +9,25 @@ import com.minecolonies.api.util.constant.Constants; import com.minecolonies.core.Network; import com.minecolonies.core.client.gui.AbstractModuleWindow; +import com.minecolonies.core.client.gui.generic.ResourceItem; +import com.minecolonies.core.client.gui.generic.ResourceItem.Resource; +import com.minecolonies.core.client.gui.generic.ResourceItem.ResourceAvailability; import com.minecolonies.core.colony.buildings.moduleviews.WarehouseOptionsModuleView; -import com.minecolonies.core.colony.buildings.utils.BuildingBuilderResource; import com.minecolonies.core.colony.buildings.workerbuildings.BuildingWareHouse; import com.minecolonies.core.network.messages.server.colony.building.MarkBuildingDirtyMessage; import com.minecolonies.core.network.messages.server.colony.building.warehouse.SortWarehouseMessage; import com.minecolonies.core.network.messages.server.colony.building.warehouse.UpgradeWarehouseMessage; -import net.minecraft.world.level.block.Blocks; import net.minecraft.client.Minecraft; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.item.ItemStack; -import net.minecraft.network.chat.Style; -import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; import net.minecraftforge.items.wrapper.InvWrapper; +import java.util.List; + import static com.minecolonies.api.util.constant.TranslationConstants.LABEL_X_OF_Z; import static com.minecolonies.api.util.constant.TranslationConstants.WAREHOUSE_SORTED; import static com.minecolonies.api.util.constant.WindowConstants.*; -import static com.minecolonies.core.client.gui.modules.WindowBuilderResModule.*; /** * BOWindow for the warehouse options. @@ -53,7 +51,8 @@ public class WarehouseOptionsModuleWindow extends AbstractModuleWindow /** * Constructor for window warehouse hut. - * @param module the module belonging to it. + * + * @param module the module belonging to it. * @param building {@link BuildingWareHouse.View}. */ public WarehouseOptionsModuleWindow(final IBuildingView building, final WarehouseOptionsModuleView module) @@ -71,10 +70,10 @@ public void onOpened() { final ButtonImage sortButton = findPaneOfTypeByID(SORT_WAREHOUSE_BUTTON, ButtonImage.class); PaneBuilders.tooltipBuilder() - .append(Component.translatable("com.minecolonies.coremod.gui.warehouse.sort.disabled.1", BUILDING_LEVEL_FOR_SORTING)) - .appendNL(Component.translatable("com.minecolonies.coremod.gui.warehouse.sort.disabled.2", BUILDING_LEVEL_FOR_SORTING)) - .hoverPane(sortButton) - .build(); + .append(Component.translatable("com.minecolonies.coremod.gui.warehouse.sort.disabled.1", BUILDING_LEVEL_FOR_SORTING)) + .appendNL(Component.translatable("com.minecolonies.coremod.gui.warehouse.sort.disabled.2", BUILDING_LEVEL_FOR_SORTING)) + .hoverPane(sortButton) + .build(); sortButton.disable(); } @@ -86,101 +85,25 @@ public void onOpened() } /** - * Update one row pad with its resource informations. + * Update one row pad with its resource information. */ private void updateResourcePane() { - final BuildingBuilderResource resource = new BuildingBuilderResource(new ItemStack(Blocks.EMERALD_BLOCK, 1), 1); - - final int amountToSet; - final Inventory inventory = this.mc.player.getInventory(); - final boolean isCreative = this.mc.player.isCreative(); - if (isCreative) - { - amountToSet = resource.getAmount(); - } - else - { - amountToSet = InventoryUtils.getItemCountInItemHandler(new InvWrapper(inventory), resource.getItem()); - } - resource.setPlayerAmount(amountToSet); - - final Text resourceLabel = findPaneOfTypeByID(RESOURCE_NAME, Text.class); - final Text resourceMissingLabel = findPaneOfTypeByID(RESOURCE_MISSING, Text.class); - final Text neededLabel = findPaneOfTypeByID(RESOURCE_AVAILABLE_NEEDED, Text.class); - final Button addButton = findPaneOfTypeByID(RESOURCE_ADD, Button.class); - - BuildingBuilderResource.RessourceAvailability availability = resource.getAvailabilityStatus(); - - if (module.getStorageUpgradeLevel() >= BuildingWareHouse.MAX_STORAGE_UPGRADE || buildingView.getBuildingLevel() < buildingView.getBuildingMaxLevel() || lockUpgrade) - { - availability = BuildingBuilderResource.RessourceAvailability.NOT_NEEDED; - } + final EmeraldBlockResource resource = new EmeraldBlockResource(); findPaneOfTypeByID(UPGRADE_PROGRESS_LABEL, Text.class).setText(Component.translatable(LABEL_X_OF_Z, module.getStorageUpgradeLevel(), BuildingWareHouse.MAX_STORAGE_UPGRADE)); - switch (availability) - { - case DONT_HAVE: - addButton.disable(); - resourceLabel.setColors(RED); - resourceMissingLabel.setColors(RED); - neededLabel.setColors(RED); - break; - case NEED_MORE: - addButton.enable(); - resourceLabel.setColors(RED); - resourceMissingLabel.setColors(RED); - neededLabel.setColors(RED); - break; - case HAVE_ENOUGH: - addButton.enable(); - resourceLabel.setColors(DARKGREEN); - resourceMissingLabel.setColors(DARKGREEN); - neededLabel.setColors(DARKGREEN); - break; - case NOT_NEEDED: - default: - addButton.disable(); - resourceLabel.setColors(BLACK); - resourceMissingLabel.setColors(BLACK); - neededLabel.setColors(BLACK); - if (buildingView.getBuildingLevel() < buildingView.getBuildingMaxLevel()) - { - resourceLabel.hide(); - resourceMissingLabel.hide(); - neededLabel.hide(); - addButton.setText(Component.literal("X").setStyle(Style.EMPTY.withColor(ChatFormatting.DARK_RED))); - PaneBuilders.tooltipBuilder() - .append(Component.translatable("com.minecolonies.coremod.gui.warehouse.upgrade.disabled.1", buildingView.getBuildingMaxLevel())) - .appendNL(Component.translatable("com.minecolonies.coremod.gui.warehouse.upgrade.disabled.2", buildingView.getBuildingMaxLevel())) - .hoverPane(addButton) - .build(); - } - break; - } - - resourceLabel.setText(Component.literal(resource.getName())); - final int missing = resource.getMissingFromPlayer(); - if (missing < 0) - { - resourceMissingLabel.setText(Component.literal(Integer.toString(missing))); - } - else - { - resourceMissingLabel.clearText(); - } - - neededLabel.setText(Component.literal(resource.getAvailable() + " / " + resource.getAmount())); - findPaneOfTypeByID(RESOURCE_QUANTITY_MISSING, Text.class).setText(Component.literal(Integer.toString(resource.getAmount() - resource.getAvailable()))); + ResourceItem.updateResourcePane(resource, mc.player, 0, this); - if(buildingView.getBuildingLevel() >= buildingView.getBuildingMaxLevel()) + if (buildingView.getBuildingLevel() < buildingView.getBuildingMaxLevel()) { - final ItemStack resourceStackOfOne = new ItemStack(resource.getItem(), 1); - resourceStackOfOne.setTag(resource.getItemStack().getTag()); - findPaneOfTypeByID(RESOURCE_ICON, ItemIcon.class).setItem(resourceStackOfOne); + PaneBuilders.tooltipBuilder() + .append(Component.translatable("com.minecolonies.coremod.gui.warehouse.upgrade.disabled.1", buildingView.getBuildingMaxLevel())) + .appendNL(Component.translatable("com.minecolonies.coremod.gui.warehouse.upgrade.disabled.2", buildingView.getBuildingMaxLevel())) + .hoverPane(findPaneByID(RESOURCE_ADD)) + .build(); } } @@ -206,4 +129,63 @@ private void sortWarehouse() MessageUtils.format(WAREHOUSE_SORTED).sendTo(Minecraft.getInstance().player); } } + + /** + * Simple container class for the emerald block resource. + */ + private class EmeraldBlockResource implements Resource + { + @Override + public Component getName() + { + return Blocks.EMERALD_BLOCK.getName(); + } + + @Override + public List getIcon() + { + return null; + } + + @Override + public ResourceAvailability getAvailabilityStatus() + { + if (module.getStorageUpgradeLevel() >= BuildingWareHouse.MAX_STORAGE_UPGRADE || buildingView.getBuildingLevel() < buildingView.getBuildingMaxLevel() || lockUpgrade) + { + return ResourceAvailability.NOT_NEEDED; + } + return Resource.super.getAvailabilityStatus(); + } + + @Override + public int getAmount() + { + return 1; + } + + @Override + public int getAmountAvailable() + { + return 0; + } + + @Override + public int getAmountPlayer() + { + if (mc.player.isCreative()) + { + return 1; + } + else + { + return InventoryUtils.getItemCountInItemHandler(new InvWrapper(mc.player.getInventory()), Blocks.EMERALD_BLOCK.asItem()); + } + } + + @Override + public int getAmountInDelivery() + { + return 0; + } + } } diff --git a/src/main/java/com/minecolonies/core/client/gui/modules/WindowBuilderResModule.java b/src/main/java/com/minecolonies/core/client/gui/modules/WindowBuilderResModule.java index 60a85365697..3e310e74663 100644 --- a/src/main/java/com/minecolonies/core/client/gui/modules/WindowBuilderResModule.java +++ b/src/main/java/com/minecolonies/core/client/gui/modules/WindowBuilderResModule.java @@ -1,10 +1,8 @@ package com.minecolonies.core.client.gui.modules; -import com.ldtteam.blockui.Color; import com.ldtteam.blockui.Pane; import com.ldtteam.blockui.PaneBuilders; import com.ldtteam.blockui.controls.Button; -import com.ldtteam.blockui.controls.ItemIcon; import com.ldtteam.blockui.controls.Text; import com.ldtteam.blockui.views.ScrollingList; import com.minecolonies.api.colony.buildings.views.IBuildingView; @@ -14,6 +12,8 @@ import com.minecolonies.api.util.Log; import com.minecolonies.core.Network; import com.minecolonies.core.client.gui.AbstractModuleWindow; +import com.minecolonies.core.client.gui.generic.ResourceItem; +import com.minecolonies.core.client.gui.generic.ResourceItem.ResourceComparator; import com.minecolonies.core.colony.buildings.moduleviews.BuildingResourcesModuleView; import com.minecolonies.core.colony.buildings.utils.BuildingBuilderResource; import com.minecolonies.core.colony.buildings.workerbuildings.BuildingBuilder; @@ -36,14 +36,6 @@ */ public class WindowBuilderResModule extends AbstractModuleWindow { - /** - * Color constants for builder list. - */ - public static final int RED = Color.getByName("red", 0); - public static final int DARKGREEN = Color.getByName("darkgreen", 0); - public static final int BLACK = Color.getByName("black", 0); - public static final int ORANGE = Color.getByName("orange", 0); - /** * List of resources needed. */ @@ -103,7 +95,7 @@ private void pullResourcesFromHut() stack -> !ItemStackUtils.isEmpty(stack) && ItemStackUtils.compareItemStacksIgnoreStackSize(stack, resource.getItemStack())); } resource.setPlayerAmount(amountToSet); - supplied += Math.min(resource.getAvailable(), resource.getAmount()); + supplied += Math.min(resource.getAmountAvailable(), resource.getAmount()); total += resource.getAmount(); } @@ -113,7 +105,7 @@ private void pullResourcesFromHut() findPaneOfTypeByID(LABEL_PROGRESS, Text.class).setText(Component.translatable("com.minecolonies.coremod.gui.progress.res", (int) ((supplied / total) * 100) + "%", moduleView.getProgress() + "%")); } - resources.sort(new BuildingBuilderResource.ResourceComparator()); + resources.sort(new ResourceComparator()); } @Override @@ -134,7 +126,7 @@ public int getElementCount() @Override public void updateElement(final int index, @NotNull final Pane rowPane) { - updateResourcePane(index, rowPane); + ResourceItem.updateResourcePane(resources.get(index), mc.player, index, rowPane); } }); @@ -155,74 +147,6 @@ public void updateElement(final int index, @NotNull final Pane rowPane) findPaneOfTypeByID(STEP_PROGRESS, Text.class).setText(Component.translatable("com.minecolonies.coremod.gui.progress.step", moduleView.getCurrentStage(), moduleView.getTotalStages())); } - /** - * Update one row pad with its resource informations. - * - * @param index index in the list of resources. - * @param rowPane The Pane to use to display the information. - */ - private void updateResourcePane(final int index, @NotNull final Pane rowPane) - { - final BuildingBuilderResource resource = resources.get(index); - final Text resourceLabel = rowPane.findPaneOfTypeByID(RESOURCE_NAME, Text.class); - final Text resourceMissingLabel = rowPane.findPaneOfTypeByID(RESOURCE_MISSING, Text.class); - final Text neededLabel = rowPane.findPaneOfTypeByID(RESOURCE_AVAILABLE_NEEDED, Text.class); - final Button addButton = rowPane.findPaneOfTypeByID(RESOURCE_ADD, Button.class); - - switch (resource.getAvailabilityStatus()) - { - case DONT_HAVE: - addButton.disable(); - resourceLabel.setColors(RED); - resourceMissingLabel.setColors(RED); - neededLabel.setColors(RED); - break; - case NEED_MORE: - addButton.enable(); - resourceLabel.setColors(ORANGE); - resourceMissingLabel.setColors(ORANGE); - neededLabel.setColors(ORANGE); - break; - case HAVE_ENOUGH: - addButton.enable(); - resourceLabel.setColors(DARKGREEN); - resourceMissingLabel.setColors(DARKGREEN); - neededLabel.setColors(DARKGREEN); - break; - case NOT_NEEDED: - default: - addButton.disable(); - resourceLabel.setColors(BLACK); - resourceMissingLabel.setColors(BLACK); - neededLabel.setColors(BLACK); - break; - } - - //position the addResource Button to the right - final int buttonX = rowPane.getWidth() - addButton.getWidth() - (rowPane.getHeight() - addButton.getHeight()) / 2; - final int buttonY = rowPane.getHeight() - addButton.getHeight() - 2; - addButton.setPosition(buttonX, buttonY); - - resourceLabel.setText(Component.literal(resource.getName())); - final int missing = resource.getMissingFromPlayer(); - if (missing < 0) - { - resourceMissingLabel.setText(Component.literal(Integer.toString(missing))); - } - else - { - resourceMissingLabel.clearText(); - } - - neededLabel.setText(Component.literal(resource.getAvailable() + " / " + resource.getAmount())); - rowPane.findPaneOfTypeByID(RESOURCE_ID, Text.class).setText(Component.literal(Integer.toString(index))); - rowPane.findPaneOfTypeByID(RESOURCE_QUANTITY_MISSING, Text.class).setText(Component.literal(Integer.toString(resource.getAmount() - resource.getAvailable()))); - - final ItemStack stack = new ItemStack(resource.getItem(), 1); - stack.setTag(resource.getItemStack().getTag()); - rowPane.findPaneOfTypeByID(RESOURCE_ICON, ItemIcon.class).setItem(stack); - } - @Override public void onUpdate() { @@ -262,10 +186,10 @@ private void transferItems(@NotNull final Button button) itemStack.setCount(1); final Text quantityLabel = pane.findPaneOfTypeByID(RESOURCE_QUANTITY_MISSING, Text.class); final int quantity = Integer.parseInt(quantityLabel.getTextAsString()); - final int needed = res.getAmount() - res.getAvailable(); - res.setAvailable(Math.min(res.getAmount(), res.getAvailable() + res.getPlayerAmount())); - res.setPlayerAmount(Math.max(0, res.getPlayerAmount() - needed)); - resources.sort(new BuildingBuilderResource.ResourceComparator()); + final int needed = res.getAmount() - res.getAmountAvailable(); + res.setAvailable(Math.min(res.getAmount(), res.getAmountAvailable() + res.getAmountPlayer())); + res.setPlayerAmount(Math.max(0, res.getAmountPlayer() - needed)); + resources.sort(new ResourceComparator()); Network.getNetwork().sendToServer(new TransferItemsRequestMessage(this.buildingView, itemStack, quantity, true)); } } diff --git a/src/main/java/com/minecolonies/core/client/gui/townhall/WindowMainPage.java b/src/main/java/com/minecolonies/core/client/gui/townhall/WindowMainPage.java index 64b8455c478..4e695d0771b 100644 --- a/src/main/java/com/minecolonies/core/client/gui/townhall/WindowMainPage.java +++ b/src/main/java/com/minecolonies/core/client/gui/townhall/WindowMainPage.java @@ -99,13 +99,14 @@ public WindowMainPage(final BuildingTownHall.View building) registerButton(BUTTON_CHANGE_SPEC, this::doNothing); registerButton(BUTTON_RENAME, this::renameClicked); - registerButton(BUTTON_MERCENARY, this::mercenaryClicked); registerButton(BUTTON_TOWNHALLMAP, this::mapButtonClicked); - registerButton(BUTTON_PATREON, this::patreonClicked); + registerButton(BUTTON_MERCENARY, this::mercenaryClicked); + registerButton(BUTTON_EXPEDITIONS, this::expeditionsClicked); + registerButton(BUTTON_PATREON, this::patreonClicked); registerButton(BUTTON_COLONY_SWITCH_STYLE, this::switchPack); - findPaneOfTypeByID(BUTTON_COLONY_SWITCH_STYLE, ButtonImage.class).setText(Component.literal(building.getColony().getStructurePack())); + registerButton(BUTTON_BANNER_PICKER, this::openBannerPicker); registerButton(BUTTON_RESET_TEXTURE, this::resetTextureStyle); @@ -394,6 +395,14 @@ private void mercenaryClicked() new WindowTownHallMercenary(building.getColony()).open(); } + /** + * Action performed when expeditions button is clicked. + */ + private void expeditionsClicked() + { + new WindowTownHallExpeditions(building.getColony()).open(); + } + /** * Opens the map on button clicked */ diff --git a/src/main/java/com/minecolonies/core/client/gui/townhall/WindowTownHallExpeditions.java b/src/main/java/com/minecolonies/core/client/gui/townhall/WindowTownHallExpeditions.java new file mode 100644 index 00000000000..1de452d7a97 --- /dev/null +++ b/src/main/java/com/minecolonies/core/client/gui/townhall/WindowTownHallExpeditions.java @@ -0,0 +1,433 @@ +package com.minecolonies.core.client.gui.townhall; + +import com.ldtteam.blockui.Pane; +import com.ldtteam.blockui.PaneBuilders; +import com.ldtteam.blockui.controls.*; +import com.ldtteam.blockui.views.Box; +import com.ldtteam.blockui.views.ScrollingList; +import com.ldtteam.blockui.views.ScrollingList.DataProvider; +import com.ldtteam.blockui.views.View; +import com.minecolonies.api.colony.IColonyView; +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; +import com.minecolonies.api.colony.expeditions.MobKill; +import com.minecolonies.api.colony.managers.interfaces.expeditions.ColonyExpedition; +import com.minecolonies.api.colony.managers.interfaces.expeditions.FinishedExpedition; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.client.gui.AbstractWindowSkeleton; +import com.minecolonies.core.colony.ColonyView; +import com.minecolonies.core.colony.expeditions.ExpeditionStage; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeDifficulty; +import com.minecolonies.core.colony.expeditions.encounters.ExpeditionEncounter; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.datalistener.ExpeditionEncounterListener; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static com.minecolonies.api.util.constant.ExpeditionConstants.*; + +/** + * Gui for viewing past expeditions. + */ +public class WindowTownHallExpeditions extends AbstractWindowSkeleton implements ButtonHandler +{ + /** + * The xml file for this gui + */ + private static final String RESOURCE_SUFFIX = ":gui/townhall/windowtownhallexpeditions.xml"; + + /** + * ID constants. + */ + private static final String LIST_ACTIVE_EXPEDITIONS = "active_expeditions"; + private static final String LIST_FINISHED_EXPEDITIONS = "finished_expeditions"; + private static final String LABEL_EXPEDITION_NAME = "expedition_name"; + private static final String IMAGE_EXPEDITION_DIFFICULTY = "expedition_difficulty"; + private static final String LABEL_EXPEDITION_STATUS = "expedition_status"; + private static final String BUTTON_EXPEDITION_OPEN = "expedition_open"; + private static final String LABEL_EMPTY = "empty_text"; + private static final String VIEW_EXPEDITION_DETAILS = "expedition_details"; + private static final String LIST_EXPEDITION_ITEMS = "expedition_items"; + private static final String STATUS_EXPEDITION_RESULTS = "expedition_results"; + private static final String LIST_EXPEDITION_RESULTS = "expedition_results_list"; + private static final String LIST_EXPEDITION_RESULTS_CHILD_HEADER = "child_header"; + private static final String LIST_EXPEDITION_RESULTS_CHILD_HEADER_TEXT = "header"; + private static final String LIST_EXPEDITION_RESULTS_CHILD_REWARDS = "child_rewards"; + private static final String LIST_EXPEDITION_RESULTS_CHILD_KILLS = "child_kills"; + private static final String PARTIAL_ITEM_PREFIX = "item_"; + + /** + * The amount of item icons showing on a single list row. + */ + private static final int ITEMS_PER_ROW = 9; + + /** + * The client side colony data + */ + private final IColonyView colony; + + /** + * The list for the active expeditions. + */ + private final ScrollingList activeExpeditionsList; + + /** + * The list for the finished expeditions. + */ + private final ScrollingList finishedExpeditionsList; + + @Nullable + private ColonyExpedition openedExpedition; + + /** + * Constructor for a town hall rename entry window. + * + * @param colony {@link ColonyView} + */ + public WindowTownHallExpeditions(final IColonyView colony) + { + super(Constants.MOD_ID + RESOURCE_SUFFIX); + this.colony = colony; + + this.activeExpeditionsList = findPaneOfTypeByID(LIST_ACTIVE_EXPEDITIONS, ScrollingList.class); + setupExpeditionList(activeExpeditionsList, colony.getExpeditionManager()::getActiveExpeditions); + this.finishedExpeditionsList = findPaneOfTypeByID(LIST_FINISHED_EXPEDITIONS, ScrollingList.class); + setupExpeditionList(finishedExpeditionsList, colony.getExpeditionManager()::getFinishedExpeditions); + + registerButton(BUTTON_EXPEDITION_OPEN, this::openExpedition); + updateOpenedExpedition(); + } + + /** + * Set up an expedition list. + * + * @param list the scrolling list element. + * @param expeditionGetter the getter for the list of expeditions. + */ + private void setupExpeditionList(final ScrollingList list, final Supplier> expeditionGetter) + { + list.setDataProvider(new DataProvider() + { + @Override + public int getElementCount() + { + return expeditionGetter.get().size(); + } + + @Override + public void updateElement(final int index, final Pane pane) + { + final ColonyExpedition expedition = expeditionGetter.get().get(index); + + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getExpeditionType(expedition.getExpeditionTypeId()); + if (expeditionType == null) + { + return; + } + + pane.findPaneOfTypeByID(LABEL_EXPEDITION_NAME, Text.class).setText(expeditionType.name()); + renderDifficultyImage(pane, expeditionType); + + final ExpeditionStatus expeditionStatus = colony.getExpeditionManager().getExpeditionStatus(expedition.getId()); + if (expeditionStatus.equals(ExpeditionStatus.FINISHED)) + { + final FinishedExpedition finishedExpedition = colony.getExpeditionManager().getFinishedExpedition(expedition.getId()); + if (finishedExpedition != null) + { + final MutableComponent statusComponent = Component.translatable(EXPEDITION_TOWNHALL_LIST_STATUS + finishedExpedition.status().name()) + .withStyle(finishedExpedition.status().getStatusType().getDisplayColor()); + pane.findPaneOfTypeByID(LABEL_EXPEDITION_STATUS, Text.class).setText(statusComponent); + } + } + else if (expeditionStatus.equals(ExpeditionStatus.ONGOING)) + { + final MutableComponent statusComponent = Component.translatable(EXPEDITION_TOWNHALL_LIST_STATUS + ExpeditionStatus.ONGOING.name()); + pane.findPaneOfTypeByID(LABEL_EXPEDITION_STATUS, Text.class).setText(statusComponent); + } + + final boolean isOpenActive = openedExpedition == null || openedExpedition.getId() != expedition.getId(); + pane.findPaneOfTypeByID(BUTTON_EXPEDITION_OPEN, ButtonImage.class).setEnabled(isOpenActive); + } + }); + } + + /** + * Called when a button is clicked to open expedition details. + * + * @param button the button that was clicked. + */ + private void openExpedition(final Button button) + { + final Box container = (Box) button.getParent(); + final int activeListIndex = activeExpeditionsList.getListElementIndexByPane(container); + if (activeListIndex != -1) + { + this.openedExpedition = colony.getExpeditionManager().getActiveExpeditions().get(activeListIndex); + } + + final int finishedListIndex = finishedExpeditionsList.getListElementIndexByPane(container); + if (finishedListIndex != -1) + { + this.openedExpedition = colony.getExpeditionManager().getFinishedExpeditions().get(finishedListIndex); + } + + updateOpenedExpedition(); + } + + /** + * Updates the pane on the right to view the opened expedition. + */ + private void updateOpenedExpedition() + { + findPaneOfTypeByID(LABEL_EMPTY, Text.class).setEnabled(openedExpedition == null); + findPaneOfTypeByID(LABEL_EMPTY, Text.class).setVisible(openedExpedition == null); + + final View detailsContainer = findPaneOfTypeByID(VIEW_EXPEDITION_DETAILS, View.class); + detailsContainer.setEnabled(openedExpedition != null); + detailsContainer.setVisible(openedExpedition != null); + + if (openedExpedition != null) + { + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getExpeditionType(openedExpedition.getExpeditionTypeId()); + if (expeditionType == null) + { + return; + } + + detailsContainer.findPaneOfTypeByID(LABEL_EXPEDITION_NAME, Text.class).setText(expeditionType.name()); + renderDifficultyImage(detailsContainer, expeditionType); + + final ExpeditionStatus expeditionStatus = colony.getExpeditionManager().getExpeditionStatus(openedExpedition.getId()); + if (expeditionStatus.equals(ExpeditionStatus.FINISHED)) + { + final FinishedExpedition finishedExpedition = colony.getExpeditionManager().getFinishedExpedition(openedExpedition.getId()); + if (finishedExpedition != null) + { + final MutableComponent statusComponent = Component.translatable(EXPEDITION_TOWNHALL_LIST_STATUS + finishedExpedition.status().name()) + .withStyle(finishedExpedition.status().getStatusType().getDisplayColor()); + detailsContainer.findPaneOfTypeByID(LABEL_EXPEDITION_STATUS, Text.class).setText(statusComponent); + + findPaneOfTypeByID(STATUS_EXPEDITION_RESULTS, View.class).on(); + + final List rows = getResultRowData(openedExpedition); + detailsContainer.findPaneOfTypeByID(LIST_EXPEDITION_RESULTS, ScrollingList.class).setDataProvider(new DataProvider() + { + @Override + public int getElementCount() + { + return rows.size(); + } + + @Override + public boolean shouldUpdate() + { + return false; + } + + @Override + public void updateElement(final int i, final Pane pane) + { + final OpenedExpeditionResultData rowData = rows.get(i); + final ExpeditionStage currentStage = openedExpedition.getResults().get(rowData.stageIndex); + + final View childHeader = pane.findPaneOfTypeByID(LIST_EXPEDITION_RESULTS_CHILD_HEADER, View.class); + final View childRewards = pane.findPaneOfTypeByID(LIST_EXPEDITION_RESULTS_CHILD_REWARDS, View.class); + final View childKills = pane.findPaneOfTypeByID(LIST_EXPEDITION_RESULTS_CHILD_KILLS, View.class); + + childHeader.off(); + childRewards.off(); + childKills.off(); + + switch (rowData.type) + { + case HEADER -> + { + childHeader.on(); + childHeader.findPaneOfTypeByID(LIST_EXPEDITION_RESULTS_CHILD_HEADER_TEXT, Text.class).setText(currentStage.getHeader()); + } + case REWARDS -> + { + childRewards.on(); + + for (int colIndex = 0; colIndex < ITEMS_PER_ROW; colIndex++) + { + final int itemIndex = colIndex + rowData.listOffsetIndex; + if (currentStage.getRewards().size() > itemIndex) + { + final ItemStack item = currentStage.getRewards().get(itemIndex); + childRewards.findPaneOfTypeByID(PARTIAL_ITEM_PREFIX + colIndex, ItemIcon.class).setItem(item); + } + else + { + childRewards.findPaneOfTypeByID(PARTIAL_ITEM_PREFIX + colIndex, ItemIcon.class).setItem(Items.AIR.getDefaultInstance()); + } + } + } + case KILLS -> + { + childKills.on(); + + for (int colIndex = 0; colIndex < ITEMS_PER_ROW; colIndex++) + { + final int itemIndex = colIndex + rowData.listOffsetIndex; + if (currentStage.getKills().size() > itemIndex) + { + final MobKill item = currentStage.getKills().get(itemIndex); + final EntityIcon entityIcon = childKills.findPaneOfTypeByID(PARTIAL_ITEM_PREFIX + colIndex, EntityIcon.class); + + final ExpeditionEncounter encounter = ExpeditionEncounterListener.getEncounter(item.encounterId()); + if (encounter != null) + { + entityIcon.setEntity(encounter.entityType()); + entityIcon.setCount(item.count()); + } + else + { + entityIcon.resetEntity(); + entityIcon.setCount(0); + } + } + else + { + final EntityIcon entityIcon = childKills.findPaneOfTypeByID(PARTIAL_ITEM_PREFIX + colIndex, EntityIcon.class); + entityIcon.resetEntity(); + entityIcon.setCount(0); + } + } + } + } + } + }); + } + else + { + findPaneOfTypeByID(STATUS_EXPEDITION_RESULTS, View.class).off(); + detailsContainer.findPaneOfTypeByID(LIST_EXPEDITION_RESULTS, ScrollingList.class).setDataProvider(null); + } + } + else + { + final MutableComponent statusComponent = Component.translatable(EXPEDITION_TOWNHALL_LIST_STATUS + ExpeditionStatus.ONGOING.name()); + detailsContainer.findPaneOfTypeByID(LABEL_EXPEDITION_STATUS, Text.class).setText(statusComponent); + + findPaneOfTypeByID(STATUS_EXPEDITION_RESULTS, View.class).off(); + detailsContainer.findPaneOfTypeByID(LIST_EXPEDITION_RESULTS, ScrollingList.class).setDataProvider(null); + } + + final ScrollingList itemsList = detailsContainer.findPaneOfTypeByID(LIST_EXPEDITION_ITEMS, ScrollingList.class); + itemsList.setDataProvider(new DataProvider() + { + @Override + public int getElementCount() + { + return (int) Math.ceil(openedExpedition.getEquipment().size() / (double) ITEMS_PER_ROW); + } + + @Override + public void updateElement(final int i, final Pane pane) + { + for (int colIndex = 0; colIndex < ITEMS_PER_ROW; colIndex++) + { + final int itemIndex = colIndex * (i + 1); + if (openedExpedition.getEquipment().size() <= itemIndex) + { + break; + } + + final ItemStack item = openedExpedition.getEquipment().get(itemIndex); + pane.findPaneOfTypeByID(PARTIAL_ITEM_PREFIX + colIndex, ItemIcon.class).setItem(item); + } + } + }); + } + } + + /** + * Render the difficulty image. + * + * @param container the container to find the image in. + * @param expeditionType the expedition type. + */ + private void renderDifficultyImage(final Pane container, final ColonyExpeditionType expeditionType) + { + final ColonyExpeditionTypeDifficulty difficulty = expeditionType.difficulty(); + container.findPaneOfTypeByID(WindowTownHallExpeditions.IMAGE_EXPEDITION_DIFFICULTY, Image.class) + .setImage(new ResourceLocation("textures/item/" + difficulty.getIcon().toString() + ".png"), true); + + PaneBuilders.tooltipBuilder() + .append(Component.translatable(EXPEDITIONARY_DIFFICULTY, Component.translatable(EXPEDITIONARY_DIFFICULTY_PREFIX + difficulty.getKey())) + .withStyle(difficulty.getStyle())) + .hoverPane(container.findPaneOfTypeByID(WindowTownHallExpeditions.IMAGE_EXPEDITION_DIFFICULTY, Image.class)) + .build(); + } + + /** + * Extract the results data from an expedition instance. + * + * @param colonyExpedition the expedition instance. + * @return the list of data. + */ + private List getResultRowData(@Nullable final ColonyExpedition colonyExpedition) + { + if (colonyExpedition == null) + { + return new ArrayList<>(); + } + + final List results = new ArrayList<>(); + + final List instanceResults = colonyExpedition.getResults(); + for (int stageIndex = 0; stageIndex < instanceResults.size(); stageIndex++) + { + final ExpeditionStage stage = instanceResults.get(stageIndex); + // 1 row for the stage header + results.add(new OpenedExpeditionResultData(OpenedExpeditionResultType.HEADER, stageIndex, 0)); + + // X rows if there's any rewards, amount of rows divided by ITEMS_PER_ROW + if (!stage.getRewards().isEmpty()) + { + final int rewardRows = (int) Math.ceil(stage.getRewards().size() / (double) ITEMS_PER_ROW); + for (int offsetIndex = 0; offsetIndex < rewardRows; offsetIndex++) + { + results.add(new OpenedExpeditionResultData(OpenedExpeditionResultType.REWARDS, stageIndex, offsetIndex)); + } + } + + // X rows if there's any kills, amount of rows divided by ITEMS_PER_ROW + if (!stage.getKills().isEmpty()) + { + final int killsRows = (int) Math.ceil(stage.getKills().size() / (double) ITEMS_PER_ROW); + for (int offsetIndex = 0; offsetIndex < killsRows; offsetIndex++) + { + results.add(new OpenedExpeditionResultData(OpenedExpeditionResultType.KILLS, stageIndex, offsetIndex)); + } + } + } + return results; + } + + /** + * The type of result row. + */ + private enum OpenedExpeditionResultType + { + HEADER, + REWARDS, + KILLS + } + + /** + * Container class for the opened expedition. + */ + private record OpenedExpeditionResultData(OpenedExpeditionResultType type, int stageIndex, int listOffsetIndex) + { + } +} diff --git a/src/main/java/com/minecolonies/core/colony/CitizenData.java b/src/main/java/com/minecolonies/core/colony/CitizenData.java index 6b41abd58f5..c2225a7fd9a 100755 --- a/src/main/java/com/minecolonies/core/colony/CitizenData.java +++ b/src/main/java/com/minecolonies/core/colony/CitizenData.java @@ -916,17 +916,44 @@ public void updateEntityIfNecessary() } } + // Check if we are traveling, we don't spawn an entity if we are traveling. + if (getColony().getTravelingManager().isTravelling(this)) + { + return; + } + + // Okay we are either just done traveling or the entity disappeared, lets check if we just finished traveling. + final Optional travelingTargetCandidate = getColony().getTravelingManager().getTravellingTargetFor(this); + if (travelingTargetCandidate.isPresent()) + { + // We just finished traveling, lets spawn the entity by setting the nextRespawnPosition. + getColony().getTravelingManager().finishTravellingFor(this); + nextRespawnPos = travelingTargetCandidate.get(); + lastPosition = nextRespawnPos; + } + if (nextRespawnPos != null) { - colony.getCitizenManager().spawnOrCreateCivilian(this, colony.getWorld(), nextRespawnPos, true); + respawnAfterUpdate(nextRespawnPos); nextRespawnPos = null; } else { - colony.getCitizenManager().spawnOrCreateCivilian(this, colony.getWorld(), lastPosition, true); + respawnAfterUpdate(lastPosition); } } + /** + * Method called from {@link CitizenData#updateEntityIfNecessary()} to trigger a respawn of the entity + * at the given position. + * + * @param position the position to spawn at. + */ + protected void respawnAfterUpdate(final BlockPos position) + { + colony.getCitizenManager().spawnOrCreateCitizen(this, colony.getWorld(), position, true); + } + @Override public IJob getJob() { @@ -1601,7 +1628,7 @@ public void update(final int tickRate) for (final IInteractionResponseHandler handler : toRemove) { citizenChatOptions.remove(handler.getId()); - for (final Component comp : handler.getPossibleResponses()) + for (final Component comp : handler.getPossibleResponses(this)) { if (citizenChatOptions.containsKey(handler.getResponseResult(comp))) { @@ -1621,7 +1648,7 @@ public void triggerInteraction(@NotNull final IInteractionResponseHandler handle this.citizenChatOptions.put(handler.getId(), handler); for (final IInteractionResponseHandler childHandler : handler.genChildInteractions()) { - this.citizenChatOptions.put(childHandler.getId(), (ServerCitizenInteraction) childHandler); + this.citizenChatOptions.put(childHandler.getId(), childHandler); } markDirty(20 * 5); } diff --git a/src/main/java/com/minecolonies/core/colony/CitizenDataView.java b/src/main/java/com/minecolonies/core/colony/CitizenDataView.java index c064ac18d32..82f8563c9df 100644 --- a/src/main/java/com/minecolonies/core/colony/CitizenDataView.java +++ b/src/main/java/com/minecolonies/core/colony/CitizenDataView.java @@ -2,7 +2,6 @@ import com.minecolonies.api.MinecoloniesAPIProxy; import com.minecolonies.api.colony.ICitizenDataView; -import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.IColonyManager; import com.minecolonies.api.colony.IColonyView; import com.minecolonies.api.colony.interactionhandling.ChatPriority; @@ -212,7 +211,7 @@ public boolean isPaused() } @Override - public IColony getColony() + public IColonyView getColony() { return colonyView; } diff --git a/src/main/java/com/minecolonies/core/colony/Colony.java b/src/main/java/com/minecolonies/core/colony/Colony.java index 1400e409339..ced0850d817 100644 --- a/src/main/java/com/minecolonies/core/colony/Colony.java +++ b/src/main/java/com/minecolonies/core/colony/Colony.java @@ -9,6 +9,7 @@ import com.minecolonies.api.colony.buildings.modules.ISettingsModule; import com.minecolonies.api.colony.buildings.registry.BuildingEntry; import com.minecolonies.api.colony.managers.interfaces.*; +import com.minecolonies.api.colony.managers.interfaces.expeditions.IColonyExpeditionManager; import com.minecolonies.api.colony.permissions.Action; import com.minecolonies.api.colony.permissions.Rank; import com.minecolonies.api.colony.requestsystem.manager.IRequestManager; @@ -187,6 +188,16 @@ public class Colony implements IColony */ private IQuestManager questManager; + /** + * Expedition manager for this colony + */ + private final IColonyExpeditionManager expeditionManager = new ColonyExpeditionManager(this); + + /** + * The traveling manager used for traveling large distances + */ + private final TravelingManager travelingManager = new TravelingManager(this); + /** * The Positions which players can freely interact. */ @@ -374,6 +385,7 @@ protected Colony(final int id, @Nullable final Level world) colonyStateMachine.addTransition(new TickingTransition<>(ACTIVE, () -> true, this::updateState, UPDATE_STATE_INTERVAL)); colonyStateMachine.addTransition(new TickingTransition<>(ACTIVE, () -> { citizenManager.tickCitizenData(TICKS_SECOND * 3); + visitorManager.tickVisitorData(TICKS_SECOND * 3); return false; }, () -> ACTIVE, TICKS_SECOND * 3)); @@ -455,6 +467,7 @@ private boolean worldTickSlow() workManager.onColonyTick(this); reproductionManager.onColonyTick(this); questManager.onColonyTick(); + travelingManager.onTick(); final long currTime = System.currentTimeMillis(); if (lastOnlineTime != 0) @@ -720,6 +733,9 @@ public void read(@NotNull final CompoundTag compound) questManager.deserializeNBT(compound.getCompound(TAG_QUEST_MANAGER)); eventDescManager.deserializeNBT(compound.getCompound(NbtTagConstants.TAG_EVENT_DESC_MANAGER)); + expeditionManager.deserializeNBT(compound.getCompound(NbtTagConstants.TAG_EXPEDITION_MANAGER)); + travelingManager.deserializeNBT(compound.getCompound(NbtTagConstants.TAG_TRAVELING_MANAGER)); + if (compound.contains(TAG_RESEARCH)) { researchManager.readFromNBT(compound.getCompound(TAG_RESEARCH)); @@ -878,6 +894,9 @@ public CompoundTag write(@NotNull final CompoundTag compound) compound.put(TAG_QUEST_MANAGER, questManager.serializeNBT()); compound.put(NbtTagConstants.TAG_EVENT_DESC_MANAGER, eventDescManager.serializeNBT()); + compound.put(NbtTagConstants.TAG_EXPEDITION_MANAGER, expeditionManager.serializeNBT()); + compound.put(NbtTagConstants.TAG_TRAVELING_MANAGER, travelingManager.serializeNBT()); + raidManager.write(compound); @NotNull final CompoundTag researchManagerCompound = new CompoundTag(); @@ -1918,6 +1937,19 @@ public IQuestManager getQuestManager() return questManager; } + @Override + @NotNull + public IColonyExpeditionManager getExpeditionManager() + { + return expeditionManager; + } + + @Override + public ITravelingManager getTravelingManager() + { + return travelingManager; + } + @Override public ICitizen getCitizen(final int id) { diff --git a/src/main/java/com/minecolonies/core/colony/ColonyView.java b/src/main/java/com/minecolonies/core/colony/ColonyView.java index c39b70ab11b..aa855fc8328 100644 --- a/src/main/java/com/minecolonies/core/colony/ColonyView.java +++ b/src/main/java/com/minecolonies/core/colony/ColonyView.java @@ -8,6 +8,7 @@ import com.minecolonies.api.colony.buildings.workerbuildings.ITownHallView; import com.minecolonies.api.colony.fields.IField; import com.minecolonies.api.colony.managers.interfaces.*; +import com.minecolonies.api.colony.managers.interfaces.expeditions.IColonyExpeditionManager; import com.minecolonies.api.colony.permissions.ColonyPlayer; import com.minecolonies.api.colony.permissions.IPermissions; import com.minecolonies.api.colony.permissions.Rank; @@ -29,8 +30,10 @@ import com.minecolonies.core.colony.buildings.modules.BuildingModules; import com.minecolonies.core.colony.buildings.views.AbstractBuildingView; import com.minecolonies.core.colony.buildings.workerbuildings.BuildingTownHall; +import com.minecolonies.core.colony.managers.ColonyExpeditionManager; import com.minecolonies.core.colony.managers.ResearchManager; import com.minecolonies.core.colony.managers.StatisticsManager; +import com.minecolonies.core.colony.managers.TravelingManager; import com.minecolonies.core.colony.permissions.PermissionsView; import com.minecolonies.core.colony.requestsystem.management.manager.StandardRequestManager; import com.minecolonies.core.colony.workorders.AbstractWorkOrder; @@ -240,6 +243,16 @@ public final class ColonyView implements IColonyView */ private IQuestManager questManager; + /** + * Client side expedition manager. + */ + private final IColonyExpeditionManager expeditionManager; + + /** + * Client side traveling manager. + */ + private final ITravelingManager travelingManager; + /** * Day in the colony. */ @@ -255,6 +268,8 @@ private ColonyView(final int id) this.id = id; this.researchManager = new ResearchManager(this); this.questManager = new QuestManager(this); + this.expeditionManager = new ColonyExpeditionManager(this); + this.travelingManager = new TravelingManager(this); } /** @@ -436,6 +451,32 @@ public static void serializeNetworkData(@NotNull Colony colony, @NotNull Friendl colony.getStatisticsManager().serialize(buf, hasNewSubscribers); buf.writeNbt(colony.getQuestManager().serializeNBT()); buf.writeInt(colony.getDay()); + + // Write expedition manager + if (colony.getExpeditionManager().isDirty() || hasNewSubscribers) + { + buf.writeBoolean(true); + buf.writeNbt(colony.getExpeditionManager().serializeNBT()); + + colony.getExpeditionManager().setDirty(false); + } + else + { + buf.writeBoolean(false); + } + + // Write traveling manager + if (colony.getTravelingManager().isDirty() || hasNewSubscribers) + { + buf.writeBoolean(true); + buf.writeNbt(colony.getTravelingManager().serializeNBT()); + + colony.getTravelingManager().setDirty(false); + } + else + { + buf.writeBoolean(false); + } } /** @@ -896,6 +937,17 @@ public IMessage handleColonyViewMessage(@NotNull final FriendlyByteBuf buf, @Not this.statisticManager.deserialize(buf); this.questManager.deserializeNBT(buf.readNbt()); this.day = buf.readInt(); + + if (buf.readBoolean()) + { + this.expeditionManager.deserializeNBT(buf.readNbt()); + } + + if (buf.readBoolean()) + { + this.travelingManager.deserializeNBT(buf.readNbt()); + } + return null; } @@ -1547,7 +1599,7 @@ public boolean areSpiesEnabled() } @Override - public ICitizenDataView getVisitor(final int citizenId) + public IVisitorViewData getVisitor(final int citizenId) { return visitors.get(citizenId); } @@ -1594,4 +1646,17 @@ public IQuestManager getQuestManager() { return this.questManager; } + + @Override + @NotNull + public IColonyExpeditionManager getExpeditionManager() + { + return expeditionManager; + } + + @Override + public ITravelingManager getTravelingManager() + { + return travelingManager; + } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/VisitorData.java b/src/main/java/com/minecolonies/core/colony/VisitorData.java index a5bfe02a5ca..07fd6df6181 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorData.java @@ -1,18 +1,25 @@ package com.minecolonies.core.colony; +import com.minecolonies.api.IMinecoloniesAPI; import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import com.minecolonies.api.entity.visitor.IVisitorType; import com.minecolonies.api.util.BlockPosUtil; -import com.minecolonies.api.util.WorldUtil; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.ItemStack; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; -import static com.minecolonies.api.util.constant.NbtTagConstants.*; +import java.util.List; + +import static com.minecolonies.api.entity.visitor.ModVisitorTypes.VISITOR_TYPE_ID; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; import static com.minecolonies.api.util.constant.SchematicTagConstants.TAG_SITTING; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_SITTING_POSITION; /** * Data for visitors @@ -20,129 +27,141 @@ public class VisitorData extends CitizenData implements IVisitorData { /** - * Recruit nbt tag + * NBT tags. */ - private static final String TAG_RECRUIT_COST = "rcost"; + public static final String TAG_VISITOR_TYPE = "visitorType"; + public static final String TAG_EXTRA_DATA = "extra"; + private static final String TAG_RECRUIT_COST = "rcost"; private static final String TAG_RECRUIT_COST_QTY = "rcostqty"; /** - * The position the citizen is sitting at + * The type of the visitor. */ - private BlockPos sittingPosition = BlockPos.ZERO; + private final IVisitorType visitorType; /** - * The recruitment level, used for stats/equipment and costs + * The extra data instances. */ - private ItemStack recruitCost = ItemStack.EMPTY; + private final List> extraData; /** - * Create a CitizenData given an ID. Used as a super-constructor or during loading. + * Create a VisitorData given an ID. Used as a super-constructor or during loading. * - * @param id ID of the Citizen. - * @param colony Colony the Citizen belongs to. + * @param id ID of the visitor. + * @param colony colony the visitor belongs to. + * @param visitorType the type of the visitor. */ - public VisitorData(final int id, final IColony colony) + public VisitorData(final int id, final IColony colony, final IVisitorType visitorType) { super(id, colony); + this.visitorType = visitorType; + this.extraData = List.copyOf(visitorType.getExtraDataKeys()); } - @Override - public CompoundTag serializeNBT() + /** + * Loads this citizen data from nbt + * + * @param colony colony to load for + * @param nbt nbt compound to read from + * @return new CitizenData + */ + public static IVisitorData loadVisitorFromNBT(final IColony colony, final CompoundTag nbt) { - CompoundTag compoundNBT = super.serializeNBT(); - CompoundTag item = new CompoundTag(); - recruitCost.save(item); - compoundNBT.put(TAG_RECRUIT_COST, item); - compoundNBT.putInt(TAG_RECRUIT_COST_QTY, recruitCost.getCount()); - BlockPosUtil.write(compoundNBT, TAG_SITTING, sittingPosition); - return compoundNBT; + final ResourceLocation visitorTypeKey = nbt.contains(TAG_VISITOR_TYPE) ? new ResourceLocation(nbt.getString(TAG_VISITOR_TYPE)) : VISITOR_TYPE_ID; + final IVisitorType visitorType = IMinecoloniesAPI.getInstance().getVisitorTypeRegistry().getValue(visitorTypeKey); + final IVisitorData data = new VisitorData(nbt.getInt(TAG_ID), colony, visitorType); + data.deserializeNBT(nbt); + return data; } @Override - public void deserializeNBT(final CompoundTag nbtTagCompound) + @NotNull + public IVisitorType getVisitorType() { - super.deserializeNBT(nbtTagCompound); - sittingPosition = BlockPosUtil.read(nbtTagCompound, TAG_SITTING); - recruitCost = ItemStack.of(nbtTagCompound.getCompound(TAG_RECRUIT_COST)); - recruitCost.setCount(nbtTagCompound.getInt(TAG_RECRUIT_COST_QTY)); + return visitorType; } @Override - public void setRecruitCosts(final ItemStack item) + @SuppressWarnings("unchecked") + public T getExtraDataValue(final IVisitorExtraData extraData) { - this.recruitCost = item; + return this.extraData.stream() + .filter(f -> f.equals(extraData)) + .map(m -> (T) m.getValue()) + .findFirst() + .orElseThrow(); } @Override - public ItemStack getRecruitCost() + @SuppressWarnings("unchecked") + public void setExtraDataValue(final IVisitorExtraData extraData, final T value) { - return recruitCost; + final IVisitorExtraData foundExtraData = this.extraData.stream() + .filter(f -> f.equals(extraData)) + .map(m -> (IVisitorExtraData) m) + .findFirst() + .orElseThrow(); + foundExtraData.setValue(value); } - /** - * Loads this citizen data from nbt - * - * @param colony colony to load for - * @param nbt nbt compound to read from - * @return new CitizenData - */ - public static IVisitorData loadVisitorFromNBT(final IColony colony, final CompoundTag nbt) + @Override + protected void respawnAfterUpdate(final BlockPos position) { - final IVisitorData data = new VisitorData(nbt.getInt(TAG_ID), colony); - data.deserializeNBT(nbt); - return data; + getColony().getVisitorManager().spawnOrCreateVisitor(visitorType, this, getColony().getWorld(), position); } @Override public void serializeViewNetworkData(@NotNull final FriendlyByteBuf buf) { super.serializeViewNetworkData(buf); - buf.writeItem(recruitCost); - buf.writeInt(recruitCost.getCount()); + buf.writeNbt(serializeNBT()); } @Override - public BlockPos getSittingPosition() + public CompoundTag serializeNBT() { - return sittingPosition; - } + final CompoundTag compound = super.serializeNBT(); + compound.putString(TAG_VISITOR_TYPE, visitorType.getId().toString()); - @Override - public void setSittingPosition(final BlockPos pos) - { - this.sittingPosition = pos; + final CompoundTag extraDataCompound = new CompoundTag(); + for (final IVisitorExtraData extraDataKey : extraData) + { + extraDataCompound.put(extraDataKey.getKey(), extraDataKey.serializeNBT()); + } + compound.put(TAG_EXTRA_DATA, extraDataCompound); + return compound; } @Override - public void updateEntityIfNecessary() + public void deserializeNBT(final CompoundTag nbtTagCompound) { - if (getEntity().isPresent()) + super.deserializeNBT(nbtTagCompound); + final CompoundTag extraDataCompound = nbtTagCompound.getCompound(TAG_EXTRA_DATA); + for (final IVisitorExtraData extraDataKey : extraData) { - final Entity entity = getEntity().get(); - if (entity.isAlive() && WorldUtil.isEntityBlockLoaded(entity.level, entity.blockPosition())) + if (extraDataCompound.contains(extraDataKey.getKey())) { - return; + extraDataKey.deserializeNBT(extraDataCompound.getCompound(extraDataKey.getKey())); } } - if (getLastPosition() != BlockPos.ZERO && (getLastPosition().getX() != 0 && getLastPosition().getZ() != 0) && WorldUtil.isEntityBlockLoaded(getColony().getWorld(), - getLastPosition())) + // TODO: Next major release: Remove backwards compat for old visitor data + if (nbtTagCompound.contains(TAG_SITTING)) { - getColony().getVisitorManager().spawnOrCreateCivilian(this, getColony().getWorld(), getLastPosition(), true); - } - else if (getHomeBuilding() != null) - { - if (WorldUtil.isEntityBlockLoaded(getColony().getWorld(), getHomeBuilding().getID())) - { - final BlockPos spawnPos = BlockPosUtil.findSpawnPosAround(getColony().getWorld(), getHomeBuilding().getID()); - if (spawnPos != null) - { - getColony().getVisitorManager().spawnOrCreateCivilian(this, getColony().getWorld(), spawnPos, true); - } - } + setExtraDataValue(EXTRA_DATA_SITTING_POSITION, BlockPosUtil.read(nbtTagCompound, TAG_SITTING)); + final ItemStack itemStack = ItemStack.of(nbtTagCompound.getCompound(TAG_RECRUIT_COST)); + itemStack.setCount(nbtTagCompound.getInt(TAG_RECRUIT_COST_QTY)); + setExtraDataValue(EXTRA_DATA_RECRUIT_COST, itemStack); } } + @Override + public void update(int tickRate) + { + super.update(tickRate); + visitorType.update(this); + } + @Override public void applyResearchEffects() { diff --git a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java index 15a5312d809..02c08b0d585 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java @@ -1,17 +1,19 @@ package com.minecolonies.core.colony; +import com.minecolonies.api.IMinecoloniesAPI; import com.minecolonies.api.colony.IColonyView; import com.minecolonies.api.colony.IVisitorViewData; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import net.minecraft.Util; -import net.minecraft.client.Minecraft; -import net.minecraft.client.resources.DefaultPlayerSkin; +import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import com.minecolonies.api.entity.visitor.IVisitorType; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.List; + +import static com.minecolonies.core.colony.VisitorData.TAG_EXTRA_DATA; +import static com.minecolonies.core.colony.VisitorData.TAG_VISITOR_TYPE; import java.util.Map; /** @@ -20,14 +22,14 @@ public class VisitorDataView extends CitizenDataView implements IVisitorViewData { /** - * The recruitment costs + * The type of the visitor. */ - private ItemStack recruitmentCosts; + private IVisitorType visitorType; /** - * Cached player info for custom texture. + * The extra data instances. */ - private volatile ResourceLocation cachedTexture; + private List> extraData; /** * Create a CitizenData given an ID. Used as a super-constructor or during loading. @@ -44,38 +46,35 @@ public VisitorDataView(final int id, final IColonyView colony) public void deserialize(@NotNull final FriendlyByteBuf buf) { super.deserialize(buf); - recruitmentCosts = buf.readItem(); - recruitmentCosts.setCount(buf.readInt()); + final CompoundTag compoundTag = buf.readNbt(); + if (compoundTag != null) + { + final ResourceLocation visitorTypeKey = new ResourceLocation(compoundTag.getString(TAG_VISITOR_TYPE)); + visitorType = IMinecoloniesAPI.getInstance().getVisitorTypeRegistry().getValue(visitorTypeKey); + extraData = visitorType.getExtraDataKeys(); + + final CompoundTag compound = compoundTag.getCompound(TAG_EXTRA_DATA); + for (final IVisitorExtraData extraDataKey : extraData) + { + extraDataKey.deserializeNBT(compound.getCompound(extraDataKey.getKey())); + } + } } @Override - public ItemStack getRecruitCost() + public @NotNull IVisitorType getVisitorType() { - return recruitmentCosts; + return visitorType; } @Override - public ResourceLocation getCustomTexture() + @SuppressWarnings("unchecked") + public T getExtraDataValue(final IVisitorExtraData extraData) { - if (textureUUID == null) - { - return null; - } - if (cachedTexture == null) - { - cachedTexture = DefaultPlayerSkin.getDefaultSkin(textureUUID); - Util.backgroundExecutor().execute(() -> - { - Minecraft minecraft = Minecraft.getInstance(); - final GameProfile profile = new GameProfile(textureUUID, "mcoltexturequery"); - minecraft.getMinecraftSessionService().fillProfileProperties(profile, true); - Map map = minecraft.getSkinManager().getInsecureSkinInformation(profile); - if (!map.isEmpty()) - { - cachedTexture = minecraft.getSkinManager().registerTexture(map.get(MinecraftProfileTexture.Type.SKIN), MinecraftProfileTexture.Type.SKIN); - } - }); - } - return cachedTexture; + return this.extraData.stream() + .filter(f -> f.equals(extraData)) + .map(m -> (T) m.getValue()) + .findFirst() + .orElseThrow(); } } diff --git a/src/main/java/com/minecolonies/core/colony/buildings/modules/BuildingResourcesModule.java b/src/main/java/com/minecolonies/core/colony/buildings/modules/BuildingResourcesModule.java index 0aa0adaa4e7..abd17e15860 100644 --- a/src/main/java/com/minecolonies/core/colony/buildings/modules/BuildingResourcesModule.java +++ b/src/main/java/com/minecolonies/core/colony/buildings/modules/BuildingResourcesModule.java @@ -23,6 +23,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,7 +31,6 @@ import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_CURR_STAGE; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_TOTAL_STAGES; -import net.minecraftforge.common.capabilities.ForgeCapabilities; /** * The structureBuilder building. @@ -81,7 +81,7 @@ public void serializeToView(@NotNull final FriendlyByteBuf buf) for (@NotNull final BuildingBuilderResource resource : neededResources.values()) { buf.writeItem(resource.getItemStack()); - buf.writeInt(resource.getAvailable()); + buf.writeInt(resource.getAmountAvailable()); buf.writeInt(resource.getAmount()); qty += resource.getAmount(); } diff --git a/src/main/java/com/minecolonies/core/colony/buildings/modules/TavernBuildingModule.java b/src/main/java/com/minecolonies/core/colony/buildings/modules/TavernBuildingModule.java index 31ef96b4282..49ed3406147 100644 --- a/src/main/java/com/minecolonies/core/colony/buildings/modules/TavernBuildingModule.java +++ b/src/main/java/com/minecolonies/core/colony/buildings/modules/TavernBuildingModule.java @@ -6,6 +6,7 @@ import com.minecolonies.api.colony.buildings.modules.*; import com.minecolonies.api.colony.buildings.modules.stat.IStat; import com.minecolonies.api.colony.interactionhandling.ChatPriority; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; import com.minecolonies.api.sounds.TavernSounds; import com.minecolonies.api.util.BlockPosUtil; import com.minecolonies.api.util.Tuple; @@ -16,17 +17,17 @@ import com.minecolonies.core.colony.interactionhandling.RecruitmentInteraction; import com.minecolonies.core.datalistener.CustomVisitorListener; import com.minecolonies.core.network.messages.client.colony.PlayMusicAtPosMessage; -import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; -import net.minecraft.nbt.ListTag; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -39,6 +40,8 @@ import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_VISITORS; import static com.minecolonies.api.util.constant.SchematicTagConstants.TAG_SITTING; import static com.minecolonies.api.util.constant.SchematicTagConstants.TAG_WORK; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_SITTING_POSITION; /** * Tavern building for the colony. Houses 4 citizens Plays a tavern theme on entering Spawns/allows citizen recruitment Spawns trader/quest npcs @@ -111,13 +114,14 @@ public void onPlayerEnterBuilding(final Player player) BlockPos avg = BlockPos.ZERO; for (final Integer id : externalCitizens) { - final IVisitorData data = building.getColony().getVisitorManager().getVisitor(id); + final IVisitorData data = building.getColony().getVisitorManager().getCivilian(id); if (data != null) { - if (!data.getSittingPosition().equals(BlockPos.ZERO)) + final BlockPos sittingPosition = data.getExtraDataValue(EXTRA_DATA_SITTING_POSITION); + if (!sittingPosition.equals(BlockPos.ZERO)) { count++; - avg = avg.offset(data.getSittingPosition()); + avg = avg.offset(sittingPosition); } } } @@ -145,7 +149,7 @@ public void onColonyTick(@NotNull final IColony colony) musicCooldown -= MAX_TICKRATE; } - externalCitizens.removeIf(id -> colony.getVisitorManager().getVisitor(id) == null); + externalCitizens.removeIf(id -> colony.getVisitorManager().getCivilian(id) == null); if (noVisitorTime > 0) { @@ -172,7 +176,7 @@ public void onUpgradeComplete(final int newlevel) */ private void spawnVisitor() { - IVisitorData newCitizen = (IVisitorData) building.getColony().getVisitorManager().createAndRegisterCivilianData(); + IVisitorData newCitizen = building.getColony().getVisitorManager().createAndRegisterVisitorData(ModVisitorTypes.visitor.get()); externalCitizens.add(newCitizen.getId()); newCitizen.setBedPos(building.getPosition()); @@ -181,11 +185,6 @@ private void spawnVisitor() int recruitLevel = building.getColony().getWorld().random.nextInt(10 * building.getBuildingLevel()) + 15; List> recruitCosts = IColonyManager.getInstance().getCompatibilityManager().getRecruitmentCostsWeights(); - if (newCitizen.getName().contains("Ray")) - { - newCitizen.setRecruitCosts(new ItemStack(Items.BAKED_POTATO, 64)); - } - newCitizen.getCitizenSkillHandler().init(recruitLevel); BlockPos spawnPos = BlockPosUtil.findSpawnPosAround(building.getColony().getWorld(), building.getPosition()); @@ -226,7 +225,7 @@ private void spawnVisitor() boots = new ItemStack(Items.DIAMOND_BOOTS); } - newCitizen.setRecruitCosts(new ItemStack(cost.getA(), (int) (recruitLevel * 3.0 / cost.getB()))); + newCitizen.setExtraDataValue(EXTRA_DATA_RECRUIT_COST, new ItemStack(cost.getA(), (int) (recruitLevel * 3.0 / cost.getB()))); if (!CustomVisitorListener.chanceCustomVisitors(newCitizen)) { @@ -235,7 +234,7 @@ private void spawnVisitor() ChatPriority.IMPORTANT)); } - building.getColony().getVisitorManager().spawnOrCreateCivilian(newCitizen, building.getColony().getWorld(), spawnPos, true); + building.getColony().getVisitorManager().spawnOrCreateVisitor(ModVisitorTypes.visitor.get(), newCitizen, building.getColony().getWorld(), spawnPos); if (newCitizen.getEntity().isPresent()) { newCitizen.getEntity().get().setItemSlot(EquipmentSlot.FEET, boots); @@ -291,10 +290,10 @@ public BlockPos getFreeSitPosition() for (final Integer id : externalCitizens) { - final IVisitorData data = building.getColony().getVisitorManager().getVisitor(id); + final IVisitorData data = building.getColony().getVisitorManager().getCivilian(id); if (data != null) { - positions.remove(data.getSittingPosition()); + positions.remove(data.getExtraDataValue(EXTRA_DATA_SITTING_POSITION)); } } @@ -311,7 +310,7 @@ public void onDestroyed() { for (final Integer id : externalCitizens) { - building.getColony().getVisitorManager().removeCivilian(building.getColony().getVisitorManager().getVisitor(id)); + building.getColony().getVisitorManager().removeCivilian(building.getColony().getVisitorManager().getCivilian(id)); } } diff --git a/src/main/java/com/minecolonies/core/colony/buildings/modules/settings/StringSetting.java b/src/main/java/com/minecolonies/core/colony/buildings/modules/settings/StringSetting.java index a43a62bd71a..4e34da24c4d 100644 --- a/src/main/java/com/minecolonies/core/colony/buildings/modules/settings/StringSetting.java +++ b/src/main/java/com/minecolonies/core/colony/buildings/modules/settings/StringSetting.java @@ -10,9 +10,9 @@ import com.minecolonies.api.colony.buildings.modules.settings.IStringSetting; import com.minecolonies.api.colony.buildings.views.IBuildingView; import com.minecolonies.api.util.Log; -import com.minecolonies.api.util.MathUtils; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -115,7 +115,7 @@ public void render( final IBuildingView building, final BOWindow window) { - int buttonWidth = MathUtils.clamp(getButtonWidth(settingsModuleView), 0, MAX_BUTTON_WIDTH); + int buttonWidth = Mth.clamp(getButtonWidth(settingsModuleView), 0, MAX_BUTTON_WIDTH); ButtonImage triggerButton = pane.findPaneOfTypeByID("trigger", ButtonImage.class); triggerButton.setSize(buttonWidth, triggerButton.getHeight()); triggerButton.setEnabled(isActive(settingsModuleView)); diff --git a/src/main/java/com/minecolonies/core/colony/buildings/utils/BuildingBuilderResource.java b/src/main/java/com/minecolonies/core/colony/buildings/utils/BuildingBuilderResource.java index 0bcfa80bc8f..1e18ec97329 100755 --- a/src/main/java/com/minecolonies/core/colony/buildings/utils/BuildingBuilderResource.java +++ b/src/main/java/com/minecolonies/core/colony/buildings/utils/BuildingBuilderResource.java @@ -1,25 +1,25 @@ package com.minecolonies.core.colony.buildings.utils; import com.minecolonies.api.crafting.ItemStorage; +import com.minecolonies.core.client.gui.generic.ResourceItem.Resource; +import com.minecolonies.core.client.gui.generic.ResourceItem.ResourceAvailability; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; -import java.io.Serializable; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; +import java.util.List; /** * Information about a resource. - How many are needed to finish the build - How many are available to the builder - How many are in the player's inventory (client side only) */ -public class BuildingBuilderResource extends ItemStorage +public class BuildingBuilderResource extends ItemStorage implements Resource { private int amountAvailable; private int amountPlayer; /** - * The amount currently beeing delivered + * The amount currently being delivered */ private int amountInDelivery = 0; @@ -59,19 +59,6 @@ public void addAvailable(final int amount) this.amountAvailable += amount; } - /** - * get how much more is needed from the player. - *

- * This is taking the builder's inventory + chest into account and the player inventory Negative number is when the player does not have enough Negative number is when the - * player does not more than enough - * - * @return the amount needed - */ - public int getMissingFromPlayer() - { - return amountPlayer + amountAvailable - getAmount(); - } - @Override public String toString() { @@ -86,13 +73,21 @@ public String toString() + ") => " + getAvailabilityStatus().name(); } - public String getName() + @Override + public Component getName() { //It is the bet way ? - return getItemStack().getHoverName().getString(); + return Component.literal(getItemStack().getHoverName().getString()); + } + + @Override + public List getIcon() + { + return List.of(getItemStack().copyWithCount(1)); } - public RessourceAvailability getAvailabilityStatus() + @Override + public ResourceAvailability getAvailabilityStatus() { if (getAmount() > amountAvailable) { @@ -100,21 +95,21 @@ public RessourceAvailability getAvailabilityStatus() { if (amountInDelivery > 0) { - return RessourceAvailability.IN_DELIVERY; + return ResourceAvailability.IN_DELIVERY; } - return RessourceAvailability.DONT_HAVE; + return ResourceAvailability.DONT_HAVE; } if (amountPlayer < (getAmount() - amountAvailable)) { if (amountInDelivery > 0) { - return RessourceAvailability.IN_DELIVERY; + return ResourceAvailability.IN_DELIVERY; } - return RessourceAvailability.NEED_MORE; + return ResourceAvailability.NEED_MORE; } - return RessourceAvailability.HAVE_ENOUGH; + return ResourceAvailability.HAVE_ENOUGH; } - return RessourceAvailability.NOT_NEEDED; + return ResourceAvailability.NOT_NEEDED; } @Override @@ -130,7 +125,7 @@ public boolean equals(final Object o) { final BuildingBuilderResource that = (BuildingBuilderResource) o; - return this.getAvailable() == that.getAvailable() && this.getPlayerAmount() == that.getPlayerAmount(); + return this.getAmountAvailable() == that.getAmountAvailable() && this.getAmountPlayer() == that.getAmountPlayer(); } return false; @@ -143,7 +138,8 @@ public boolean equals(final Object o) * * @return the amount available */ - public int getAvailable() + @Override + public int getAmountAvailable() { return amountAvailable; } @@ -158,12 +154,8 @@ public void setAvailable(final int amount) amountAvailable = amount; } - /** - * get how the player have in its inventory. - * - * @return the amount - */ - public int getPlayerAmount() + @Override + public int getAmountPlayer() { return amountPlayer; } @@ -178,6 +170,7 @@ public void setPlayerAmount(final int amount) amountPlayer = amount; } + @Override public int getAmountInDelivery() { return amountInDelivery; @@ -187,56 +180,4 @@ public void setAmountInDelivery(final int amountInDelivery) { this.amountInDelivery = amountInDelivery; } - - /** - * Availability status of the resource. according to the builder's chest, inventory and the player's inventory - */ - public enum RessourceAvailability - { - NOT_NEEDED, - IN_DELIVERY, - DONT_HAVE, - NEED_MORE, - HAVE_ENOUGH - } - - /** - * Comparator class for BuildingBuilderResource. - *

- * This is use in the gui to order the list of resources needed. - */ - public static class ResourceComparator implements Comparator, Serializable - { - private static final long serialVersionUID = 1; - private final Map order = new HashMap<>(); - - public ResourceComparator(final RessourceAvailability... resourceOrder) - { - for (int i = 0; i < resourceOrder.length; i++) - { - order.put(resourceOrder[i], i); - } - } - - /** - * Compare to resource together. - *

- * We want the item availalable in the player inventory first and the one not needed last In alphabetical order otherwise - */ - @Override - public int compare(final BuildingBuilderResource resource1, final BuildingBuilderResource resource2) - { - if (resource1.getAvailabilityStatus() == resource2.getAvailabilityStatus()) - { - return resource1.getName().compareTo(resource2.getName()); - } - - if (!order.isEmpty()) - { - return order.get(resource2.getAvailabilityStatus()).compareTo(order.get(resource1.getAvailabilityStatus())); - } - - return resource2.getAvailabilityStatus().compareTo(resource1.getAvailabilityStatus()); - } - } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/events/ColonyExpeditionEvent.java b/src/main/java/com/minecolonies/core/colony/events/ColonyExpeditionEvent.java new file mode 100644 index 00000000000..737ae506c54 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/events/ColonyExpeditionEvent.java @@ -0,0 +1,756 @@ +package com.minecolonies.core.colony.events; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.colony.colonyEvents.EventStatus; +import com.minecolonies.api.colony.colonyEvents.IColonyEvent; +import com.minecolonies.api.colony.expeditions.ExpeditionFinishedStatus; +import com.minecolonies.api.colony.expeditions.ExpeditionFinishedStatusType; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import com.minecolonies.api.colony.managers.interfaces.expeditions.ColonyExpedition; +import com.minecolonies.api.compatibility.Compatibility; +import com.minecolonies.api.equipment.ModEquipmentTypes; +import com.minecolonies.api.loot.ModLootConditions; +import com.minecolonies.api.util.*; +import com.minecolonies.api.util.MessageUtils.MessagePriority; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.colony.expeditions.ExpeditionCitizenMember; +import com.minecolonies.core.colony.expeditions.ExpeditionVisitorMember; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.encounters.ExpeditionEncounter; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.datalistener.ExpeditionEncounterListener; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DespawnTimeData.DespawnTime; +import com.minecolonies.core.items.ItemAdventureToken; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.damagesource.CombatRules; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Entity.RemovalReason; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobType; +import net.minecraft.world.food.FoodProperties; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.SwordItem; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.LootParams.Builder; +import net.minecraftforge.common.extensions.IForgeItemStack; +import net.minecraftforge.items.ItemStackHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.minecolonies.api.util.constant.ExpeditionConstants.*; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_INVENTORY; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DEFAULT_DESPAWN_TIME; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_DESPAWN_TIME; +import static com.minecolonies.core.generation.ExpeditionResourceManager.getStructureId; +import static com.minecolonies.core.generation.defaults.DefaultExpeditionStructureLootProvider.RUINED_PORTAL_ID; +import static com.minecolonies.core.generation.defaults.DefaultExpeditionStructureLootProvider.STRONGHOLD_ID; + +/** + * Event class for simulating colony expeditions. + */ +public class ColonyExpeditionEvent implements IColonyEvent +{ + /** + * Tags for the adventure tokens. + */ + public static final String TOKEN_TAG_EXPEDITION_TYPE = "type"; + public static final String TOKEN_TAG_EXPEDITION_TYPE_STRUCTURE_START = "structure_start"; + public static final String TOKEN_TAG_EXPEDITION_TYPE_STRUCTURE_END = "structure_end"; + public static final String TOKEN_TAG_EXPEDITION_STRUCTURE = "structure"; + public static final String TOKEN_TAG_EXPEDITION_TYPE_ENCOUNTER = "encounter"; + public static final String TOKEN_TAG_EXPEDITION_ENCOUNTER = "encounter"; + public static final String TOKEN_TAG_EXPEDITION_ENCOUNTER_AMOUNT = "amount"; + public static final String TOKEN_TAG_EXPEDITION_ENCOUNTER_SCALE = "scale"; + + /** + * The event ID. + */ + public static final ResourceLocation COLONY_EXPEDITION_EVENT_TYPE_ID = new ResourceLocation(Constants.MOD_ID, "colony_expedition"); + + /** + * NBT tags. + */ + private static final String TAG_EXPEDITION_ID = "expeditionId"; + private static final String TAG_REMAINING_ITEMS = "remainingItems"; + private static final String TAG_END_TIME = "endTime"; + + /** + * The size of the expedition inventory. + */ + private static final int EXPEDITION_INVENTORY_SIZE = 27; + + /** + * Usage damage percentages. + */ + private static final float MIN_PERCENTAGE_USAGE_DAMAGE = 0.05f; + private static final float MAX_PERCENTAGE_USAGE_DAMAGE = 0.15f; + + /** + * The id of this event. + */ + private final int id; + + /** + * The id of the expedition. + */ + private final int expeditionId; + + /** + * The colony this event is for. + */ + private final IColony colony; + + /** + * Random instance. + */ + private final RandomSource random; + + /** + * The inventory for the expedition. + */ + private final ItemStackHandler inventory = new ItemStackHandler(EXPEDITION_INVENTORY_SIZE); + + /** + * Contains a set of items that still have yet to be found. + */ + @NotNull + private Deque remainingItems = new ArrayDeque<>(); + + /** + * The minimum time that the expedition is able to end at. + */ + private long endTime = -1; + + /** + * The cached expedition instance. + */ + private ColonyExpedition cachedExpedition; + + /** + * Create a new colony expedition event. + * + * @param colony the colony instance. + * @param expedition the expedition instance. + */ + public ColonyExpeditionEvent(final IColony colony, final ColonyExpedition expedition) + { + this.colony = colony; + this.random = RandomSource.create(); + this.id = colony.getEventManager().getAndTakeNextEventID(); + this.expeditionId = expedition.getId(); + } + + /** + * Create a new colony expedition event. + * + * @param colony the colony instance. + */ + private ColonyExpeditionEvent(final int id, final int expeditionId, final IColony colony) + { + this.id = id; + this.expeditionId = expeditionId; + this.colony = colony; + this.random = RandomSource.create(); + } + + /** + * Loads the event from the nbt compound. + * + * @param colony colony to load into + * @param compound NBT compound with saved values + * @return the raid event. + */ + public static ColonyExpeditionEvent loadFromNBT(final IColony colony, final CompoundTag compound) + { + final int id = compound.getInt(TAG_ID); + final int expeditionId = compound.getInt(TAG_EXPEDITION_ID); + return new ColonyExpeditionEvent(id, expeditionId, colony); + } + + /** + * Get the expedition instance for this event. + * + * @return the expedition instance. + */ + public ColonyExpedition getExpedition() + { + if (cachedExpedition == null) + { + cachedExpedition = colony.getExpeditionManager().getActiveExpedition(expeditionId); + } + return cachedExpedition; + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + compound.putInt(TAG_ID, id); + compound.putInt(TAG_EXPEDITION_ID, expeditionId); + compound.put(TAG_INVENTORY, inventory.serializeNBT()); + compound.put(TAG_REMAINING_ITEMS, remainingItems.stream().map(IForgeItemStack::serializeNBT).collect(NBTUtils.toListNBT())); + compound.putLong(TAG_END_TIME, endTime); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + inventory.deserializeNBT(compoundTag.getCompound(TAG_INVENTORY)); + remainingItems = NBTUtils.streamCompound(compoundTag.getList(TAG_REMAINING_ITEMS, Tag.TAG_COMPOUND)).map(ItemStack::of).collect(Collectors.toCollection(ArrayDeque::new)); + endTime = compoundTag.getLong(TAG_END_TIME); + } + + /** + * Simulates an entire mob fighting process. + * This is essentially a turn based combat system where each guard rotates attacks. + * + * @param encounter the encounter to fight. + * @param encounterAmount a number for the mob encounter amount. + * @param scaleEncounterSize whether the difficulty should scale the size of the amount of mobs. + */ + private void processMobFight(final @NotNull ExpeditionEncounter encounter, final int encounterAmount, final boolean scaleEncounterSize) + { + final ColonyExpedition expedition = getExpedition(); + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getExpeditionType(expedition.getExpeditionTypeId()); + if (expeditionType == null) + { + return; + } + + // Determine the amount of mobs we're going to fight + int amount = encounterAmount; + if (scaleEncounterSize) + { + amount *= expeditionType.difficulty().getMobEncounterMultiplier(); + } + + // Determine the mob type + MobType mobType = MobType.UNDEFINED; + try + { + final Entity entity = encounter.entityType().create(colony.getWorld()); + if (entity instanceof Mob mob) + { + mobType = mob.getMobType(); + } + entity.remove(RemovalReason.DISCARDED); + } + catch (Exception ex) + { + Log.getLogger().warn("Failure attempting to spawn", ex); + } + + for (int i = 0; i < amount; i++) + { + double encounterHealth = encounter.health(); + + // Keep the fight going as long as the mob is not dead. + while (encounterHealth > 0) + { + final IExpeditionMember attacker = getNextAttacker(expedition); + if (attacker == null) + { + // When no attackers are available, it means everyone is dead, so the fight cannot continue + remainingItems.clear(); + break; + } + + final ItemStack weapon = attacker.getPrimaryWeapon(); + encounterHealth -= CombatRules.getDamageAfterAbsorb(getWeaponDamage(weapon, mobType), encounter.armor(), 0); + if (weapon.hurt(1, random, null)) + { + attacker.setPrimaryWeapon(ItemStack.EMPTY); + } + + if (encounterHealth > 0) + { + final float damageAmount = handleDamageReduction(attacker, encounter.damage()); + attacker.setHealth(Math.max(0, attacker.getHealth() - damageAmount)); + encounterHealth -= encounter.reflectingDamage(); + + if (attacker.isDead()) + { + getExpedition().memberLost(attacker); + } + } + } + + if (encounterHealth <= 0) + { + expedition.mobKilled(encounter.id()); + final List loot = processLootTable(encounter.lootTable(), expeditionType); + loot.forEach(this::processReward); + } + } + } + + /** + * Get the next attacker to engage in a mob fight. + * + * @param expedition the expedition instance. + * @return the next member to attack, or null if no one is capable of fighting. + */ + private IExpeditionMember getNextAttacker(final ColonyExpedition expedition) + { + final List> activeMembers = expedition.getActiveMembers(); + if (activeMembers.isEmpty()) + { + return null; + } + + IExpeditionMember selected = null; + for (final IExpeditionMember member : activeMembers) + { + if (member instanceof ExpeditionCitizenMember && (selected == null || member.getHealth() > selected.getHealth())) + { + selected = member; + } + } + + final ExpeditionVisitorMember leader = expedition.getLeader(); + if (selected == null && !leader.isDead()) + { + selected = leader; + } + + return selected; + } + + /** + * Processes all the special adventure tokens part of the loot table for special actions. + * + * @param itemStack the item stack. + */ + private void processAdventureToken(final ItemStack itemStack) + { + final CompoundTag compound = itemStack.getTag(); + if (compound == null) + { + return; + } + + final String type = compound.getString(TOKEN_TAG_EXPEDITION_TYPE); + switch (type) + { + case TOKEN_TAG_EXPEDITION_TYPE_ENCOUNTER: + { + final String encounterId = compound.getString(TOKEN_TAG_EXPEDITION_ENCOUNTER); + final ExpeditionEncounter encounter = ExpeditionEncounterListener.getEncounter(new ResourceLocation(encounterId)); + if (encounter == null) + { + Log.getLogger().warn("Expedition loot table referred to encounter '{}' which does not exist.", encounterId); + break; + } + + final int amount = compound.contains(TOKEN_TAG_EXPEDITION_ENCOUNTER_AMOUNT) ? compound.getInt(TOKEN_TAG_EXPEDITION_ENCOUNTER_AMOUNT) : 1; + final boolean scaleAmount = !compound.contains(TOKEN_TAG_EXPEDITION_ENCOUNTER_SCALE) || compound.getBoolean(TOKEN_TAG_EXPEDITION_ENCOUNTER_SCALE); + + processMobFight(encounter, amount, scaleAmount); + break; + } + case TOKEN_TAG_EXPEDITION_TYPE_STRUCTURE_START: + { + final ResourceLocation structureId = new ResourceLocation(compound.getString(TOKEN_TAG_EXPEDITION_STRUCTURE)); + getExpedition().advanceStage(Component.translatable(EXPEDITION_STAGE_STRUCTURE + structureId)); + + if (structureId.equals(getStructureId(RUINED_PORTAL_ID))) + { + colony.getExpeditionManager().unlockNether(); + } + else if (structureId.equals(getStructureId(STRONGHOLD_ID))) + { + colony.getExpeditionManager().unlockEnd(); + } + break; + } + case TOKEN_TAG_EXPEDITION_TYPE_STRUCTURE_END: + { + getExpedition().advanceStage(Component.translatable(EXPEDITION_STAGE_WILDERNESS)); + break; + } + default: + Log.getLogger().warn("Adventure token with type '{}' found. This adventure token is not implemented to do anything, skipping.", type); + break; + } + } + + @Override + public EventStatus getStatus() + { + if (endTime == -1) + { + return EventStatus.STARTING; + } + + if (endTime < colony.getWorld().getGameTime() && remainingItems.isEmpty()) + { + return EventStatus.DONE; + } + + return EventStatus.PROGRESSING; + } + + @Override + public void setStatus(final EventStatus status) + { + // No-op, expedition status uses a different enumeration to control active status, which can only be modified directly within this event. + } + + @Override + public int getID() + { + return id; + } + + @Override + public ResourceLocation getEventTypeID() + { + return COLONY_EXPEDITION_EVENT_TYPE_ID; + } + + @Override + public void setColony(final @NotNull IColony colony) + { + // No-op + } + + @Override + public void onUpdate() + { + // If the deque is empty, we can set the flag for loot table empty to be done. + if (!remainingItems.isEmpty()) + { + processLootTableEntry(); + } + } + + @Override + public void onStart() + { + final Level world = colony.getWorld(); + if (!world.isClientSide) + { + final ColonyExpedition expedition = getExpedition(); + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getExpeditionType(expedition.getExpeditionTypeId()); + if (expeditionType == null) + { + Log.getLogger().warn("Expedition cannot start because of a missing expedition type: '{}'", expedition.getExpeditionTypeId()); + return; + } + + // Calculate the end time + final int randomTimeOffset = expeditionType.difficulty().getRandomTime(); + final int randomTime = random.nextInt(-randomTimeOffset, randomTimeOffset); + + endTime = world.getGameTime() + 1; // TODO: expeditionType.getDifficulty().getBaseTime() + randomTime; + + // Move the equipment into the temporary event storage for inventory simulation. + expedition.getEquipment().forEach(f -> InventoryUtils.addItemStackToItemHandler(inventory, f)); + + // Generate the loot table + remainingItems = new ArrayDeque<>(processLootTable(expeditionType.lootTable(), expeditionType)); + } + } + + @Override + public void onFinish() + { + final ColonyExpedition expedition = getExpedition(); + expedition.cleanStages(); + + ExpeditionFinishedStatus finishedStatus = ExpeditionFinishedStatus.RETURNED; + if (expedition.getActiveMembers().isEmpty()) + { + finishedStatus = ExpeditionFinishedStatus.KILLED; + } + else + { + final int chance = random.nextInt(100); + if (chance <= 2) + { + finishedStatus = ExpeditionFinishedStatus.LOST; + } + } + + if (!colony.getExpeditionManager().finishExpedition(expeditionId, finishedStatus)) + { + Log.getLogger().warn(String.format("Expedition with id %d could not be found after finishing.", expeditionId)); + } + + if (finishedStatus.getStatusType().equals(ExpeditionFinishedStatusType.SUCCESSFUL)) + { + MessageUtils.format(EXPEDITION_FINISH_MESSAGE, expedition.getLeader().getName()).withPriority(MessagePriority.IMPORTANT).sendTo(colony).forManagers(); + } + else + { + MessageUtils.format(EXPEDITION_FAILURE_MESSAGE, expedition.getLeader().getName()).withPriority(MessagePriority.DANGER).sendTo(colony).forManagers(); + } + + // Remove all members to the travelling manager and respawn them. + for (final IExpeditionMember member : expedition.getMembers()) + { + colony.getTravelingManager().finishTravellingFor(member.getId()); + + if (member.isDead()) + { + member.removeFromColony(colony); + } + else + { + // Apply usage damage to all armor of all members. + final ArmorList armor = getArmor(member); + damageArmor(armor, + armor.getTotalArmor() * Mth.randomBetween(random, MIN_PERCENTAGE_USAGE_DAMAGE, MAX_PERCENTAGE_USAGE_DAMAGE), + slot -> member.setArmor(slot, ItemStack.EMPTY)); + } + } + + // Add all the loot to the leader inventory + final IVisitorData leaderData = expedition.getLeader().resolveCivilianData(colony); + if (leaderData != null) + { + InventoryUtils.clearItemHandler(leaderData.getInventory()); + InventoryUtils.transferAllItemHandler(inventory, leaderData.getInventory()); + leaderData.setExtraDataValue(EXTRA_DATA_DESPAWN_TIME, DespawnTime.fromNow(colony.getWorld(), DEFAULT_DESPAWN_TIME)); + } + } + + /** + * Process a rewards by inserting it into the expedition inventory, if successful, log that we found the rewards. + * + * @param itemStack the input item stack. + */ + private void processReward(final ItemStack itemStack) + { + if (InventoryUtils.addItemStackToItemHandler(inventory, itemStack)) + { + getExpedition().rewardFound(itemStack); + } + } + + /** + * Processes the next item from the loot table list. + */ + private void processLootTableEntry() + { + // Process the next item in the loot table deque. + final ItemStack nextItem = remainingItems.pop(); + if (nextItem.isEmpty()) + { + return; + } + + // Check if there's food left in an attempt to heal members. + if (!attemptHealMembers()) + { + remainingItems.clear(); + return; + } + + if (nextItem.getItem() instanceof ItemAdventureToken) + { + processAdventureToken(nextItem); + } + else + { + processReward(nextItem); + } + } + + /** + * Attempt a single heal attempt on all members. + * + * @return whether there is food left and the expedition may continue. + */ + private boolean attemptHealMembers() + { + for (final IExpeditionMember member : getExpedition().getActiveMembers()) + { + if (member.getHealth() < member.getMaxHealth()) + { + final int slot = InventoryUtils.findFirstSlotInItemHandlerNotEmptyWith(inventory, ItemStackUtils.ISFOOD); + if (slot >= 0) + { + final FoodProperties foodStack = inventory.getStackInSlot(slot).getFoodProperties(null); + member.setHealth(Mth.clamp(member.getHealth() + foodStack.getNutrition(), 0, member.getMaxHealth())); + } + else + { + return false; + } + } + } + + return true; + } + + /** + * Get the damage the given weapon item stack will do. + * + * @param itemStack the item stack. + * @param target the kind of mob they will deal damage to. + * @return the damage value. + */ + private float getWeaponDamage(final ItemStack itemStack, final MobType target) + { + if (ModEquipmentTypes.sword.get().checkIsEquipment(itemStack)) + { + if (Compatibility.isTinkersWeapon(itemStack)) + { + return Math.min(1, (float) Compatibility.getAttackDamage(itemStack)); + } + + if (itemStack.getItem() instanceof SwordItem swordItem) + { + return swordItem.getDamage() + EnchantmentHelper.getDamageBonus(itemStack, target); + } + } + + if (ModEquipmentTypes.bow.get().checkIsEquipment(itemStack)) + { + return 6; + } + + return 1; + } + + /** + * Handler methods for calculating damage reduction based on available armor. + * + * @param expeditionMember the expedition member. + * @param damage the amount of incoming damage. + * @return the calculated damage after absorption. + */ + private float handleDamageReduction(final @NotNull IExpeditionMember expeditionMember, final float damage) + { + final Map> armor = new HashMap<>(); + armor.computeIfAbsent(EquipmentSlot.HEAD, getArmorPiece(expeditionMember)); + armor.computeIfAbsent(EquipmentSlot.CHEST, getArmorPiece(expeditionMember)); + armor.computeIfAbsent(EquipmentSlot.LEGS, getArmorPiece(expeditionMember)); + armor.computeIfAbsent(EquipmentSlot.FEET, getArmorPiece(expeditionMember)); + + final int armorPieces = armor.size(); + if (armorPieces > 0) + { + float finalDamage = damage; + for (final Map.Entry> entry : armor.entrySet()) + { + final ArmorItem armorItem = entry.getValue().getB(); + finalDamage = CombatRules.getDamageAfterAbsorb(finalDamage, armorItem.getDefense(), armorItem.getToughness()); + } + + damageArmor(getArmor(expeditionMember), finalDamage, slot -> expeditionMember.setArmor(slot, ItemStack.EMPTY)); + return finalDamage; + } + + return damage; + } + + /** + * Get the list of armor a member is wearing. + * + * @param expeditionMember the member. + * @return the armor list. + */ + private ArmorList getArmor(final @NotNull IExpeditionMember expeditionMember) + { + final ArmorList armor = new ArmorList(); + armor.computeIfAbsent(EquipmentSlot.HEAD, getArmorPiece(expeditionMember)); + armor.computeIfAbsent(EquipmentSlot.CHEST, getArmorPiece(expeditionMember)); + armor.computeIfAbsent(EquipmentSlot.LEGS, getArmorPiece(expeditionMember)); + armor.computeIfAbsent(EquipmentSlot.FEET, getArmorPiece(expeditionMember)); + return armor; + } + + /** + * Damage all armor in an armor list. + * + * @param list the armor list. + * @param damage the amount of damage dealt. + * @param onBreak function called when an armor slot breaks. + */ + private void damageArmor(final @NotNull ArmorList list, final float damage, final Consumer onBreak) + { + final int armorPieces = list.size(); + final float dividedDamage = damage / armorPieces; + for (final Map.Entry> entry : list.entrySet()) + { + if (entry.getValue().getA().hurt(Math.round(damage / dividedDamage), random, null)) + { + onBreak.accept(entry.getKey()); + } + } + } + + /** + * Extract armor piece from a member. + * + * @param member the member instance. + * @return the lambda function to provide to the armor map. + */ + private Function> getArmorPiece(final IExpeditionMember member) + { + return (slot) -> { + final ItemStack armor = member.getArmor(slot); + if (armor.getItem() instanceof ArmorItem armorItem) + { + return new Tuple<>(armor, armorItem); + } + return null; + }; + } + + /** + * Generate loot table rewards. + * + * @param lootTableId the input loot table id. + * @param expeditionType the expedition type. + * @return the list of items. + */ + private List processLootTable(final ResourceLocation lootTableId, final ColonyExpeditionType expeditionType) + { + final LootParams lootParams = new Builder((ServerLevel) colony.getWorld()) + .withParameter(ModLootConditions.EXPEDITION_DIFFICULTY_PARAM, expeditionType.difficulty()) + .create(ModLootConditions.EXPEDITION_PARAMS); + + return colony.getWorld().getServer().getLootData().getLootTable(lootTableId).getRandomItems(lootParams); + } + + /** + * Wrapper for a combination of all armor. + */ + private static class ArmorList extends HashMap> + { + /** + * Get the total armor level. + * + * @return the armor level. + */ + public int getTotalArmor() + { + int armor = 0; + for (Tuple entry : this.values()) + { + armor += entry.getB().getDefense(); + } + return armor; + } + } +} diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/AbstractExpedition.java b/src/main/java/com/minecolonies/core/colony/expeditions/AbstractExpedition.java new file mode 100644 index 00000000000..ea7c17866ec --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/AbstractExpedition.java @@ -0,0 +1,294 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.expeditions.IExpedition; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITION_STAGE_WILDERNESS; + +/** + * Class for an expedition instance. + */ +public abstract class AbstractExpedition implements IExpedition +{ + /** + * Nbt tag constants. + */ + private static final String TAG_EQUIPMENT = "equipment"; + private static final String TAG_MEMBERS = "members"; + private static final String TAG_MEMBER_TYPE = "memberType"; + private static final String TAG_RESULTS = "results"; + + /** + * The members of the expedition. + */ + @NotNull + protected final Map> members; + + /** + * The equipment given to the expedition prior to starting. + */ + @NotNull + protected final List equipment; + + /** + * The results of this expedition. + */ + protected final Deque results; + + /** + * The current active members of the expedition. + */ + @Nullable + private List> activeMembersCache; + + /** + * Deserialization constructor. + * + * @param members the members for this expedition. + * @param equipment the list of equipment for this expedition. + * @param results the results for this expedition. + */ + protected AbstractExpedition( + final @NotNull Map> members, + final @NotNull List equipment, + final @NotNull List results) + { + this.members = Collections.unmodifiableMap(members); + this.equipment = Collections.unmodifiableList(equipment); + this.results = new ArrayDeque<>(results); + } + + /** + * Create an expedition instance from compound data. + * + * @param compound the compound data. + */ + public static T loadFromNBT(final CompoundTag compound, final ExpeditionCreator creator) + { + final List> members = readMembers(compound, TAG_MEMBERS); + + final List equipment = new ArrayList<>(); + final ListTag equipmentList = compound.getList(TAG_EQUIPMENT, Tag.TAG_COMPOUND); + for (int i = 0; i < equipmentList.size(); ++i) + { + equipment.add(ItemStack.of(equipmentList.getCompound(i))); + } + + final List results = new ArrayList<>(); + final ListTag resultsList = compound.getList(TAG_RESULTS, Tag.TAG_COMPOUND); + for (int i = 0; i < resultsList.size(); ++i) + { + results.add(ExpeditionStage.loadFromNBT(resultsList.getCompound(i))); + } + + return creator.create(members, equipment, results); + } + + /** + * Read member data from NBT. + * + * @param compound the compound data. + * @param tagName the name of the tag where the members data is stored. + * @return the list of members. + */ + public static List> readMembers(final CompoundTag compound, final String tagName) + { + final List> members = new ArrayList<>(); + final ListTag membersList = compound.getList(tagName, Tag.TAG_COMPOUND); + for (int i = 0; i < membersList.size(); ++i) + { + final CompoundTag memberCompound = membersList.getCompound(i); + final String memberType = memberCompound.getString(TAG_MEMBER_TYPE); + if (Objects.equals(memberType, "citizen")) + { + members.add(new ExpeditionCitizenMember(memberCompound)); + } + else if (Objects.equals(memberType, "visitor")) + { + members.add(new ExpeditionVisitorMember(memberCompound)); + } + } + return members; + } + + /** + * Write member data to NBT. + * + * @param compound the compound data. + * @param tagName the name of the tag where the members data is stored. + * @param members the list of members. + */ + public static void writeMembers(final CompoundTag compound, final String tagName, final Collection> members) + { + final ListTag membersCompound = new ListTag(); + for (final IExpeditionMember member : members) + { + final CompoundTag memberCompound = new CompoundTag(); + if (member instanceof ExpeditionCitizenMember) + { + memberCompound.putString(TAG_MEMBER_TYPE, "citizen"); + } + else if (member instanceof ExpeditionVisitorMember) + { + memberCompound.putString(TAG_MEMBER_TYPE, "visitor"); + } + member.write(memberCompound); + membersCompound.add(memberCompound); + } + compound.put(tagName, membersCompound); + } + + @Override + @NotNull + public List> getMembers() + { + return this.members.values().stream().toList(); + } + + @Override + @NotNull + public List getEquipment() + { + return equipment; + } + + @Override + @NotNull + public List> getActiveMembers() + { + if (activeMembersCache == null) + { + activeMembersCache = this.members.values().stream().filter(f -> !f.isDead()).toList(); + } + + return activeMembersCache; + } + + @Override + @NotNull + public List getResults() + { + return this.results.stream().toList(); + } + + @Override + public void advanceStage(final Component header) + { + cleanStages(); + + // In case 2 identical structures hit back-to-back, we want to assume its one large structure. + final ExpeditionStage stage = getCurrentStage(); + if (!stage.getHeader().equals(header)) + { + this.results.add(new ExpeditionStage(header)); + } + } + + @Override + public void rewardFound(final ItemStack itemStack) + { + getCurrentStage().addReward(itemStack); + } + + @Override + public void mobKilled(final ResourceLocation encounterId) + { + getCurrentStage().addKill(encounterId); + } + + @Override + public void memberLost(final IExpeditionMember member) + { + getCurrentStage().memberLost(member.getId()); + activeMembersCache = null; + } + + /** + * Write this expedition builder to compound data. + * + * @param compound the compound tag. + */ + @Override + public void write(final CompoundTag compound) + { + writeMembers(compound, TAG_MEMBERS, members.values()); + + final ListTag equipmentCompound = new ListTag(); + for (final ItemStack itemStack : equipment) + { + equipmentCompound.add(itemStack.serializeNBT()); + } + compound.put(TAG_EQUIPMENT, equipmentCompound); + + final ListTag resultsCompound = new ListTag(); + for (final ExpeditionStage result : results) + { + final CompoundTag resultCompound = new CompoundTag(); + result.write(resultCompound); + resultsCompound.add(resultCompound); + } + compound.put(TAG_RESULTS, resultsCompound); + } + + /** + * Clean up empty stages from the last stage back to the front. + */ + public void cleanStages() + { + if (results.isEmpty()) + { + return; + } + + final ExpeditionStage current = results.getLast(); + if (current.getRewards().isEmpty() && current.getKills().isEmpty() && current.getMembersLost().isEmpty()) + { + results.removeLast(); + cleanStages(); + } + } + + /** + * The currently active stage, or create a default one if none exists just yet. + * + * @return the current expedition stage. + */ + @NotNull + private ExpeditionStage getCurrentStage() + { + if (results.isEmpty()) + { + results.push(new ExpeditionStage(Component.translatable(EXPEDITION_STAGE_WILDERNESS))); + } + return results.getLast(); + } + + /** + * Lambda method for creating the expedition instance. + * + * @param the type of the expedition. + */ + @FunctionalInterface + public interface ExpeditionCreator + { + /** + * Callback for creating the new expedition. + * + * @param members the members for this expedition. + * @param equipment the list of equipment for this expedition. + * @param results the results for this expedition. + * @return the new expedition instance. + */ + T create(final List> members, final List equipment, final List results); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java new file mode 100644 index 00000000000..54d55a4f39e --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java @@ -0,0 +1,246 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.ICitizenData; +import com.minecolonies.api.colony.ICitizenDataView; +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_NAME; + +/** + * Citizen expedition members. + */ +public final class ExpeditionCitizenMember implements IExpeditionMember +{ + /** + * Nbt tag constants. + */ + private static final String TAG_MAX_HEALTH = "maxHealth"; + private static final String TAG_HEALTH = "health"; + private static final String TAG_ARMOR = "armor"; + private static final String TAG_ARMOR_TYPE = "type"; + private static final String TAG_ARMOR_STACK = "stack"; + private static final String TAG_WEAPON = "weapon"; + + /** + * The id of the citizen. + */ + private final int id; + + /** + * The name of the citizen. + */ + private final String name; + + /** + * The max health for this member. + */ + private final float maxHealth; + + /** + * The armor pieces this member is wearing. + */ + private final Map armor; + + /** + * The primary weapon the member is carrying. + */ + private ItemStack primaryWeapon; + + /** + * The current health for this member. + */ + private float health; + + /** + * Default constructor for deserialization. + */ + public ExpeditionCitizenMember(final CompoundTag compound) + { + this.id = compound.getInt(TAG_ID); + this.name = compound.getString(TAG_NAME); + this.maxHealth = compound.getFloat(TAG_MAX_HEALTH); + this.health = compound.getFloat(TAG_HEALTH); + this.armor = new HashMap<>(); + final ListTag armorsCompound = compound.getList(TAG_ARMOR, Tag.TAG_COMPOUND); + for (int i = 0; i < armorsCompound.size(); ++i) + { + final CompoundTag armorCompound = armorsCompound.getCompound(i); + final EquipmentSlot equipmentSlot = EquipmentSlot.byName(armorCompound.getString(TAG_ARMOR_TYPE)); + final ItemStack itemStack = ItemStack.of(armorCompound.getCompound(TAG_ARMOR_STACK)); + this.armor.put(equipmentSlot, itemStack); + } + this.primaryWeapon = ItemStack.of(compound.getCompound(TAG_WEAPON)); + } + + /** + * Default constructor. + * + * @param citizenData the citizen to create the expedition member for. + */ + public ExpeditionCitizenMember(final ICitizenData citizenData) + { + this.id = citizenData.getId(); + this.name = citizenData.getName(); + this.maxHealth = citizenData.getEntity().orElseThrow().getMaxHealth(); + this.health = this.maxHealth; + this.armor = new HashMap<>(); + this.armor.computeIfAbsent(EquipmentSlot.HEAD, citizenData.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.CHEST, citizenData.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.LEGS, citizenData.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.FEET, citizenData.getInventory()::getArmorInSlot); + this.primaryWeapon = citizenData.getInventory().getHeldItem(InteractionHand.MAIN_HAND); + } + + /** + * Default constructor. + * + * @param citizenDataView the citizen to create the expedition member for. + */ + public ExpeditionCitizenMember(final ICitizenDataView citizenDataView) + { + this.id = citizenDataView.getId(); + this.name = citizenDataView.getName(); + this.maxHealth = (float) citizenDataView.getMaxHealth(); + this.health = (float) citizenDataView.getHealth(); + this.armor = new HashMap<>(); + this.armor.computeIfAbsent(EquipmentSlot.HEAD, citizenDataView.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.CHEST, citizenDataView.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.LEGS, citizenDataView.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.FEET, citizenDataView.getInventory()::getArmorInSlot); + this.primaryWeapon = citizenDataView.getInventory().getHeldItem(InteractionHand.MAIN_HAND); + } + + @Override + public int getId() + { + return this.id; + } + + @Override + public String getName() + { + return this.name; + } + + @Override + public float getHealth() + { + return health; + } + + @Override + public void setHealth(final float health) + { + this.health = health; + } + + @Override + public float getMaxHealth() + { + return maxHealth; + } + + @Override + public boolean isDead() + { + return this.health <= 0; + } + + @Override + public ItemStack getPrimaryWeapon() + { + return primaryWeapon; + } + + @Override + public void setPrimaryWeapon(final ItemStack itemStack) + { + this.primaryWeapon = itemStack; + } + + @Override + @NotNull + public ItemStack getArmor(final EquipmentSlot slot) + { + return armor.get(slot); + } + + @Override + public void setArmor(final EquipmentSlot slot, final @NotNull ItemStack itemStack) + { + armor.put(slot, itemStack); + } + + @Override + @Nullable + public ICitizenData resolveCivilianData(final IColony colony) + { + return colony.getCitizenManager().getCivilian(this.id); + } + + @Override + public void write(final CompoundTag compound) + { + compound.putInt(TAG_ID, this.id); + compound.putString(TAG_NAME, this.name); + compound.putFloat(TAG_MAX_HEALTH, this.maxHealth); + compound.putFloat(TAG_HEALTH, this.health); + final ListTag armorsCompound = new ListTag(); + for (final Entry armorEntry : armor.entrySet()) + { + final CompoundTag armorCompound = new CompoundTag(); + armorCompound.putString(TAG_ARMOR_TYPE, armorEntry.getKey().getName()); + armorCompound.put(TAG_ARMOR_STACK, armorEntry.getValue().serializeNBT()); + armorsCompound.add(armorCompound); + } + compound.put(TAG_ARMOR, armorsCompound); + compound.put(TAG_WEAPON, primaryWeapon.save(new CompoundTag())); + } + + @Override + public void removeFromColony(final IColony colony) + { + final ICitizenData citizenData = resolveCivilianData(colony); + if (citizenData != null) + { + colony.getCitizenManager().removeCivilian(citizenData); + } + } + + @Override + public int hashCode() + { + return id; + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final ExpeditionCitizenMember that = (ExpeditionCitizenMember) o; + + return id == that.id; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java new file mode 100644 index 00000000000..54f072a4392 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java @@ -0,0 +1,255 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.expeditions.MobKill; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.IntStream; + +/** + * Implementation for an expedition stage, an expedition can contain multiple stages, each with its own rewards/unlocks. + */ +public final class ExpeditionStage +{ + /** + * NBT Tags. + */ + private static final String TAG_HEADER = "header"; + private static final String TAG_REWARDS = "rewards"; + private static final String TAG_REWARD_ITEM = "item"; + private static final String TAG_REWARD_COUNT = "count"; + private static final String TAG_KILLS = "kills"; + private static final String TAG_KILL_ENCOUNTER_ID = "encounterId"; + private static final String TAG_KILL_COUNT = "count"; + private static final String TAG_MEMBERS_LOST = "membersLost"; + + /** + * The header for this stage. + */ + private final Component header; + + /** + * The map of rewards. + */ + private final Map rewards; + + /** + * The map of kills. + */ + private final Map kills; + + /** + * The list of members that died. + */ + private final List membersLost; + + /** + * A cache for the rewards so the list doesn't have to be recalculated each time. + */ + private List cachedRewards; + + /** + * A cache for the kills so the list doesn't have to be recalculated each time. + */ + private List cachedKills; + + /** + * Default constructor. + * + * @param header the header for this stage. + */ + public ExpeditionStage(final Component header) + { + this(header, new HashMap<>(), new HashMap<>(), new ArrayList<>()); + } + + /** + * Deserialization constructor. + * + * @param header the header for this stage. + * @param rewards the map of rewards. + * @param kills the map of kills. + * @param membersLost the list of members that died. + */ + public ExpeditionStage(final Component header, final Map rewards, final Map kills, final List membersLost) + { + this.header = header; + this.rewards = new HashMap<>(rewards); + this.kills = new HashMap<>(kills); + this.membersLost = new ArrayList<>(membersLost); + } + + /** + * Create an expedition stage instance from compound data. + * + * @param compound the compound data. + * @return the expedition instance. + */ + @NotNull + public static ExpeditionStage loadFromNBT(final CompoundTag compound) + { + final Component header = Component.Serializer.fromJson(compound.getString(TAG_HEADER)); + + final Map rewards = new HashMap<>(); + final ListTag rewardsList = compound.getList(TAG_REWARDS, Tag.TAG_COMPOUND); + for (int i = 0; i < rewardsList.size(); ++i) + { + final CompoundTag rewardCompound = rewardsList.getCompound(i); + final Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(rewardCompound.getString(TAG_REWARD_ITEM))); + if (item == null) + { + continue; + } + + rewards.put(item, rewardCompound.getInt(TAG_REWARD_COUNT)); + } + + final Map kills = new HashMap<>(); + final ListTag killsList = compound.getList(TAG_KILLS, Tag.TAG_COMPOUND); + for (int i = 0; i < killsList.size(); ++i) + { + final CompoundTag killCompound = killsList.getCompound(i); + final ResourceLocation encounterId = new ResourceLocation(killCompound.getString(TAG_KILL_ENCOUNTER_ID)); + kills.put(encounterId, killCompound.getInt(TAG_KILL_COUNT)); + } + + final List membersLost = IntStream.of(compound.getIntArray(TAG_MEMBERS_LOST)).boxed().toList(); + + return new ExpeditionStage(header, rewards, kills, membersLost); + } + + /** + * Get the header for this stage. + * + * @return the component. + */ + public Component getHeader() + { + return header; + } + + /** + * A list of items obtained during this expedition stage. + * Note: Adventure tokens are mixed raw into this list. Parsing them is up to implementation to handle. + * + * @return the list of items. + */ + @NotNull + public List getRewards() + { + if (cachedRewards == null) + { + cachedRewards = rewards.entrySet().stream().map(entry -> new ItemStack(entry.getKey(), entry.getValue())).toList(); + } + return cachedRewards; + } + + /** + * Adds a reward to this stage. + * + * @param itemStack the item to add to the stage. + */ + public void addReward(final ItemStack itemStack) + { + if (itemStack.isEmpty()) + { + return; + } + + rewards.putIfAbsent(itemStack.getItem(), 0); + rewards.put(itemStack.getItem(), rewards.get(itemStack.getItem()) + itemStack.getCount()); + cachedRewards = null; + } + + /** + * Get a map of mobs killed during this expedition stage, entries contain the mob type and their amount killed. + * + * @return the list of kills. + */ + @NotNull + public List getKills() + { + if (cachedKills == null) + { + cachedKills = kills.entrySet().stream().map(entry -> new MobKill(entry.getKey(), entry.getValue())).toList(); + } + return cachedKills; + } + + /** + * Adds a kill to this stage. + * + * @param encounterId the encounter type that got killed. + */ + public void addKill(final ResourceLocation encounterId) + { + kills.putIfAbsent(encounterId, 0); + kills.put(encounterId, kills.get(encounterId) + 1); + cachedKills = null; + } + + /** + * Get a members instance of what members were lost during this stage. + * + * @return which members were lost during this part of the expedition. + */ + @NotNull + public List getMembersLost() + { + return membersLost; + } + + /** + * Adds a member that got lost during this stage. + * + * @param memberId the id of the member that was lost. + */ + public void memberLost(final int memberId) + { + membersLost.add(memberId); + } + + /** + * Write this stage to compound data. + * + * @param compound the compound tag. + */ + public void write(final CompoundTag compound) + { + compound.putString(TAG_HEADER, Component.Serializer.toJson(header)); + + final ListTag rewardsList = new ListTag(); + for (final Entry rewardEntry : rewards.entrySet()) + { + final CompoundTag rewardCompound = new CompoundTag(); + rewardCompound.putString(TAG_REWARD_ITEM, ForgeRegistries.ITEMS.getKey(rewardEntry.getKey()).toString()); + rewardCompound.putInt(TAG_REWARD_COUNT, rewardEntry.getValue()); + rewardsList.add(rewardCompound); + } + compound.put(TAG_REWARDS, rewardsList); + + final ListTag killsList = new ListTag(); + for (final Entry killEntry : kills.entrySet()) + { + final CompoundTag killCompound = new CompoundTag(); + killCompound.putString(TAG_KILL_ENCOUNTER_ID, killEntry.getKey().toString()); + killCompound.putInt(TAG_KILL_COUNT, killEntry.getValue()); + killsList.add(killCompound); + } + compound.put(TAG_KILLS, killsList); + + compound.putIntArray(TAG_MEMBERS_LOST, membersLost); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java new file mode 100644 index 00000000000..1cea09d8c73 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java @@ -0,0 +1,227 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_NAME; + +/** + * Visitor expedition members. + */ +public final class ExpeditionVisitorMember implements IExpeditionMember +{ + /** + * Nbt tag constants. + */ + private static final String TAG_MAX_HEALTH = "maxHealth"; + private static final String TAG_HEALTH = "health"; + private static final String TAG_ARMOR = "armor"; + private static final String TAG_ARMOR_TYPE = "type"; + private static final String TAG_ARMOR_STACK = "stack"; + private static final String TAG_WEAPON = "weapon"; + + /** + * The id of the citizen. + */ + private final int id; + + /** + * The name of the citizen. + */ + private final String name; + + /** + * The max health for this member. + */ + private final float maxHealth; + + /** + * The armor pieces this member is wearing. + */ + private final Map armor; + + /** + * The primary weapon the member is carrying. + */ + private ItemStack primaryWeapon; + + /** + * The current health for this member. + */ + private float health; + + /** + * Default constructor for deserialization. + */ + public ExpeditionVisitorMember(final CompoundTag compound) + { + this.id = compound.getInt(TAG_ID); + this.name = compound.getString(TAG_NAME); + this.maxHealth = compound.getFloat(TAG_MAX_HEALTH); + this.health = compound.getFloat(TAG_HEALTH); + this.armor = new HashMap<>(); + final ListTag armorsCompound = compound.getList(TAG_ARMOR, Tag.TAG_COMPOUND); + for (int i = 0; i < armorsCompound.size(); ++i) + { + final CompoundTag armorCompound = armorsCompound.getCompound(i); + final EquipmentSlot equipmentSlot = EquipmentSlot.byName(armorCompound.getString(TAG_ARMOR_TYPE)); + final ItemStack itemStack = ItemStack.of(armorCompound.getCompound(TAG_ARMOR_STACK)); + this.armor.put(equipmentSlot, itemStack); + } + this.primaryWeapon = ItemStack.of(compound.getCompound(TAG_WEAPON)); + } + + /** + * Default constructor. + * + * @param visitorData the visitor to create the expedition member for. + */ + public ExpeditionVisitorMember(final IVisitorData visitorData) + { + this.id = visitorData.getId(); + this.name = visitorData.getName(); + this.maxHealth = visitorData.getEntity().map(LivingEntity::getMaxHealth).orElseThrow(); + this.health = this.maxHealth; + this.armor = new HashMap<>(); + this.armor.computeIfAbsent(EquipmentSlot.HEAD, visitorData.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.CHEST, visitorData.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.LEGS, visitorData.getInventory()::getArmorInSlot); + this.armor.computeIfAbsent(EquipmentSlot.FEET, visitorData.getInventory()::getArmorInSlot); + this.primaryWeapon = visitorData.getInventory().getHeldItem(InteractionHand.MAIN_HAND); + } + + @Override + public int getId() + { + return this.id; + } + + @Override + public String getName() + { + return this.name; + } + + @Override + public float getHealth() + { + return health; + } + + @Override + public void setHealth(final float health) + { + this.health = health; + } + + @Override + public float getMaxHealth() + { + return maxHealth; + } + + @Override + public boolean isDead() + { + return this.health <= 0; + } + + @Override + public ItemStack getPrimaryWeapon() + { + return primaryWeapon; + } + + @Override + public void setPrimaryWeapon(final ItemStack itemStack) + { + this.primaryWeapon = itemStack; + } + + @Override + @NotNull + public ItemStack getArmor(final EquipmentSlot slot) + { + return armor.get(slot); + } + + @Override + public void setArmor(final EquipmentSlot slot, final @NotNull ItemStack itemStack) + { + armor.put(slot, itemStack); + } + + @Override + @Nullable + public IVisitorData resolveCivilianData(final IColony colony) + { + return colony.getVisitorManager().getCivilian(this.id); + } + + @Override + public void write(final CompoundTag compound) + { + compound.putInt(TAG_ID, this.id); + compound.putString(TAG_NAME, this.name); + compound.putFloat(TAG_MAX_HEALTH, this.maxHealth); + compound.putFloat(TAG_HEALTH, this.health); + final ListTag armorsCompound = new ListTag(); + for (final Entry armorEntry : armor.entrySet()) + { + final CompoundTag armorCompound = new CompoundTag(); + armorCompound.putString(TAG_ARMOR_TYPE, armorEntry.getKey().getName()); + armorCompound.put(TAG_ARMOR_STACK, armorEntry.getValue().serializeNBT()); + armorsCompound.add(armorCompound); + } + compound.put(TAG_ARMOR, armorsCompound); + compound.put(TAG_WEAPON, primaryWeapon.save(new CompoundTag())); + } + + @Override + public void removeFromColony(final IColony colony) + { + final IVisitorData visitorData = resolveCivilianData(colony); + if (visitorData != null) + { + colony.getVisitorManager().removeCivilian(visitorData); + } + } + + @Override + public int hashCode() + { + return id; + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final ExpeditionVisitorMember that = (ExpeditionVisitorMember) o; + + return id == that.id; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionBuilder.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionBuilder.java new file mode 100644 index 00000000000..ddb0a170f91 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionBuilder.java @@ -0,0 +1,86 @@ +package com.minecolonies.core.colony.expeditions.colony; + +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import com.minecolonies.api.colony.managers.interfaces.expeditions.ColonyExpedition; +import com.minecolonies.api.util.InventoryUtils; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Builder class for expedition instances. + */ +public class ColonyExpeditionBuilder +{ + /** + * The leader for the expedition. + */ + private final IExpeditionMember leader; + + /** + * The map of all members assigned. + */ + private final Map> members = new HashMap<>(); + + /** + * The list of all equipment provided on start. + */ + private final List equipment = new ArrayList<>(); + + /** + * Default constructor. + * + * @param leader the leader for the expedition. + */ + public ColonyExpeditionBuilder(final IExpeditionMember leader) + { + this.leader = leader; + this.members.put(leader.getId(), leader); + } + + /** + * Get the leader for the expedition. + * + * @return the leader. + */ + public IExpeditionMember getLeader() + { + return leader; + } + + /** + * Add a member to the builder. + * + * @param member the member instance. + */ + public void addMember(final IExpeditionMember member) + { + this.members.putIfAbsent(member.getId(), member); + } + + /** + * Add equipment to the builder. + * + * @param itemStack the item stack. + */ + public void addEquipment(final ItemStack itemStack) + { + this.equipment.add(itemStack); + } + + /** + * Build the final expedition instance. + * + * @param id the input id of the expedition. + * @param expeditionTypeId the expedition type. + * @return the full expedition instance. + */ + public ColonyExpedition build(int id, ResourceLocation expeditionTypeId) + { + return new ColonyExpedition(id, expeditionTypeId, members, InventoryUtils.processItemStackListAndMerge(equipment)); + } +} diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionEquipmentRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionEquipmentRequirement.java new file mode 100644 index 00000000000..b27e1d74c21 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionEquipmentRequirement.java @@ -0,0 +1,162 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.colony.IColonyManager; +import com.minecolonies.api.equipment.registry.EquipmentTypeEntry; +import com.minecolonies.api.util.ItemStackUtils; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Colony expedition requirements for providing any kind of {@link com.minecolonies.api.equipment.registry.EquipmentTypeEntry}. + */ +public class ColonyExpeditionEquipmentRequirement extends ColonyExpeditionRequirement +{ + /** + * The required equipment type. + */ + private final EquipmentTypeEntry equipmentType; + + /** + * The minimum amount to fulfill this requirement. + */ + private final int amount; + + /** + * Cached set of icons, because lookup is expensive. + */ + private List iconsCached; + + /** + * Default constructor. + * + * @param equipmentType the required equipment type. + * @param amount the minimum amount. + */ + public ColonyExpeditionEquipmentRequirement(final EquipmentTypeEntry equipmentType, final int amount) + { + this.equipmentType = equipmentType; + this.amount = amount; + } + + @Override + @NotNull + public ResourceLocation getId() + { + return equipmentType.getRegistryName().withPrefix("equipment"); + } + + @Override + public int getAmount() + { + return amount; + } + + @Override + public EquipmentRequirementHandler createHandler(final IItemHandler inventorySupplier) + { + return new EquipmentRequirementHandler(new RequirementHandlerOptions(inventorySupplier, (builder, stack) -> { + if (stack.getItem() instanceof ArmorItem armorItem) + { + builder.getLeader().setArmor(armorItem.getEquipmentSlot(), stack); + } + else + { + builder.addEquipment(stack); + } + })); + } + + /** + * Get the required equipment type. + * + * @return the equipment type. + */ + public EquipmentTypeEntry getEquipmentType() + { + return equipmentType; + } + + /** + * Equipment handler instance used for verifying if the given item handler contains the required equipment. + */ + public class EquipmentRequirementHandler extends RequirementHandler + { + /** + * Default constructor. + * + * @param options the options for this requirement handler. + */ + private EquipmentRequirementHandler(final RequirementHandlerOptions options) + { + super(options); + } + + @Override + public ResourceLocation getId() + { + return ColonyExpeditionEquipmentRequirement.this.getId(); + } + + @Override + public Predicate getItemPredicate() + { + return equipmentType::checkIsEquipment; + } + + @Override + public ItemStack getDefaultItemStack() + { + final List allowedEquipment = IColonyManager.getInstance().getCompatibilityManager().getListOfAllItems().stream() + .filter(equipmentType::checkIsEquipment) + .toList(); + + if (allowedEquipment.isEmpty()) + { + return ItemStack.EMPTY; + } + + ItemStack bestEquipment = allowedEquipment.get(0); + for (int i = 1; i < allowedEquipment.size(); i++) + { + final ItemStack equipment = allowedEquipment.get(i); + if (ItemStackUtils.isBetterEquipment(equipment, bestEquipment)) + { + bestEquipment = equipment; + } + } + + return bestEquipment; + } + + @Override + public Component getName() + { + return Component.translatable("com.minecolonies.core.expedition.gui.items.requirement.type.equipment", equipmentType.getDisplayName()); + } + + @Override + public List getIcon() + { + if (iconsCached == null) + { + iconsCached = + IColonyManager.getInstance().getCompatibilityManager().getListOfAllItems().stream().filter(equipmentType::checkIsEquipment).toList(); + } + + return iconsCached; + } + + @Override + public int getAmount() + { + return amount; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionFoodRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionFoodRequirement.java new file mode 100644 index 00000000000..c5a6d5cae84 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionFoodRequirement.java @@ -0,0 +1,117 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.colony.IColonyManager; +import com.minecolonies.api.util.ItemStackUtils; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionBuilder; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraftforge.items.IItemHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Colony expedition requirements for providing any kind of food, with a minimum amount. + */ +public class ColonyExpeditionFoodRequirement extends ColonyExpeditionRequirement +{ + /** + * The minimum amount to fulfill this requirement. + */ + private final int amount; + + /** + * Cached set of icons, because lookup is expensive. + */ + private List iconsCached; + + /** + * Default constructor. + * + * @param amount the minimum amount. + */ + public ColonyExpeditionFoodRequirement(final int amount) + { + this.amount = amount; + } + + @Override + @NotNull + public ResourceLocation getId() + { + return new ResourceLocation(Constants.MOD_ID, "food"); + } + + @Override + public int getAmount() + { + return amount; + } + + @Override + public RequirementHandler createHandler(final IItemHandler inventory) + { + return new FoodRequirementHandler(new RequirementHandlerOptions(inventory, ColonyExpeditionBuilder::addEquipment)); + } + + /** + * Food handler instance used for verifying if the given item handler contains any food items. + */ + public class FoodRequirementHandler extends RequirementHandler + { + /** + * Default constructor. + * + * @param options the options for this requirement handler. + */ + private FoodRequirementHandler(final RequirementHandlerOptions options) + { + super(options); + } + + @Override + public ResourceLocation getId() + { + return ColonyExpeditionFoodRequirement.this.getId(); + } + + @Override + public Predicate getItemPredicate() + { + return ItemStackUtils.ISFOOD; + } + + @Override + public ItemStack getDefaultItemStack() + { + return Items.COOKED_BEEF.getDefaultInstance(); + } + + @Override + public Component getName() + { + return Component.translatable("com.minecolonies.core.expedition.gui.items.requirement.type.food"); + } + + @Override + public List getIcon() + { + if (iconsCached == null) + { + iconsCached = IColonyManager.getInstance().getCompatibilityManager().getListOfAllItems().stream().filter(ItemStackUtils.ISFOOD).toList(); + } + + return iconsCached; + } + + @Override + public int getAmount() + { + return amount; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionItemRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionItemRequirement.java new file mode 100644 index 00000000000..fee0a7e9dc9 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionItemRequirement.java @@ -0,0 +1,145 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.util.ItemStackUtils; +import com.minecolonies.api.util.constant.Constants; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Colony expedition requirements for providing any kind of item, with a minimum amount. + */ +public class ColonyExpeditionItemRequirement extends ColonyExpeditionRequirement +{ + /** + * The item to request. + */ + private final Item item; + + /** + * The minimum amount to fulfill this requirement. + */ + private final int amount; + + /** + * Whether the stack should be consumed on the start of the expedition. + */ + private final boolean shouldConsumeOnStart; + + /** + * Default constructor. + * + * @param item the item to request. + * @param amount the minimum amount. + */ + public ColonyExpeditionItemRequirement(final Item item, final int amount) + { + this(item, amount, false); + } + + /** + * Default constructor. + * + * @param item the item to request. + * @param amount the minimum amount. + * @param shouldConsumeOnStart whether the stack should be consumed on the start of the expedition. + */ + public ColonyExpeditionItemRequirement(final Item item, final int amount, final boolean shouldConsumeOnStart) + { + this.item = item; + this.amount = amount; + this.shouldConsumeOnStart = shouldConsumeOnStart; + } + + @Override + @NotNull + public ResourceLocation getId() + { + return new ResourceLocation(Constants.MOD_ID, "item/" + item.getDescriptionId()); + } + + @Override + public int getAmount() + { + return amount; + } + + @Override + public RequirementHandler createHandler(final IItemHandler inventory) + { + return new ItemRequirementHandler(new RequirementHandlerOptions(inventory, (builder, stack) -> { + if (!shouldConsumeOnStart) + { + builder.addEquipment(stack); + } + })); + } + + /** + * Get the item to request. + * + * @return the item. + */ + public Item getItem() + { + return null; + } + + /** + * Item handler instance used for verifying if the given item handler contains the exact item. + */ + public class ItemRequirementHandler extends RequirementHandler + { + /** + * Default constructor. + * + * @param options the options for this requirement handler. + */ + private ItemRequirementHandler(final RequirementHandlerOptions options) + { + super(options); + } + + @Override + public ResourceLocation getId() + { + return ColonyExpeditionItemRequirement.this.getId(); + } + + @Override + public Predicate getItemPredicate() + { + return stack -> ItemStackUtils.compareItemStacksIgnoreStackSize(stack, item.getDefaultInstance()); + } + + @Override + public ItemStack getDefaultItemStack() + { + return item.getDefaultInstance(); + } + + @Override + public Component getName() + { + return Component.translatable("com.minecolonies.core.expedition.gui.items.requirement.type.item", item.getDescription()); + } + + @Override + public List getIcon() + { + return List.of(new ItemStack(item)); + } + + @Override + public int getAmount() + { + return amount; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionRequirement.java new file mode 100644 index 00000000000..cf01c7d6071 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionRequirement.java @@ -0,0 +1,129 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.core.client.gui.generic.ResourceItem.Resource; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.wrapper.InvWrapper; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +/** + * Interface for defining different types of colony expedition requirements. + */ +public abstract class ColonyExpeditionRequirement +{ + /** + * Container class for the different options passed to requirement handlers. + * + * @param inventory the inventory. + * @param processOnStartHandler handler function to process what to do with a given item on expedition start. + */ + public record RequirementHandlerOptions(IItemHandler inventory, BiConsumer processOnStartHandler) + { + } + + /** + * Get a unique ID for this requirement. + * + * @return the resource location. + */ + @NotNull + public abstract ResourceLocation getId(); + + /** + * Get the minimum amount to fulfill this requirement. + * + * @return the amount. + */ + public abstract int getAmount(); + + /** + * Create the handler for verifying if the given inventory satisfies this requirement. + * + * @param inventory the item handler instance. + * @return the handler instance. + */ + public abstract RequirementHandler createHandler(final IItemHandler inventory); + + /** + * Handler instance used for verifying if the given item handler satisfies the requirements. + */ + public abstract static class RequirementHandler implements Resource + { + /** + * The options for this requirement handler. + */ + private final RequirementHandlerOptions options; + + /** + * Default constructor. + * + * @param options the options for this requirement handler. + */ + protected RequirementHandler(final RequirementHandlerOptions options) + { + this.options = options; + } + + /** + * Get a unique ID for this requirement handler, must match the one provided by the {@link ColonyExpeditionRequirement}. + * + * @return the resource location. + */ + public abstract ResourceLocation getId(); + + /** + * Get the predicate used to filter out items from the inventory. + * + * @return the predicate function. + */ + public abstract Predicate getItemPredicate(); + + /** + * Get the default item stack to provide the visitor with in case they are getting items creatively inserted. + */ + public abstract ItemStack getDefaultItemStack(); + + @Override + public final int getAmountAvailable() + { + return InventoryUtils.getItemCountInItemHandler(options.inventory, getItemPredicate()); + } + + @Override + public final int getAmountPlayer() + { + return InventoryUtils.getItemCountInItemHandler(getPlayerInventory(), getItemPredicate()); + } + + @Override + public final int getAmountInDelivery() + { + return 0; + } + + /** + * Get the player inventory item handler. + * + * @return the item handler instance. + */ + private IItemHandler getPlayerInventory() + { + return new InvWrapper(Minecraft.getInstance().player.getInventory()); + } + + /** + * Execute a function checking what should happen to the given item + */ + public void processOnStart(final ColonyExpeditionBuilder builder, final ItemStack itemStack) + { + options.processOnStartHandler.accept(builder, itemStack); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionType.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionType.java new file mode 100644 index 00000000000..5762564a630 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionType.java @@ -0,0 +1,25 @@ +package com.minecolonies.core.colony.expeditions.colony.types; + +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; + +import java.util.List; + +/** + * JSON based class for defining colony expedition types. + * + * @param id The id of the expedition. + * @param name The name of the expedition, may be a translation string or a fixed text. + * @param toText The "to text" of the expedition, used as part of the interaction inquiry to give a real quick indication of what to expect from the expedition. + * @param difficulty The difficulty of the expedition. + * @param dimension The target dimension this expedition would go to. + * @param lootTable The loot table to use for rewards generation. + * @param requirements The list of requirements for this expedition type to be sent. + * @param guards The minimum amount of guards needed for this expedition. + */ +public record ColonyExpeditionType(ResourceLocation id, Component name, Component toText, ColonyExpeditionTypeDifficulty difficulty, ResourceKey dimension, + ResourceLocation lootTable, List requirements, int guards) +{} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeBuilder.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeBuilder.java new file mode 100644 index 00000000000..b227da36e7e --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeBuilder.java @@ -0,0 +1,271 @@ +package com.minecolonies.core.colony.expeditions.colony.types; + +import com.minecolonies.api.equipment.registry.EquipmentTypeEntry; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionEquipmentRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionFoodRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionItemRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.Level; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Builder class for defining colony expedition types. + */ +public class ColonyExpeditionTypeBuilder +{ + /** + * The id of the expedition. + */ + private final ResourceLocation id; + + /** + * The target dimension this expedition would go to. + */ + private final ResourceKey dimension; + + /** + * The loot table to use for rewards generation. + */ + private final ResourceLocation lootTable; + + /** + * The list of requirements for this expedition type to be sent. + */ + private final List requirements = new ArrayList<>(); + + /** + * The name of the expedition, may be a translation string or a fixed text. + */ + private String name; + + /** + * The "to text" of the expedition, used as part of the interaction inquiry to give a real quick indication of what to expect from the expedition. + */ + private String toText; + + /** + * The difficulty of the expedition. + */ + private ColonyExpeditionTypeDifficulty difficulty = ColonyExpeditionTypeDifficulty.EASY; + + /** + * The minimum amount of guards needed for this expedition. + */ + private int guards = 1; + + /** + * Create a new expedition type builder. + * + * @param id the id for this expedition type. + * @param dimension the target dimension. + * @param lootTable the loot table to generate rewards with. + */ + public ColonyExpeditionTypeBuilder(final ResourceLocation id, final ResourceKey dimension, final ResourceLocation lootTable) + { + this.id = id; + this.dimension = dimension; + this.lootTable = lootTable; + this.name = "Expedition in the " + StringUtils.capitalize(dimension.location().getPath()); + this.toText = "the " + StringUtils.capitalize(dimension.location().getPath()); + } + + /** + * Adds an equipment requirement to this expedition type builder, with an amount of 1. + * + * @param equipmentType the equipment type. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder addEquipmentRequirement(final EquipmentTypeEntry equipmentType) + { + return addEquipmentRequirement(equipmentType, 1); + } + + /** + * Adds an equipment requirement to this expedition type builder. + * + * @param equipmentType the equipment type. + * @param amount the amount needed. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder addEquipmentRequirement(final EquipmentTypeEntry equipmentType, final int amount) + { + this.requirements.add(new ColonyExpeditionEquipmentRequirement(equipmentType, amount)); + return this; + } + + /** + * Adds a food requirement to this expedition type builder. + * + * @param amount the amount needed. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder addFoodRequirement(final int amount) + { + this.requirements.add(new ColonyExpeditionFoodRequirement(amount)); + return this; + } + + /** + * Adds an item requirement to this expedition type builder, with an amount of 1. + * + * @param item the item. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder addItemRequirement(final ItemLike item) + { + return addItemRequirement(item, 1); + } + + /** + * Adds an item requirement to this expedition type builder. + * + * @param item the item. + * @param amount the amount needed. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder addItemRequirement(final ItemLike item, final int amount) + { + this.requirements.add(new ColonyExpeditionItemRequirement(item.asItem(), amount)); + return this; + } + + /** + * Get the id of the expedition. + * + * @return the resource id. + */ + public ResourceLocation getId() + { + return id; + } + + /** + * The target dimension this expedition would go to. + * + * @return the level key. + */ + public ResourceKey getDimension() + { + return dimension; + } + + /** + * Get the loot table to use for rewards generation. + * + * @return the resource id. + */ + public ResourceLocation getLootTable() + { + return lootTable; + } + + /** + * Get the name of the expedition, may be a translation string or a fixed text. + * + * @return the text. + */ + public String getName() + { + return name; + } + + /** + * Set the name of the expedition, may be a translation string or a fixed text. + * Defaults to "Expedition in the (capitalized dimension name)". + * + * @param name the new name. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder setName(final String name) + { + this.name = name; + return this; + } + + /** + * Get the "to text" of the expedition, used as part of the interaction inquiry to give a real quick indication of what to expect from the expedition. + * + * @return the text. + */ + public String getToText() + { + return toText; + } + + /** + * Set the "to text" of the expedition, used as part of the interaction inquiry to give a real quick indication of what to expect from the expedition. + * Hence, this could be read as "go to ...". + *

+ * Defaults to "the (capitalized dimension name)". + * + * @param toText the new "to text". + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder setToText(final String toText) + { + this.toText = toText; + return this; + } + + /** + * Get the difficulty of the expedition. + * + * @return the difficulty. + */ + public ColonyExpeditionTypeDifficulty getDifficulty() + { + return difficulty; + } + + /** + * Set the difficulty for this expedition. + * Defaults to EASY. + * + * @param difficulty the new difficulty. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder setDifficulty(final ColonyExpeditionTypeDifficulty difficulty) + { + this.difficulty = difficulty; + return this; + } + + /** + * Get the list of requirements for this expedition type to be sent. + * + * @return the list of requirements. + */ + public List getRequirements() + { + return requirements; + } + + /** + * Get the minimum amount of guards needed for this expedition. + * + * @return the minimum guard count. + */ + public int getGuards() + { + return guards; + } + + /** + * Set the amount of guards required for this expedition. + * Defaults to 1. + * + * @param guards the new amount of guards. + * @return the builder for chaining. + */ + public ColonyExpeditionTypeBuilder setGuards(final int guards) + { + this.guards = guards; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeDifficulty.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeDifficulty.java new file mode 100644 index 00000000000..293a51ebd7e --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeDifficulty.java @@ -0,0 +1,197 @@ +package com.minecolonies.core.colony.expeditions.colony.types; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Style; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import org.jetbrains.annotations.Nullable; + +/** + * The expedition difficulty. + */ +public enum ColonyExpeditionTypeDifficulty +{ + EASY("easy", 1, Items.IRON_SWORD, false, Style.EMPTY, 30, 5, 1, 1f), + MEDIUM("medium", 2, Items.IRON_SWORD, false, Style.EMPTY, 45, 10, 2, 1.2f), + HARD("hard", 3, Items.IRON_SWORD, false, Style.EMPTY, 60, 15, 3, 1.5f), + NIGHTMARE("nightmare", 4, Items.NETHERITE_SWORD, true, Style.EMPTY.withColor(ChatFormatting.DARK_RED).withItalic(true), 120, 30, 4, 2f); + + /** + * The key of the difficulty, used in the json files. + */ + private final String key; + + /** + * The level of the difficulty. + */ + private final int level; + + /** + * The sword item which should be rendered for the difficulty icons. + */ + private final Item icon; + + /** + * Whether the icon should by default be hidden, only shown if the difficulty is selected. + */ + private final boolean hidden; + + /** + * The style for the hover pane to display. + */ + private final Style style; + + /** + * The base amount of ticks the expedition will take. + */ + private final int baseTime; + + /** + * The amount of random time that can be added/removed from the base time. + */ + private final int randomTime; + + /** + * A multiplier that will spawn more mobs during encounters. + */ + private final int mobEncounterMultiplier; + + /** + * A multiplier that will increase the damage for mobs during encounters. + */ + private final float mobDamageMultiplier; + + /** + * Internal constructor. + */ + ColonyExpeditionTypeDifficulty( + final String key, + final int level, + final Item icon, + final boolean hidden, + final Style style, + final int baseTime, + final int randomTime, + final int mobEncounterMultiplier, + final float mobDamageMultiplier) + { + this.key = key; + this.level = level; + this.icon = icon; + this.hidden = hidden; + this.style = style; + this.baseTime = baseTime; + this.randomTime = randomTime; + this.mobEncounterMultiplier = mobEncounterMultiplier; + this.mobDamageMultiplier = mobDamageMultiplier; + } + + /** + * Get the difficulty from its key value. + * + * @param key the input key. + * @return the difficulty, or none if the key is incorrect. + */ + @Nullable + public static ColonyExpeditionTypeDifficulty fromKey(final String key) + { + for (final ColonyExpeditionTypeDifficulty item : ColonyExpeditionTypeDifficulty.values()) + { + if (item.getKey().equals(key)) + { + return item; + } + } + return null; + } + + /** + * Get the key for this difficulty instance. + * + * @return the key for the difficulty. + */ + public String getKey() + { + return key; + } + + /** + * Get the level of the expedition. + * + * @return the level. + */ + public int getLevel() + { + return level; + } + + /** + * Get the icon of the expedition difficulty. + * + * @return the item. + */ + public Item getIcon() + { + return icon; + } + + /** + * Whether this difficulty is hidden. + * + * @return true if so. + */ + public boolean isHidden() + { + return hidden; + } + + /** + * Get the style for the hover pane to display. + * + * @return the custom style. + */ + public Style getStyle() + { + return style; + } + + /** + * Get the base amount of ticks the expedition will take. + * + * @return the amount of time. + */ + public int getBaseTime() + { + return baseTime; + } + + /** + * Get the amount of random time that can be added/removed from the base time. + * + * @return the amount of time. + */ + public int getRandomTime() + { + return randomTime; + } + + /** + * Get a multiplier that will spawn more mobs during encounters. + * + * @return the multiplier. + */ + public int getMobEncounterMultiplier() + { + return mobEncounterMultiplier; + } + + /** + * Get a multiplier that will increase the damage for mobs during encounters. + * + * @return the multiplier. + */ + public float getMobDamageMultiplier() + { + return mobDamageMultiplier; + } +} diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeParser.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeParser.java new file mode 100644 index 00000000000..68a60f5ec00 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/types/ColonyExpeditionTypeParser.java @@ -0,0 +1,270 @@ +package com.minecolonies.core.colony.expeditions.colony.types; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.minecolonies.api.IMinecoloniesAPI; +import com.minecolonies.api.equipment.ModEquipmentTypes; +import com.minecolonies.api.equipment.registry.EquipmentTypeEntry; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionEquipmentRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionFoodRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionItemRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.Level; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Parser for converting expedition types to and from json + */ +public class ColonyExpeditionTypeParser +{ + /** + * The json property keys. + */ + private static final String PROP_NAME = "name"; + private static final String PROP_TO_TEXT = "to-text"; + private static final String PROP_DIFFICULTY = "difficulty"; + private static final String PROP_DIMENSION = "dimension"; + private static final String PROP_LOOT_TABLE = "loot-table"; + private static final String PROP_REQUIREMENTS = "requirements"; + private static final String PROP_REQUIREMENT_TYPE = "type"; + private static final String PROP_REQUIREMENT_AMOUNT = "amount"; + private static final String PROP_REQUIREMENT_EQUIPMENT_KEY = "equipment"; + private static final String PROP_REQUIREMENT_ITEM_KEY = "item"; + private static final String PROP_GUARDS = "guards"; + + /** + * Requirement types + */ + private static final String REQUIREMENT_TYPE_EQUIPMENT = "equipment"; + private static final String REQUIREMENT_TYPE_FOOD = "food"; + private static final String REQUIREMENT_TYPE_ITEM = "item"; + + /** + * Hidden constructor. + */ + private ColonyExpeditionTypeParser() {} + + /** + * Attempt to parse a colony expedition type instance from a json object. + * + * @param id the id of the expedition type. + * @param object the input json object. + * @return the colony expedition type instance, or null. + * @throws JsonParseException when a fault is found during parsing the json. + */ + @NotNull + public static ColonyExpeditionType parse(final ResourceLocation id, final JsonObject object) throws JsonParseException + { + final Component name = Component.translatable(object.getAsJsonPrimitive(PROP_NAME).getAsString()); + final Component toText = Component.translatable(object.getAsJsonPrimitive(PROP_TO_TEXT).getAsString()); + final ColonyExpeditionTypeDifficulty difficulty = ColonyExpeditionTypeDifficulty.fromKey(object.getAsJsonPrimitive(PROP_DIFFICULTY).getAsString()); + final ResourceKey dimension = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(object.getAsJsonPrimitive(PROP_DIMENSION).getAsString())); + final ResourceLocation lootTable = new ResourceLocation(object.getAsJsonPrimitive(PROP_LOOT_TABLE).getAsString()); + + final Set requirements = new HashSet<>(); + if (object.has(PROP_REQUIREMENTS) && object.get(PROP_REQUIREMENTS).isJsonArray()) + { + final JsonArray jsonRequirements = object.getAsJsonArray(PROP_REQUIREMENTS); + for (final JsonElement jsonRequirement : jsonRequirements) + { + if (!jsonRequirement.isJsonObject()) + { + continue; + } + + final ColonyExpeditionRequirement requirement = parseRequirement(jsonRequirement.getAsJsonObject()); + if (requirement != null) + { + requirements.add(requirement); + } + } + } + + final int guards = object.has(PROP_GUARDS) ? object.getAsJsonPrimitive(PROP_GUARDS).getAsInt() : 1; + + if (difficulty == null) + { + throw new JsonParseException(String.format("Provided difficulty does not exist, must be one of: [%s]", + Arrays.stream(ColonyExpeditionTypeDifficulty.values()).map(ColonyExpeditionTypeDifficulty::getKey).collect(Collectors.joining(", ")))); + } + + return new ColonyExpeditionType(id, name, toText, difficulty, dimension, lootTable, requirements.stream().toList(), guards); + } + + /** + * Parse an individual requirement from a json object. + * + * @param requirement the input json object. + * @return a requirement instance or null. + */ + @Nullable + private static ColonyExpeditionRequirement parseRequirement(final JsonObject requirement) + { + final int amount = Math.max(requirement.has(PROP_REQUIREMENT_AMOUNT) ? requirement.getAsJsonPrimitive(PROP_REQUIREMENT_AMOUNT).getAsInt() : 1, 1); + return switch (requirement.get(PROP_REQUIREMENT_TYPE).getAsString()) + { + case REQUIREMENT_TYPE_EQUIPMENT -> + { + final ResourceLocation equipmentTypeId = new ResourceLocation(requirement.getAsJsonPrimitive(PROP_REQUIREMENT_EQUIPMENT_KEY).getAsString()); + final EquipmentTypeEntry equipmentTypeEntry = IMinecoloniesAPI.getInstance().getEquipmentTypeRegistry().getValue(equipmentTypeId); + if (equipmentTypeEntry == null) + { + yield null; + } + yield new ColonyExpeditionEquipmentRequirement(equipmentTypeEntry, amount); + } + case REQUIREMENT_TYPE_FOOD -> new ColonyExpeditionFoodRequirement(amount); + case REQUIREMENT_TYPE_ITEM -> + { + final ResourceLocation itemId = new ResourceLocation(requirement.getAsJsonPrimitive(PROP_REQUIREMENT_ITEM_KEY).getAsString()); + final Item item = ForgeRegistries.ITEMS.getValue(itemId); + if (item == null) + { + yield null; + } + yield new ColonyExpeditionItemRequirement(item, amount); + } + default -> null; + }; + } + + /** + * Turns an expedition type instance into JSON format. + * + * @param expeditionTypeBuilder the expedition type builder instance. + * @return the json object. + */ + public static JsonObject toJson(final ColonyExpeditionTypeBuilder expeditionTypeBuilder) + { + final JsonObject object = new JsonObject(); + object.addProperty(PROP_NAME, expeditionTypeBuilder.getName()); + object.addProperty(PROP_TO_TEXT, expeditionTypeBuilder.getToText()); + object.addProperty(PROP_DIFFICULTY, expeditionTypeBuilder.getDifficulty().getKey()); + object.addProperty(PROP_DIMENSION, expeditionTypeBuilder.getDimension().location().toString()); + object.addProperty(PROP_LOOT_TABLE, expeditionTypeBuilder.getLootTable().toString()); + final JsonArray requirements = new JsonArray(); + for (final ColonyExpeditionRequirement requirement : expeditionTypeBuilder.getRequirements()) + { + final JsonObject requirementObject = new JsonObject(); + requirementObject.addProperty(PROP_REQUIREMENT_AMOUNT, requirement.getAmount()); + + if (requirement instanceof ColonyExpeditionEquipmentRequirement toolRequirement) + { + requirementObject.addProperty(PROP_REQUIREMENT_TYPE, REQUIREMENT_TYPE_EQUIPMENT); + requirementObject.addProperty(PROP_REQUIREMENT_EQUIPMENT_KEY, toolRequirement.getEquipmentType().getRegistryName().toString()); + } + else if (requirement instanceof ColonyExpeditionFoodRequirement) + { + requirementObject.addProperty(PROP_REQUIREMENT_TYPE, REQUIREMENT_TYPE_FOOD); + } + else if (requirement instanceof ColonyExpeditionItemRequirement itemRequirement) + { + requirementObject.addProperty(PROP_REQUIREMENT_TYPE, REQUIREMENT_TYPE_ITEM); + requirementObject.addProperty(PROP_REQUIREMENT_ITEM_KEY, itemRequirement.getItem().toString()); + } + else + { + continue; + } + + requirements.add(requirementObject); + } + object.add(PROP_REQUIREMENTS, requirements); + object.addProperty(PROP_GUARDS, expeditionTypeBuilder.getGuards()); + return object; + } + + /** + * Turns an expedition type instance into NBT format. + * + * @param expeditionType the expedition type instance. + * @param buf the buf to write into. + */ + public static void toBuffer(final ColonyExpeditionType expeditionType, final FriendlyByteBuf buf) + { + buf.writeResourceLocation(expeditionType.id()); + buf.writeComponent(expeditionType.name()); + buf.writeComponent(expeditionType.toText()); + buf.writeEnum(expeditionType.difficulty()); + buf.writeResourceKey(expeditionType.dimension()); + buf.writeResourceLocation(expeditionType.lootTable()); + buf.writeInt(expeditionType.requirements().size()); + for (final ColonyExpeditionRequirement requirement : expeditionType.requirements()) + { + if (requirement instanceof ColonyExpeditionEquipmentRequirement toolRequirement) + { + buf.writeUtf(REQUIREMENT_TYPE_EQUIPMENT); + buf.writeInt(requirement.getAmount()); + buf.writeResourceLocation(toolRequirement.getEquipmentType().getRegistryName()); + } + else if (requirement instanceof ColonyExpeditionFoodRequirement) + { + buf.writeUtf(REQUIREMENT_TYPE_FOOD); + buf.writeInt(requirement.getAmount()); + } + else if (requirement instanceof ColonyExpeditionItemRequirement itemRequirement) + { + buf.writeUtf(REQUIREMENT_TYPE_ITEM); + buf.writeInt(requirement.getAmount()); + buf.writeResourceLocation(BuiltInRegistries.ITEM.getKey(itemRequirement.getItem())); + } + } + buf.writeInt(expeditionType.guards()); + } + + /** + * Attempt to parse a colony expedition type instance from a network buffer. + * + * @param buf the network buffer. + * @return the colony expedition type instance, or null. + */ + public static ColonyExpeditionType fromBuffer(final FriendlyByteBuf buf) + { + final ResourceLocation id = buf.readResourceLocation(); + final Component name = buf.readComponent(); + final Component toText = buf.readComponent(); + final ColonyExpeditionTypeDifficulty difficulty = buf.readEnum(ColonyExpeditionTypeDifficulty.class); + final ResourceKey dimension = buf.readResourceKey(Registries.DIMENSION); + final ResourceLocation lootTable = buf.readResourceLocation(); + + final List requirements = new ArrayList<>(); + final int requirementCount = buf.readInt(); + for (int i = 0; i < requirementCount; i++) + { + final String requirementType = buf.readUtf(); + final int amount = buf.readInt(); + switch (requirementType) + { + case REQUIREMENT_TYPE_EQUIPMENT: + final EquipmentTypeEntry equipment = ModEquipmentTypes.getRegistry().getValue(buf.readResourceLocation()); + requirements.add(new ColonyExpeditionEquipmentRequirement(equipment, amount)); + break; + case REQUIREMENT_TYPE_FOOD: + requirements.add(new ColonyExpeditionFoodRequirement(amount)); + break; + case REQUIREMENT_TYPE_ITEM: + final Item item = BuiltInRegistries.ITEM.get(buf.readResourceLocation()); + requirements.add(new ColonyExpeditionItemRequirement(item, amount)); + break; + } + } + + final int guards = buf.readInt(); + + return new ColonyExpeditionType(id, name, toText, difficulty, dimension, lootTable, requirements, guards); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/encounters/ExpeditionEncounter.java b/src/main/java/com/minecolonies/core/colony/expeditions/encounters/ExpeditionEncounter.java new file mode 100644 index 00000000000..c6a98e6cffa --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/encounters/ExpeditionEncounter.java @@ -0,0 +1,31 @@ +package com.minecolonies.core.colony.expeditions.encounters; + +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; + +/** + * Encounters are mob encounters that can be found on expeditions. + * + * @param id The id of the encounter. + * @param entityType The entity type of the encounter. + * @param damage The damage this encounter will deal. + * @param reflectingDamage The damage this encounter will reflect upon itself after dealing damage. + * @param health The health this encounter has. + * @param armor The armor level this encounter has. + * @param lootTable The loot table killing this encounter will give. + * @param xp The experience killing this encounter will give. + */ +public record ExpeditionEncounter(ResourceLocation id, EntityType entityType, float damage, float reflectingDamage, double health, int armor, ResourceLocation lootTable, + double xp) +{ + /** + * Get the name of the encounter. + * + * @return the display name. + */ + public Component getName() + { + return entityType.getDescription(); + } +} diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/encounters/ExpeditionEncounterParser.java b/src/main/java/com/minecolonies/core/colony/expeditions/encounters/ExpeditionEncounterParser.java new file mode 100644 index 00000000000..278f32df78e --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/encounters/ExpeditionEncounterParser.java @@ -0,0 +1,110 @@ +package com.minecolonies.core.colony.expeditions.encounters; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.NotNull; + +/** + * Parser for encounters found on expeditions. + */ +public class ExpeditionEncounterParser +{ + /** + * The json property keys. + */ + private static final String PROP_ENTITY_TYPE = "entity-type"; + private static final String PROP_DAMAGE = "damage"; + private static final String PROP_REFLECTING_DAMAGE = "reflect"; + private static final String PROP_HEALTH = "health"; + private static final String PROP_ARMOR = "armor"; + private static final String PROP_LOOT_TABLE = "loot-table"; + private static final String PROP_XP = "xp"; + + /** + * Hidden constructor. + */ + private ExpeditionEncounterParser() {} + + /** + * Attempt to parse a colony expedition encounter instance from a json object. + * + * @param id the id of the encounter. + * @param object the input json object. + * @return the colony expedition type instance, or null. + * @throws JsonParseException when a fault is found during parsing the json. + */ + @NotNull + public static ExpeditionEncounter parse(final ResourceLocation id, final JsonObject object) throws JsonParseException + { + final EntityType entityType = EntityType.byString(object.getAsJsonPrimitive(PROP_ENTITY_TYPE).getAsString()) + .orElseThrow(() -> new JsonParseException(String.format("Provided entity type '%s' does not exist.", + object.getAsJsonPrimitive(PROP_ENTITY_TYPE).getAsString()))); + final float damage = object.getAsJsonPrimitive(PROP_DAMAGE).getAsFloat(); + final float reflectingDamage = object.has(PROP_REFLECTING_DAMAGE) ? object.getAsJsonPrimitive(PROP_REFLECTING_DAMAGE).getAsFloat() : 0; + final double health = object.getAsJsonPrimitive(PROP_HEALTH).getAsDouble(); + final int armor = object.getAsJsonPrimitive(PROP_ARMOR).getAsInt(); + final ResourceLocation lootTable = new ResourceLocation(object.getAsJsonPrimitive(PROP_LOOT_TABLE).getAsString()); + final double xp = object.getAsJsonPrimitive(PROP_XP).getAsDouble(); + return new ExpeditionEncounter(id, entityType, damage, reflectingDamage, health, armor, lootTable, xp); + } + + /** + * Turns an expedition encounter instance into JSON format. + * + * @param encounter the expedition encounter instance. + * @return the json object. + */ + public static JsonObject toJson(final ExpeditionEncounter encounter) + { + final JsonObject object = new JsonObject(); + object.addProperty(PROP_ENTITY_TYPE, EntityType.getKey(encounter.entityType()).toString()); + object.addProperty(PROP_DAMAGE, encounter.damage()); + object.addProperty(PROP_REFLECTING_DAMAGE, encounter.reflectingDamage()); + object.addProperty(PROP_HEALTH, encounter.health()); + object.addProperty(PROP_ARMOR, encounter.armor()); + object.addProperty(PROP_LOOT_TABLE, encounter.lootTable().toString()); + object.addProperty(PROP_XP, encounter.xp()); + return object; + } + + /** + * Turns an expedition encounter instance into NBT format. + * + * @param encounter the expedition encounter instance. + * @param buf the buf to write into. + */ + public static void toBuffer(final ExpeditionEncounter encounter, final FriendlyByteBuf buf) + { + buf.writeResourceLocation(encounter.id()); + buf.writeRegistryId(ForgeRegistries.ENTITY_TYPES, encounter.entityType()); + buf.writeFloat(encounter.damage()); + buf.writeFloat(encounter.reflectingDamage()); + buf.writeDouble(encounter.health()); + buf.writeInt(encounter.armor()); + buf.writeResourceLocation(encounter.lootTable()); + buf.writeDouble(encounter.xp()); + } + + /** + * Attempt to parse an expedition encounter instance from a network buffer. + * + * @param buf the network buffer. + * @return the expedition encounter instance, or null. + */ + public static ExpeditionEncounter fromBuffer(final FriendlyByteBuf buf) + { + final ResourceLocation id = buf.readResourceLocation(); + final EntityType entityType = buf.readRegistryIdSafe(EntityType.class); + final float damage = buf.readFloat(); + final float reflectingDamage = buf.readFloat(); + final double health = buf.readDouble(); + final int armor = buf.readInt(); + final ResourceLocation lootTable = buf.readResourceLocation(); + final double xp = buf.readDouble(); + return new ExpeditionEncounter(id, entityType, damage, reflectingDamage, health, armor, lootTable, xp); + } +} diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionInteraction.java new file mode 100644 index 00000000000..03cfe535262 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionInteraction.java @@ -0,0 +1,366 @@ +package com.minecolonies.core.colony.interactionhandling; + +import com.ldtteam.blockui.views.BOWindow; +import com.minecolonies.api.colony.*; +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import com.minecolonies.api.colony.interactionhandling.ChatPriority; +import com.minecolonies.api.colony.interactionhandling.IInteractionResponseHandler; +import com.minecolonies.api.colony.interactionhandling.ModInteractionResponseHandlers; +import com.minecolonies.api.colony.managers.interfaces.expeditions.ColonyExpedition; +import com.minecolonies.api.colony.managers.interfaces.expeditions.CreatedExpedition; +import com.minecolonies.api.items.AbstractItemExpeditionSheet; +import com.minecolonies.api.items.AbstractItemExpeditionSheet.ExpeditionSheetInfo; +import com.minecolonies.api.items.ModItems; +import com.minecolonies.api.util.BlockPosUtil; +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.Log; +import com.minecolonies.api.util.MessageUtils; +import com.minecolonies.api.util.MessageUtils.MessagePriority; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.Network; +import com.minecolonies.core.colony.events.ColonyExpeditionEvent; +import com.minecolonies.core.colony.expeditions.ExpeditionCitizenMember; +import com.minecolonies.core.colony.expeditions.ExpeditionVisitorMember; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionBuilder; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement.RequirementHandler; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DespawnTimeData.DespawnTime; +import com.minecolonies.core.items.ItemExpeditionSheet.ExpeditionSheetContainerManager; +import com.minecolonies.core.network.messages.server.colony.InteractionResponse; +import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity.RemovalReason; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.wrapper.InvWrapper; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.function.Function; + +import static com.minecolonies.api.util.constant.Constants.TICKS_HOUR; +import static com.minecolonies.api.util.constant.ExpeditionConstants.*; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DEFAULT_DESPAWN_TIME; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_DESPAWN_TIME; + +/** + * The interaction for expeditionary visitors. + *

+ * This interaction has several states it can be in. + *

    + *
  • Accept phase: Option to accept or cancel the expedition.
  • + *
  • Prepare phase: Option to finish, ask for the guide or cancel the expedition.
  • + *
  • Finished phase: Option to view the loot, or ignore.
  • + *
+ */ +public class ExpeditionInteraction extends ServerCitizenInteraction +{ + /** + * All possible questions. + */ + private static final Function acceptInquiry = (to) -> Component.translatable(EXPEDITION_INTERACTION_INQUIRY_ACCEPT, to); + private static final Component prepareInquiry = Component.translatable(EXPEDITION_INTERACTION_INQUIRY_PREPARE); + private static final Component finishedInquiry = Component.translatable(EXPEDITION_INTERACTION_INQUIRY_FINISHED); + + /** + * All possible answer fields. + */ + private static final Component acceptOkAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_CREATED_ACCEPT); + private static final Component acceptCancelAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_CREATED_NOT_INTERESTED); + private static final Component prepareFinishAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_START); + private static final Component prepareGetSheetAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_GET_SHEET); + private static final Component prepareLaterAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_NOT_NOW); + private static final Component prepareCancelAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_ACCEPTED_NOT_INTERESTED); + private static final Component finishedViewAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_FINISHED_VIEW_RESULTS); + private static final Component finishedLaterAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_FINISHED_NOT_NOW); + private static final Component finishedCancelAnswer = Component.translatable(EXPEDITION_INTERACTION_RESPONSE_FINISHED_NOT_INTERESTED); + + /** + * Predicates + */ + private static final BiPredicate IS_FINISHED_EXPEDITION_SHEET = (stack, colony) -> { + if (stack.getItem() instanceof AbstractItemExpeditionSheet expeditionSheet) + { + final ExpeditionSheetInfo expeditionSheetInfo = expeditionSheet.getExpeditionSheetInfo(stack); + if (expeditionSheetInfo == null) + { + return false; + } + + if (colony.getID() != expeditionSheetInfo.colonyId()) + { + return false; + } + + final CreatedExpedition createdExpedition = colony.getExpeditionManager().getCreatedExpedition(expeditionSheetInfo.expeditionId()); + if (createdExpedition == null) + { + return false; + } + + return colony.getExpeditionManager().meetsRequirements(createdExpedition.expeditionTypeId(), new ExpeditionSheetContainerManager(stack)); + } + + return false; + }; + + /** + * Default constructor. + */ + public ExpeditionInteraction() + { + super(Component.empty(), + true, + ChatPriority.IMPORTANT, + null, + Component.translatable(EXPEDITION_INTERACTION_VALIDATOR_ID)); + this.loadValidator(); + } + + /** + * Initializer constructor. + * + * @param data the input citizen data. + */ + public ExpeditionInteraction(final ICitizen data) + { + super(data); + } + + @Override + public Component getInquiry(final Player player, final ICitizen data) + { + final ExpeditionStatus currentState = data.getColony().getExpeditionManager().getExpeditionStatus(data.getId()); + return switch (currentState) + { + case CREATED -> + { + final ResourceLocation expeditionTypeId = data.getColony().getExpeditionManager().getCreatedExpedition(data.getId()).expeditionTypeId(); + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getExpeditionType(expeditionTypeId); + if (expeditionType != null) + { + yield acceptInquiry.apply(expeditionType.toText()); + } + else + { + yield Component.empty(); + } + } + case ACCEPTED -> prepareInquiry; + case FINISHED -> finishedInquiry; + default -> Component.empty(); + }; + } + + @Override + public List genChildInteractions() + { + return Collections.emptyList(); + } + + @Override + public String getType() + { + return ModInteractionResponseHandlers.EXPEDITION.getPath(); + } + + @Override + public ResourceLocation getInteractionIcon() + { + return new ResourceLocation(Constants.MOD_ID, EXPEDITION_INTERACTION_ICON); + } + + @Override + public List getPossibleResponses(final ICitizen data) + { + final ExpeditionStatus currentState = data.getColony().getExpeditionManager().getExpeditionStatus(data.getId()); + return switch (currentState) + { + case CREATED -> List.of(acceptOkAnswer, acceptCancelAnswer); + case ACCEPTED -> List.of(prepareFinishAnswer, prepareGetSheetAnswer, prepareLaterAnswer, prepareCancelAnswer); + case FINISHED -> List.of(finishedViewAnswer, finishedLaterAnswer, finishedCancelAnswer); + default -> List.of(); + }; + } + + @Override + public void onServerResponseTriggered(final int responseId, final Player player, final ICitizenData data) + { + if (data instanceof IVisitorData visitorData) + { + handleResponse(responseId, player, visitorData); + } + } + + @Override + public boolean onClientResponseTriggered(final int responseId, final Player player, final ICitizenDataView data, final BOWindow window) + { + Network.getNetwork().sendToServer(new InteractionResponse(data.getColonyId(), data.getId(), player.level.dimension(), this.getInquiry(), responseId)); + final boolean response = handleResponse(responseId, player, data); + if (!response) + { + window.close(); + Minecraft.getInstance().popGuiLayer(); + } + return response; + } + + /** + * Handle the response interaction, identical logic for server and client side. + * + * @param responseId the clicked index. + * @param player the player who clicked. + * @param data the visitor that was clicked. + * @return if wishing to continue interacting. + */ + private boolean handleResponse(final int responseId, final Player player, final ICitizen data) + { + final Component response = getPossibleResponses(data).get(responseId); + final int expeditionId = data.getId(); + + if (response.equals(acceptOkAnswer)) + { + // Accept the expedition and reset the despawn time + data.getColony().getExpeditionManager().acceptExpedition(expeditionId); + if (data instanceof IVisitorData visitorData) + { + visitorData.setExtraDataValue(EXTRA_DATA_DESPAWN_TIME, DespawnTime.fromNow(player.level, DEFAULT_DESPAWN_TIME)); + } + } + + if (response.equals(acceptOkAnswer) || response.equals(prepareGetSheetAnswer)) + { + // Give the player the expedition sheet item + if (!player.level.isClientSide) + { + final ItemStack expeditionSheet = ModItems.expeditionSheet.createItemStackForExpedition(new ExpeditionSheetInfo(data.getColony().getID(), expeditionId)); + if (!InventoryUtils.addItemStackToProvider(player, expeditionSheet)) + { + InventoryUtils.spawnItemStack(player.level, player.getX(), player.getY(), player.getZ(), expeditionSheet); + } + } + } + + if (response.equals(prepareFinishAnswer)) + { + // Attempt to start the expedition + if (!player.level.isClientSide && data instanceof IVisitorData visitorData) + { + tryStartExpedition(visitorData, player); + } + } + + if (response.equals(acceptCancelAnswer) || response.equals(prepareCancelAnswer) || response.equals(finishedCancelAnswer)) + { + // Remove the visitor and remove the expedition + if (!player.level.isClientSide && data instanceof IVisitorData visitorData) + { + data.getColony().getVisitorManager().removeCivilian(visitorData); + } + data.getColony().getExpeditionManager().removeCreatedExpedition(expeditionId); + } + + if (response.equals(finishedViewAnswer)) + { + if (player.level.isClientSide && data instanceof IVisitorViewData visitorData) + { + // Open the loot view + Network.getNetwork().sendToServer(new OpenInventoryMessage(visitorData.getColony(), data.getName(), visitorData.getEntityId())); + } + } + + return false; + } + + /** + * Starts the expedition, assuming all preconditions are met to be able to start said expedition. + * + * @param visitorData the visitor that is being talked to. + * @param player the current player. + */ + private void tryStartExpedition(final IVisitorData visitorData, final Player player) + { + final IColony colony = visitorData.getColony(); + + // Get the expedition instance. + final CreatedExpedition createdExpedition = colony.getExpeditionManager().getCreatedExpedition(visitorData.getId()); + if (createdExpedition == null) + { + return; + } + + // Get the expedition type. + final ColonyExpeditionType colonyExpeditionType = ColonyExpeditionTypeListener.getExpeditionType(createdExpedition.expeditionTypeId()); + if (colonyExpeditionType == null) + { + Log.getLogger().warn("Starting expedition failed, expedition type '{}' does not exist on the server side.", createdExpedition.expeditionTypeId()); + return; + } + + // Find in which slot a valid expedition sheet is located. + final int slot = InventoryUtils.findFirstSlotInItemHandlerWith(new InvWrapper(player.getInventory()), stack -> IS_FINISHED_EXPEDITION_SHEET.test(stack, colony)); + final ItemStack expeditionSheet = player.getInventory().getItem(slot); + if (expeditionSheet.isEmpty()) + { + return; + } + + // Create all the data needed for creating an expedition + final ExpeditionSheetContainerManager expeditionSheetContainerManager = new ExpeditionSheetContainerManager(expeditionSheet); + + final ColonyExpeditionBuilder colonyExpeditionBuilder = new ColonyExpeditionBuilder(new ExpeditionVisitorMember(visitorData)); + expeditionSheetContainerManager.getMembers() + .forEach(memberId -> colonyExpeditionBuilder.addMember(new ExpeditionCitizenMember(colony.getCitizenManager().getCivilian(memberId)))); + + // Process the requirements + final IItemHandler handler = new InvWrapper(expeditionSheetContainerManager); + for (final ColonyExpeditionRequirement requirement : colonyExpeditionType.requirements()) + { + final RequirementHandler requirementHandler = requirement.createHandler(handler); + List matchingItems = InventoryUtils.filterItemHandler(handler, requirementHandler.getItemPredicate()); + matchingItems.forEach(item -> requirementHandler.processOnStart(colonyExpeditionBuilder, item)); + } + + // Attempt to start the expedition + if (!colony.getExpeditionManager().startExpedition(visitorData.getId(), colonyExpeditionBuilder)) + { + return; + } + + // Remove the expedition sheet from the inventory + player.getInventory().removeItem(slot, 1); + + // Create the event related to this expedition. + final ColonyExpedition expedition = Objects.requireNonNull(colony.getExpeditionManager().getActiveExpedition(visitorData.getId())); + colony.getEventManager().addEvent(new ColonyExpeditionEvent(colony, expedition)); + + // Send expedition start message + MessageUtils.format(EXPEDITION_START_MESSAGE, visitorData.getName()) + .withPriority(MessagePriority.IMPORTANT) + .sendTo(colony) + .forManagers(); + + // Add all members to the travelling manager and de-spawn them. + final BlockPos townHallReturnPosition = + BlockPosUtil.findSpawnPosAround(colony.getWorld(), colony.getBuildingManager().getTownHall().getPosition()); + for (final IExpeditionMember member : expedition.getMembers()) + { + colony.getTravelingManager().startTravellingTo(member.getId(), townHallReturnPosition, TICKS_HOUR, false); + + final ICivilianData memberData = member.resolveCivilianData(colony); + if (memberData != null) + { + memberData.getEntity().ifPresent(entity -> entity.remove(RemovalReason.DISCARDED)); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/QuestDialogueInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/QuestDialogueInteraction.java index 90b4ae14d95..5e246b50c20 100644 --- a/src/main/java/com/minecolonies/core/colony/interactionhandling/QuestDialogueInteraction.java +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/QuestDialogueInteraction.java @@ -217,7 +217,7 @@ public boolean isVisible(final Level world) } @Override - public List getPossibleResponses() + public List getPossibleResponses(final ICitizen data) { return currentElement == null ? Collections.emptyList() : currentElement.getOptions().stream().map(this::processText).collect(Collectors.toList()); } diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/RecruitmentInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/RecruitmentInteraction.java index cce517c02d3..075c3c385ac 100644 --- a/src/main/java/com/minecolonies/core/colony/interactionhandling/RecruitmentInteraction.java +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/RecruitmentInteraction.java @@ -33,6 +33,7 @@ import static com.minecolonies.api.util.constant.WindowConstants.CHAT_LABEL_ID; import static com.minecolonies.api.util.constant.WindowConstants.RESPONSE_BOX_ID; import static com.minecolonies.core.client.gui.WindowInteraction.BUTTON_RESPONSE_ID; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; /** * Interaction for recruiting visitors @@ -96,11 +97,10 @@ public void onWindowOpened(final BOWindow window, final ICitizenDataView dataVie final ButtonImage recruitButton = window.findPaneOfTypeByID(BUTTON_RESPONSE_ID + 2, ButtonImage.class); final Box group = window.findPaneOfTypeByID(RESPONSE_BOX_ID, Box.class); - if (recruitButton != null && dataView instanceof IVisitorViewData visitorViewData) { - final ItemStack recruitCost = visitorViewData.getRecruitCost(); - final IColonyView colony = (IColonyView) dataView.getColony(); + final ItemStack recruitCost = visitorViewData.getExtraDataValue(EXTRA_DATA_RECRUIT_COST); + final IColonyView colony = dataView.getColony(); window.findPaneOfTypeByID(CHAT_LABEL_ID, Text.class).setText(PaneBuilders.textBuilder() .append(Component.literal(dataView.getName() + ": ")) @@ -129,12 +129,12 @@ public void onWindowOpened(final BOWindow window, final ICitizenDataView dataVie @OnlyIn(Dist.CLIENT) public boolean onClientResponseTriggered(final int responseId, final Player player, final ICitizenDataView data, final BOWindow window) { - final Component response = getPossibleResponses().get(responseId); + final Component response = getPossibleResponses(data).get(responseId); // Validate recruitment before returning true - if (response.equals(recruitAnswer.getA()) && data instanceof IVisitorViewData) + if (response.equals(recruitAnswer.getA()) && data instanceof IVisitorViewData visitorViewData) { - if (player.isCreative() || InventoryUtils.getItemCountInItemHandler(new InvWrapper(player.getInventory()), ((IVisitorViewData) data).getRecruitCost().getItem()) - >= ((IVisitorViewData) data).getRecruitCost().getCount()) + final ItemStack recruitCost = visitorViewData.getExtraDataValue(EXTRA_DATA_RECRUIT_COST); + if (player.isCreative() || InventoryUtils.getItemCountInItemHandler(new InvWrapper(player.getInventory()), recruitCost.getItem()) >= recruitCost.getCount()) { return super.onClientResponseTriggered(responseId, player, data, window); } @@ -149,18 +149,17 @@ public boolean onClientResponseTriggered(final int responseId, final Player play @Override public void onServerResponseTriggered(final int responseId, final Player player, final ICitizenData data) { - final Component response = getPossibleResponses().get(responseId); - if (response.equals(recruitAnswer.getA()) && data instanceof IVisitorData) + final Component response = getPossibleResponses(data).get(responseId); + if (response.equals(recruitAnswer.getA()) && data instanceof IVisitorData visitorData) { IColony colony = data.getColony(); if (colony.getCitizenManager().getCurrentCitizenCount() < colony.getCitizenManager().getPotentialMaxCitizens()) { - if (player.isCreative() || InventoryUtils.attemptReduceStackInItemHandler(new InvWrapper(player.getInventory()), - ((IVisitorData) data).getRecruitCost(), - ((IVisitorData) data).getRecruitCost().getCount(), true, true)) + final ItemStack recruitCost = visitorData.getExtraDataValue(EXTRA_DATA_RECRUIT_COST); + if (player.isCreative() || InventoryUtils.attemptReduceStackInItemHandler(new InvWrapper(player.getInventory()), recruitCost, recruitCost.getCount(), true, true)) { // Recruits visitor as new citizen and respawns entity - colony.getVisitorManager().removeCivilian(data); + colony.getVisitorManager().removeCivilian(visitorData); data.setHomeBuilding(null); data.setJob(null); @@ -171,7 +170,7 @@ public void onServerResponseTriggered(final int responseId, final Player player, } // Create and read new citizen - ICitizenData newCitizen = colony.getCitizenManager().createAndRegisterCivilianData(); + ICitizenData newCitizen = colony.getCitizenManager().createAndRegisterCitizenData(); newCitizen.deserializeNBT(data.serializeNBT()); newCitizen.setParents("", ""); newCitizen.setLastPosition(data.getLastPosition()); diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/RequestBasedInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/RequestBasedInteraction.java index eab0cfed347..0d7c88e9c17 100644 --- a/src/main/java/com/minecolonies/core/colony/interactionhandling/RequestBasedInteraction.java +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/RequestBasedInteraction.java @@ -154,7 +154,7 @@ public void onWindowOpened(final BOWindow window, final ICitizenDataView dataVie @OnlyIn(Dist.CLIENT) public boolean onClientResponseTriggered(final int responseId, final Player player, final ICitizenDataView data, final BOWindow window) { - if (((TranslatableContents) getPossibleResponses().get(responseId).getContents()).getKey().equals("com.minecolonies.coremod.gui.chat.fulfill")) + if (((TranslatableContents) getPossibleResponses(data).get(responseId).getContents()).getKey().equals("com.minecolonies.coremod.gui.chat.fulfill")) { final IColony colony = IColonyManager.getInstance().getColonyView(data.getColonyId(), player.level.dimension()); @@ -185,7 +185,7 @@ public boolean onClientResponseTriggered(final int responseId, final Player play public void onServerResponseTriggered(final int responseId, final Player player, final ICitizenData data) { super.onServerResponseTriggered(responseId, player, data); - final Component response = getPossibleResponses().get(responseId); + final Component response = getPossibleResponses(data).get(responseId); if (response.equals(Component.translatable("com.minecolonies.coremod.gui.chat.cancel")) && data.getColony() != null) { data.getColony().getRequestManager().updateRequestState(token, RequestState.CANCELLED); diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/ServerCitizenInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/ServerCitizenInteraction.java index 5655879bd65..892153e3e81 100644 --- a/src/main/java/com/minecolonies/core/colony/interactionhandling/ServerCitizenInteraction.java +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/ServerCitizenInteraction.java @@ -129,7 +129,7 @@ public void removeParent(final Component oldParent) @Override public void onServerResponseTriggered(final int responseId, final Player player, final ICitizenData data) { - final Component response = getPossibleResponses().get(responseId); + final Component response = getPossibleResponses(data).get(responseId); tryHandleIgnoreResponse(response, player); } @@ -164,9 +164,9 @@ else if (((TranslatableContents) response.getContents()).getKey().equals(INTERAC @OnlyIn(Dist.CLIENT) public boolean onClientResponseTriggered(final int responseId, final Player player, final ICitizenDataView data, final BOWindow window) { - final Component response = getPossibleResponses().get(responseId); + final Component response = getPossibleResponses(data).get(responseId); tryHandleIgnoreResponse(response, player); - if (((TranslatableContents) getPossibleResponses().get(responseId).getContents()).getKey().equals("com.minecolonies.coremod.gui.chat.skipchitchat")) + if (((TranslatableContents) getPossibleResponses(data).get(responseId).getContents()).getKey().equals("com.minecolonies.coremod.gui.chat.skipchitchat")) { final MainWindowCitizen windowCitizen = new MainWindowCitizen(data); windowCitizen.open(); diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/SimpleNotificationInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/SimpleNotificationInteraction.java index 6e0ab503d8a..ecceee697f6 100644 --- a/src/main/java/com/minecolonies/core/colony/interactionhandling/SimpleNotificationInteraction.java +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/SimpleNotificationInteraction.java @@ -1,6 +1,7 @@ package com.minecolonies.core.colony.interactionhandling; import com.ldtteam.blockui.views.BOWindow; +import com.minecolonies.api.colony.ICitizen; import com.minecolonies.api.colony.ICitizenData; import com.minecolonies.api.colony.ICitizenDataView; import com.minecolonies.api.colony.interactionhandling.IChatPriority; @@ -33,14 +34,14 @@ public SimpleNotificationInteraction( public void onServerResponseTriggered(final int responseId, final Player player, final ICitizenData data) { super.onServerResponseTriggered(responseId, player, data); - onResponse(responseId); + onResponse(responseId, data); } @Override @OnlyIn(Dist.CLIENT) public boolean onClientResponseTriggered(final int responseId, final Player player, final ICitizenDataView data, final BOWindow window) { - onResponse(responseId); + onResponse(responseId, data); return super.onClientResponseTriggered(responseId, player, data, window); } @@ -48,10 +49,11 @@ public boolean onClientResponseTriggered(final int responseId, final Player play * Removes the interaction after a response * * @param responseId response + * @param data the citizen related to it. */ - private void onResponse(final int responseId) + private void onResponse(final int responseId, final ICitizen data) { - final Component response = getPossibleResponses().get(responseId); + final Component response = getPossibleResponses(data).get(responseId); if (response.getContents() instanceof TranslatableContents) { if (((TranslatableContents) response.getContents()).getKey().equals(INTERACTION_R_OKAY) diff --git a/src/main/java/com/minecolonies/core/colony/jobs/AbstractJob.java b/src/main/java/com/minecolonies/core/colony/jobs/AbstractJob.java index 8bcadbadabd..a39ca0fe0fd 100755 --- a/src/main/java/com/minecolonies/core/colony/jobs/AbstractJob.java +++ b/src/main/java/com/minecolonies/core/colony/jobs/AbstractJob.java @@ -236,6 +236,8 @@ public void serializeToView(final FriendlyByteBuf buffer) StandardFactoryController.getInstance().serialize(buffer, token); } buffer.writeRegistryId(IJobRegistry.getInstance(), getJobRegistryEntry()); + buffer.writeBoolean(isGuard()); + buffer.writeBoolean(isCombatGuard()); } /** diff --git a/src/main/java/com/minecolonies/core/colony/jobs/AbstractJobGuard.java b/src/main/java/com/minecolonies/core/colony/jobs/AbstractJobGuard.java index 22df908a50c..750045aa9f0 100755 --- a/src/main/java/com/minecolonies/core/colony/jobs/AbstractJobGuard.java +++ b/src/main/java/com/minecolonies/core/colony/jobs/AbstractJobGuard.java @@ -55,6 +55,12 @@ public boolean isGuard() return true; } + @Override + public boolean isCombatGuard() + { + return true; + } + /** * Whether the guard is asleep. * diff --git a/src/main/java/com/minecolonies/core/colony/jobs/JobDruid.java b/src/main/java/com/minecolonies/core/colony/jobs/JobDruid.java index 9b7719621cf..64a5051db96 100644 --- a/src/main/java/com/minecolonies/core/colony/jobs/JobDruid.java +++ b/src/main/java/com/minecolonies/core/colony/jobs/JobDruid.java @@ -28,6 +28,12 @@ public JobDruid(final ICitizenData entity) super(entity); } + @Override + public boolean isCombatGuard() + { + return false; + } + @Override public EntityAIDruid generateGuardAI() { diff --git a/src/main/java/com/minecolonies/core/colony/jobs/views/DefaultJobView.java b/src/main/java/com/minecolonies/core/colony/jobs/views/DefaultJobView.java index 0aa94b0b147..04f031cd513 100644 --- a/src/main/java/com/minecolonies/core/colony/jobs/views/DefaultJobView.java +++ b/src/main/java/com/minecolonies/core/colony/jobs/views/DefaultJobView.java @@ -31,6 +31,16 @@ public class DefaultJobView implements IJobView */ private JobEntry entry; + /** + * Whether this job is a guard job. + */ + private boolean isGuard; + + /** + * Whether this job is a guard job that actively engages in combat. + */ + private boolean isCombatGuard; + /** * Instantiate the default job view. * @param iColonyView the colony it belongs to. @@ -51,6 +61,8 @@ public void deserialize(final FriendlyByteBuf buffer) asyncRequests.add(StandardFactoryController.getInstance().deserialize(buffer)); } entry = buffer.readRegistryId(); + isGuard = buffer.readBoolean(); + isCombatGuard = buffer.readBoolean(); } @Override @@ -65,6 +77,18 @@ public JobEntry getEntry() return entry; } + @Override + public boolean isGuard() + { + return isGuard; + } + + @Override + public boolean isCombatGuard() + { + return isCombatGuard; + } + /** * Get the colony view this job belongs to. * @return the view. diff --git a/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java b/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java index 0ac81368971..6ef0008adbe 100755 --- a/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java @@ -3,7 +3,6 @@ import com.minecolonies.api.MinecoloniesAPIProxy; import com.minecolonies.api.colony.ICitizenData; import com.minecolonies.api.colony.ICitizenDataManager; -import com.minecolonies.api.colony.ICivilianData; import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.buildings.HiringMode; import com.minecolonies.api.colony.buildings.IBuilding; @@ -221,11 +220,11 @@ public void sendPackets( } @Override - public ICitizenData spawnOrCreateCivilian(@Nullable final ICivilianData data, final Level world, final BlockPos spawnPos, final boolean force) + public ICitizenData spawnOrCreateCitizen(@Nullable final ICitizenData data, final Level world, final BlockPos spawnPos, final boolean force) { if (!colony.getBuildingManager().hasTownHall() || (!colony.canMoveIn() && !force)) { - return (ICitizenData) data; + return data; } BlockPos spawnLocation = spawnPos; @@ -239,7 +238,7 @@ public ICitizenData spawnOrCreateCivilian(@Nullable final ICivilianData data, fi BlockPos calculatedSpawn = EntityUtils.getSpawnPoint(world, spawnLocation); if (calculatedSpawn != null) { - return spawnCitizenOnPosition((ICitizenData) data, world, force, calculatedSpawn); + return spawnCitizenOnPosition(data, world, force, calculatedSpawn); } else { @@ -248,7 +247,7 @@ public ICitizenData spawnOrCreateCivilian(@Nullable final ICivilianData data, fi calculatedSpawn = EntityUtils.getSpawnPoint(world, colony.getBuildingManager().getTownHall().getID()); if (calculatedSpawn != null) { - return spawnCitizenOnPosition((ICitizenData) data, world, force, calculatedSpawn); + return spawnCitizenOnPosition(data, world, force, calculatedSpawn); } } @@ -256,7 +255,7 @@ public ICitizenData spawnOrCreateCivilian(@Nullable final ICivilianData data, fi } } - return (ICitizenData) data; + return data; } @NotNull @@ -269,7 +268,7 @@ private ICitizenData spawnCitizenOnPosition( ICitizenData citizenData = data; if (citizenData == null) { - citizenData = createAndRegisterCivilianData(); + citizenData = createAndRegisterCitizenData(); if (getMaxCitizens() >= getCurrentCitizenCount() && !force) { @@ -319,7 +318,7 @@ private ICitizenData spawnCitizenOnPosition( } @Override - public ICitizenData createAndRegisterCivilianData() + public ICitizenData createAndRegisterCitizenData() { //This ensures that citizen IDs are getting reused. //That's needed to prevent bugs when calling IDs that are not used. @@ -375,13 +374,8 @@ public ICitizenData resurrectCivilianData(@NotNull final CompoundTag compoundNBT } @Override - public void removeCivilian(@NotNull final ICivilianData citizen) + public void removeCivilian(@NotNull final ICitizenData citizen) { - if (!(citizen instanceof ICitizenData)) - { - return; - } - //Remove the Citizen citizens.remove(citizen.getId()); @@ -389,11 +383,11 @@ public void removeCivilian(@NotNull final ICivilianData citizen) { for (final AbstractAssignedCitizenModule assignedCitizenModule : building.getModulesByType(AbstractAssignedCitizenModule.class)) { - assignedCitizenModule.removeCitizen((ICitizenData) citizen); + assignedCitizenModule.removeCitizen(citizen); } } - colony.getWorkManager().clearWorkForCitizen((ICitizenData) citizen); + colony.getWorkManager().clearWorkForCitizen(citizen); // Inform Subscribers of removed citizen for (final ServerPlayer player : colony.getPackageManager().getCloseSubscribers()) @@ -468,7 +462,7 @@ public void spawnOrCreateCitizen() @NotNull @Override - public Map getCivilianDataMap() + public Map getCivilianDataMap() { return Collections.unmodifiableMap(citizens); } @@ -604,7 +598,7 @@ public void onColonyTick(final IColony colony) } final boolean firstCitizen = getCitizens().size() == 0; - final ICitizenData newCitizen = createAndRegisterCivilianData(); + final ICitizenData newCitizen = createAndRegisterCitizenData(); if (firstCitizen) { colony.getQuestManager().injectAvailableQuest(new QuestInstance(new ResourceLocation(MOD_ID, "tutorial/welcome"), colony, List.of(new CitizenTriggerReturnData(newCitizen)))); @@ -626,7 +620,7 @@ else if (femaleCount < (getCitizens().size() - 1) / 2.0) newCitizen.setGenderAndGenerateName(false); } - spawnOrCreateCivilian(newCitizen, colony.getWorld(), null, true); + spawnOrCreateCitizen(newCitizen, colony.getWorld(), null, true); try { diff --git a/src/main/java/com/minecolonies/core/colony/managers/ColonyExpeditionManager.java b/src/main/java/com/minecolonies/core/colony/managers/ColonyExpeditionManager.java new file mode 100644 index 00000000000..3853a11322f --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/managers/ColonyExpeditionManager.java @@ -0,0 +1,457 @@ +package com.minecolonies.core.colony.managers; + +import com.google.common.collect.EvictingQueue; +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.buildings.ModBuildings; +import com.minecolonies.api.colony.expeditions.ExpeditionFinishedStatus; +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; +import com.minecolonies.api.colony.managers.interfaces.expeditions.ColonyExpedition; +import com.minecolonies.api.colony.managers.interfaces.expeditions.CreatedExpedition; +import com.minecolonies.api.colony.managers.interfaces.expeditions.FinishedExpedition; +import com.minecolonies.api.colony.managers.interfaces.expeditions.IColonyExpeditionManager; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; +import com.minecolonies.api.util.Log; +import com.minecolonies.api.util.NBTUtils; +import com.minecolonies.core.client.gui.generic.ResourceItem.ResourceAvailability; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionBuilder; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.items.ItemExpeditionSheet.ExpeditionSheetContainerManager; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.eventbus.api.Event.HasResult; +import net.minecraftforge.eventbus.api.Event.Result; +import net.minecraftforge.items.wrapper.InvWrapper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +/** + * Implementation for the colony expedition manager. From here all outgoing expeditions to external places are managed. + */ +public class ColonyExpeditionManager implements IColonyExpeditionManager +{ + /** + * NBT tags. + */ + private static final String TAG_CREATED_EXPEDITIONS = "createdExpeditions"; + private static final String TAG_CREATED_EXPEDITION_ID = "id"; + private static final String TAG_CREATED_EXPEDITION_TYPE_ID = "expeditionTypeId"; + private static final String TAG_CREATED_EXPEDITION_ACCEPTED = "accepted"; + private static final String TAG_ACTIVE_EXPEDITIONS = "activeExpeditions"; + private static final String TAG_FINISHED_EXPEDITIONS = "finishedExpeditions"; + private static final String TAG_FINISHED_EXPEDITION_DATA = "data"; + private static final String TAG_FINISHED_EXPEDITION_STATUS = "status"; + private static final String TAG_RUINED_PORTAL_DISCOVER = "isRuinedPortalDiscovered"; + private static final String TAG_STRONGHOLD_PORTAL_DISCOVER = "isStrongholdDiscovered"; + + /** + * The maximum amount of expeditions kept in the history. + */ + private static final int MAX_EXPEDITION_HISTORY = 5; + + /** + * The colony this manager is for. + */ + private final IColony colony; + + /** + * The currently created expedition(s). + */ + private final Map createdExpeditions = new HashMap<>(); + + /** + * The currently active expedition(s). + */ + private final Map activeExpeditions = new HashMap<>(); + + /** + * The currently finished expeditions. + */ + private final EvictingQueue finishedExpeditions = EvictingQueue.create(MAX_EXPEDITION_HISTORY); + + /** + * Whether a ruined portal has been discovered by an expedition. + */ + private boolean isRuinedPortalDiscovered; + + /** + * Whether a stronghold has been discovered by an expedition. + */ + private boolean isStrongholdDiscovered; + + /** + * The cached list of active expeditions for {@link ColonyExpeditionManager#getActiveExpeditions()}. + */ + private List activeExpeditionsCache; + + /** + * The cached list of finished expeditions for {@link ColonyExpeditionManager#getFinishedExpeditions()} ()}. + */ + private List finishedExpeditionsCache; + + /** + * Whether this manager is dirty. + */ + private boolean dirty; + + /** + * Default constructor. + */ + public ColonyExpeditionManager(final IColony colony) + { + this.colony = colony; + } + + @Override + public List getActiveExpeditions() + { + return activeExpeditionsCache; + } + + @Override + public List getFinishedExpeditions() + { + return finishedExpeditionsCache; + } + + @Override + @Nullable + public CreatedExpedition getCreatedExpedition(final int id) + { + return createdExpeditions.get(id); + } + + @Override + @Nullable + public ColonyExpedition getActiveExpedition(final int id) + { + return activeExpeditions.get(id); + } + + @Override + @Nullable + public FinishedExpedition getFinishedExpedition(final int id) + { + return finishedExpeditions.stream().filter(f -> f.expedition().getId() == id).findFirst().orElse(null); + } + + @Override + @NotNull + public ExpeditionStatus getExpeditionStatus(final int id) + { + if (createdExpeditions.containsKey(id)) + { + return createdExpeditions.get(id).accepted() ? ExpeditionStatus.ACCEPTED : ExpeditionStatus.CREATED; + } + + if (activeExpeditions.containsKey(id)) + { + return ExpeditionStatus.ONGOING; + } + + if (finishedExpeditions.stream().anyMatch(finishedExpedition -> finishedExpedition.expedition().getId() == id)) + { + return ExpeditionStatus.FINISHED; + } + + return ExpeditionStatus.UNKNOWN; + } + + @Override + public boolean addExpedition(final int id, final ResourceLocation expeditionTypeId) + { + if (createdExpeditions.containsKey(id)) + { + return false; + } + + createdExpeditions.put(id, new CreatedExpedition(id, expeditionTypeId, false)); + updateCaches(); + + colony.markDirty(); + dirty = true; + return true; + } + + @Override + public boolean acceptExpedition(final int id) + { + final boolean exists = createdExpeditions.containsKey(id); + if (exists) + { + createdExpeditions.put(id, createdExpeditions.get(id).accept()); + updateCaches(); + + colony.markDirty(); + dirty = true; + } + return exists; + } + + @Override + public boolean startExpedition(final int id, final ColonyExpeditionBuilder builder) + { + final boolean exists = createdExpeditions.containsKey(id); + if (exists && createdExpeditions.get(id).accepted()) + { + activeExpeditions.put(id, createdExpeditions.get(id).createExpedition(builder)); + createdExpeditions.remove(id); + updateCaches(); + + colony.markDirty(); + dirty = true; + } + return exists; + } + + @Override + public boolean finishExpedition(final int id, final ExpeditionFinishedStatus status) + { + final boolean exists = activeExpeditions.containsKey(id); + if (exists) + { + finishedExpeditions.add(new FinishedExpedition(activeExpeditions.remove(id), status)); + updateCaches(); + + colony.markDirty(); + dirty = true; + } + return exists; + } + + @Override + public void removeCreatedExpedition(final int id) + { + createdExpeditions.remove(id); + } + + @Override + public boolean canStartNewExpedition() + { + return colony.getVisitorManager().getCivilianDataMap().values().stream() + .noneMatch(f -> f.getVisitorType().equals(ModVisitorTypes.expeditionary.get())); + } + + @Override + public boolean canGoToDimension(final ResourceKey dimension) + { + final ExpeditionDimensionAllowedEvent event = new ExpeditionDimensionAllowedEvent(dimension); + MinecraftForge.EVENT_BUS.post(event); + if (event.getResult().equals(Result.ALLOW)) + { + return true; + } + + if (dimension.equals(Level.OVERWORLD)) + { + return true; + } + else if (dimension.equals(Level.NETHER)) + { + return isRuinedPortalDiscovered + || colony.getBuildingManager().getFirstBuildingMatching(building -> building.getBuildingType().equals(ModBuildings.netherWorker.get())) != null; + } + else if (dimension.equals(Level.END)) + { + return isStrongholdDiscovered; + } + return false; + } + + @Override + public boolean meetsRequirements(final ResourceLocation expeditionTypeId, final ExpeditionSheetContainerManager inventory) + { + return parseExpeditionAndRunActual(expeditionTypeId, inventory, this::meetsRequirements, false); + } + + @Override + public boolean meetsRequirements(final ColonyExpeditionType expeditionType, final ExpeditionSheetContainerManager inventory) + { + return expeditionType.requirements().stream() + .map(m -> m.createHandler(new InvWrapper(inventory))) + .anyMatch(f -> f.getAvailabilityStatus().equals(ResourceAvailability.NOT_NEEDED)) + && inventory.getMembers().size() >= expeditionType.guards(); + } + + @Override + public void unlockNether() + { + isRuinedPortalDiscovered = true; + } + + @Override + public void unlockEnd() + { + isStrongholdDiscovered = true; + } + + @Override + public boolean isDirty() + { + return dirty; + } + + @Override + public void setDirty(final boolean dirty) + { + this.dirty = dirty; + } + + /** + * Helper method to parse the underlying expedition type and execute the full method. + * + * @param expeditionTypeId the expedition type id. + * @param arg the extra argument. + * @param func the function runnable. + * @param def the default return value if the expedition can't be found. + * @param the return type. + * @param the extra argument. + * @return the original result from the actual method. + */ + private T parseExpeditionAndRunActual(final ResourceLocation expeditionTypeId, final A arg, final BiFunction func, final T def) + { + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getExpeditionType(expeditionTypeId); + if (expeditionType == null) + { + Log.getLogger().warn("Expedition type with id {} does not exist", expeditionTypeId); + return def; + } + + return func.apply(expeditionType, arg); + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + final ListTag createdExpeditionsCompound = createdExpeditions.entrySet().stream() + .map(expedition -> { + final CompoundTag expeditionItemCompound = new CompoundTag(); + expeditionItemCompound.putInt(TAG_CREATED_EXPEDITION_ID, expedition.getKey()); + expeditionItemCompound.putString(TAG_CREATED_EXPEDITION_TYPE_ID, expedition.getValue().expeditionTypeId().toString()); + expeditionItemCompound.putBoolean(TAG_CREATED_EXPEDITION_ACCEPTED, expedition.getValue().accepted()); + return expeditionItemCompound; + }) + .collect(NBTUtils.toListNBT()); + compound.put(TAG_CREATED_EXPEDITIONS, createdExpeditionsCompound); + + final ListTag activeExpeditionsCompound = activeExpeditions.values().stream() + .map(expedition -> { + final CompoundTag expeditionItemCompound = new CompoundTag(); + expedition.write(expeditionItemCompound); + return expeditionItemCompound; + }) + .collect(NBTUtils.toListNBT()); + compound.put(TAG_ACTIVE_EXPEDITIONS, activeExpeditionsCompound); + + final ListTag finishedExpeditionsCompound = finishedExpeditions.stream() + .map(expedition -> { + final CompoundTag expeditionItemCompound = new CompoundTag(); + final CompoundTag expeditionDataCompound = new CompoundTag(); + expedition.expedition().write(expeditionDataCompound); + expeditionItemCompound.put(TAG_FINISHED_EXPEDITION_DATA, expeditionDataCompound); + expeditionItemCompound.putString(TAG_FINISHED_EXPEDITION_STATUS, expedition.status().name()); + return expeditionItemCompound; + }) + .collect(NBTUtils.toListNBT()); + compound.put(TAG_FINISHED_EXPEDITIONS, finishedExpeditionsCompound); + + compound.putBoolean(TAG_RUINED_PORTAL_DISCOVER, isRuinedPortalDiscovered); + compound.putBoolean(TAG_STRONGHOLD_PORTAL_DISCOVER, isStrongholdDiscovered); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compound) + { + final ListTag createdExpeditionsCompound = compound.getList(TAG_CREATED_EXPEDITIONS, Tag.TAG_COMPOUND); + createdExpeditions.clear(); + createdExpeditions.putAll(NBTUtils.streamCompound(createdExpeditionsCompound) + .map(expeditionItemCompound -> { + final int id = expeditionItemCompound.getInt(TAG_CREATED_EXPEDITION_ID); + final ResourceLocation expeditionTypeId = new ResourceLocation(expeditionItemCompound.getString(TAG_CREATED_EXPEDITION_TYPE_ID)); + final boolean accepted = expeditionItemCompound.getBoolean(TAG_CREATED_EXPEDITION_ACCEPTED); + return new CreatedExpedition(id, expeditionTypeId, accepted); + }) + .collect(Collectors.toMap(CreatedExpedition::id, v -> v))); + + final ListTag activeExpeditionsCompound = compound.getList(TAG_ACTIVE_EXPEDITIONS, Tag.TAG_COMPOUND); + activeExpeditions.clear(); + activeExpeditions.putAll(NBTUtils.streamCompound(activeExpeditionsCompound) + .map(ColonyExpedition::loadFromNBT) + .collect(Collectors.toMap(ColonyExpedition::getId, v -> v))); + + final ListTag finishedExpeditionsCompound = compound.getList(TAG_FINISHED_EXPEDITIONS, Tag.TAG_COMPOUND); + finishedExpeditions.clear(); + finishedExpeditions.addAll(NBTUtils.streamCompound(finishedExpeditionsCompound) + .map((expeditionItemCompound) -> { + final ColonyExpedition expeditionCompound = ColonyExpedition.loadFromNBT(expeditionItemCompound.getCompound(TAG_FINISHED_EXPEDITION_DATA)); + final ExpeditionFinishedStatus status = ExpeditionFinishedStatus.valueOf(expeditionItemCompound.getString(TAG_FINISHED_EXPEDITION_STATUS)); + return new FinishedExpedition(expeditionCompound, status); + }) + .toList()); + + updateCaches(); + + isRuinedPortalDiscovered = compound.getBoolean(TAG_RUINED_PORTAL_DISCOVER); + isStrongholdDiscovered = compound.getBoolean(TAG_STRONGHOLD_PORTAL_DISCOVER); + } + + /** + * Update the cache lists for the list getters. + */ + private void updateCaches() + { + activeExpeditionsCache = activeExpeditions.values().stream().toList(); + finishedExpeditionsCache = finishedExpeditions.stream().map(FinishedExpedition::expedition).collect(Collectors.toList()); + Collections.reverse(finishedExpeditionsCache); + } + + /** + * This event is fired by {@link ColonyExpeditionManager#canGoToDimension(ResourceKey)}. + * This allows other mods to control whether a dimension is allowed to send expedition to from the colony. + *

+ * Set the result to {@link net.minecraftforge.eventbus.api.Event.Result#ALLOW}, otherwise the dimension is deemed as not allowed. + */ + @HasResult + private static class ExpeditionDimensionAllowedEvent extends Event + { + /** + * The requested dimension. + */ + private final ResourceKey dimension; + + /** + * Internal constructor. + * + * @param dimension the requested dimension. + */ + private ExpeditionDimensionAllowedEvent(final ResourceKey dimension) + { + this.dimension = dimension; + } + + /** + * The requested dimension. + * + * @return the level resource key. + */ + public ResourceKey getDimension() + { + return dimension; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/managers/EventManager.java b/src/main/java/com/minecolonies/core/colony/managers/EventManager.java index b62a681bf5f..15a5e8025e1 100755 --- a/src/main/java/com/minecolonies/core/colony/managers/EventManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/EventManager.java @@ -2,6 +2,7 @@ import com.minecolonies.api.MinecoloniesAPIProxy; import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.colonyEvents.EventStatus; import com.minecolonies.api.colony.colonyEvents.IColonyEntitySpawnEvent; import com.minecolonies.api.colony.colonyEvents.IColonyEvent; import com.minecolonies.api.colony.colonyEvents.registry.ColonyEventTypeRegistryEntry; @@ -188,18 +189,19 @@ public void onColonyTick(@NotNull final IColony colony) { final IColonyEvent event = iterator.next(); - if (event.getStatus() == DONE) + final EventStatus status = event.getStatus(); + if (status == DONE) { event.onFinish(); structureManager.loadBackupForEvent(event.getID()); colony.markDirty(); iterator.remove(); } - else if (event.getStatus() == STARTING) + else if (status == STARTING) { event.onStart(); } - else if (event.getStatus() == CANCELED) + else if (status == CANCELED) { colony.markDirty(); iterator.remove(); diff --git a/src/main/java/com/minecolonies/core/colony/managers/ReproductionManager.java b/src/main/java/com/minecolonies/core/colony/managers/ReproductionManager.java index 9d287a6e609..11778c7eca0 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/ReproductionManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/ReproductionManager.java @@ -115,7 +115,7 @@ public void trySpawnChild() } } - final ICitizenData newCitizen = colony.getCitizenManager().createAndRegisterCivilianData(); + final ICitizenData newCitizen = colony.getCitizenManager().createAndRegisterCitizenData(); ICitizenData firstParent; ICitizenData secondParent; if (!assignedCitizens.isEmpty()) diff --git a/src/main/java/com/minecolonies/core/colony/managers/TravelingManager.java b/src/main/java/com/minecolonies/core/colony/managers/TravelingManager.java new file mode 100644 index 00000000000..187cf7cb644 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/managers/TravelingManager.java @@ -0,0 +1,343 @@ +package com.minecolonies.core.colony.managers; + +import com.minecolonies.api.colony.ICitizenData; +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.managers.interfaces.ITravelingManager; +import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; +import com.minecolonies.api.util.EntityUtils; +import com.minecolonies.api.util.Log; +import com.minecolonies.api.util.constant.ColonyConstants; +import com.minecolonies.api.util.constant.NbtTagConstants; +import com.minecolonies.core.util.TeleportHelper; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.Tag; +import net.minecraftforge.common.util.INBTSerializable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Implementation that manages the traveling system for a given colony. + */ +public class TravelingManager implements ITravelingManager +{ + /** + * The colony this manager belongs to. + */ + private final IColony colony; + + /** + * The map containing the information on which citizens are travelling. + */ + private final Map travelerDataMap = new HashMap<>(); + + /** + * Whether this manager is dirty and needs to sync to the client again. + */ + private boolean dirty = false; + + /** + * Default constructor. + * + * @param colony the colony this manager belongs to. + */ + public TravelingManager(final IColony colony) + { + this.colony = colony; + } + + @Override + public boolean isTravelling(final int citizenId) + { + return Optional.ofNullable(travelerDataMap.get(citizenId)).map(TravelerData::isTraveling).orElse(false); + } + + @Override + public Optional getTravellingTargetFor(final int citizenId) + { + return Optional.ofNullable(travelerDataMap.get(citizenId)).map(TravelerData::getTarget); + } + + @Override + public void startTravellingTo(final int citizenId, final BlockPos target, final int travelTimeInTicks, final boolean canRecall) + { + travelerDataMap.put(citizenId, new TravelerData(citizenId, target, travelTimeInTicks, canRecall)); + dirty = true; + colony.markDirty(); + } + + @Override + public void finishTravellingFor(final int citizenId) + { + if (travelerDataMap.containsKey(citizenId)) + { + travelerDataMap.remove(citizenId); + dirty = true; + colony.markDirty(); + } + } + + @Override + public void recallAllTravellingCitizens() + { + final Map travelersToKeep = new HashMap<>(); + for (final TravelerData travelerData : this.travelerDataMap.values()) + { + if (!travelerData.canRecall) + { + travelersToKeep.put(travelerData.citizenId, travelerData); + continue; + } + + final ICitizenData citizenData = this.colony.getCitizenManager().getCivilian(travelerData.citizenId); + final BlockPos spawnHutPos; + if (citizenData.getWorkBuilding() != null) + { + spawnHutPos = citizenData.getWorkBuilding().getPosition(); + } + else + { + spawnHutPos = colony.getBuildingManager().getTownHall().getPosition(); + } + + Optional optionalEntityCitizen = citizenData.getEntity(); + if (optionalEntityCitizen.isEmpty()) + { + Log.getLogger().warn(String.format("The traveller %d from colony #%d has returned very confused!", citizenData.getId(), colony.getID())); + citizenData.setNextRespawnPosition(EntityUtils.getSpawnPoint(colony.getWorld(), spawnHutPos)); + citizenData.updateEntityIfNecessary(); + optionalEntityCitizen = citizenData.getEntity(); + } + + optionalEntityCitizen.ifPresent(abstractEntityCitizen -> TeleportHelper.teleportCitizen(abstractEntityCitizen, colony.getWorld(), spawnHutPos)); + } + + this.travelerDataMap.clear(); + this.travelerDataMap.putAll(travelersToKeep); + dirty = true; + colony.markDirty(); + } + + @Override + public boolean isDirty() + { + return dirty; + } + + @Override + public void setDirty(final boolean dirty) + { + this.dirty = dirty; + } + + /** + * Tick the travelers it's data. + */ + public void onTick() + { + travelerDataMap.values().forEach(TravelerData::onTick); + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag data = new CompoundTag(); + final ListTag travelerData = new ListTag(); + + travelerDataMap.values().stream().map(TravelerData::serializeNBT).forEach(travelerData::add); + + data.put(NbtTagConstants.TRAVELER_DATA, travelerData); + + return data; + } + + @Override + public void deserializeNBT(final CompoundTag nbt) + { + final ListTag travelerData = nbt.getList(NbtTagConstants.TRAVELER_DATA, Tag.TAG_COMPOUND); + travelerDataMap.clear(); + + travelerData + .stream() + .filter(CompoundTag.class::isInstance) + .map(CompoundTag.class::cast) + .map(TravelerData::new) + .forEach(data -> travelerDataMap.put(data.getCitizenId(), data)); + } + + /** + * Container class for a traveling citizen. + */ + private static final class TravelerData implements INBTSerializable + { + /** + * The id of the citizen. + */ + private int citizenId = -1; + + /** + * The position they are traveling to. + */ + private BlockPos target = BlockPos.ZERO; + + /** + * The amount of time they are traveling for. + */ + private int initialTravelTime = 0; + + /** + * How much of their travel time is remaining. + */ + private int remainingTravelTime = 0; + + /** + * Whether this citizen can be recalled to the town hall or not. + */ + private boolean canRecall = true; + + /** + * Default constructor. + * + * @param citizenId the id of the citizen. + * @param target the position they are traveling to. + * @param initialTravelTime the amount of time they are traveling for. + * @param canRecall whether this citizen can be recalled to the town hall or not. + */ + public TravelerData(final int citizenId, final BlockPos target, final int initialTravelTime, final boolean canRecall) + { + this.citizenId = citizenId; + this.target = target; + this.initialTravelTime = initialTravelTime; + this.remainingTravelTime = initialTravelTime; + this.canRecall = canRecall; + } + + /** + * Deserialization constructor. + * + * @param tag the compound data. + */ + public TravelerData(final CompoundTag tag) + { + this.deserializeNBT(tag); + } + + /** + * Ticking method for the travelers. + */ + public void onTick() + { + if (remainingTravelTime > 0) + { + remainingTravelTime -= ColonyConstants.UPDATE_TRAVELING_INTERVAL; + remainingTravelTime = Math.max(0, remainingTravelTime); + } + } + + /** + * Whether the citizen has reached their target. + * + * @return true when the travel time is done. + */ + public boolean hasReachedTarget() + { + return remainingTravelTime == 0; + } + + /** + * Get the current percentage of travel time. + * + * @return a percentage. + */ + public double getTravelPercentage() + { + return ((double) remainingTravelTime * 100) / (double) initialTravelTime; + } + + /** + * Get the id of the citizen. + * + * @return the number. + */ + public int getCitizenId() + { + return citizenId; + } + + /** + * Get the position they are traveling to. + * + * @return the position. + */ + public BlockPos getTarget() + { + return target; + } + + /** + * Get the amount of time they are traveling for. + * + * @return the time. + */ + public int getInitialTravelTime() + { + return initialTravelTime; + } + + /** + * Get how much of their travel time is remaining. + * + * @return the time. + */ + public int getRemainingTravelTime() + { + return remainingTravelTime; + } + + /** + * Get whether the citizen is still busy traveling or not. + * + * @return true when the travel time is not done. + */ + public boolean isTraveling() + { + return !hasReachedTarget(); + } + + /** + * Get whether this citizen can be recalled to the town hall or not. + * + * @return true if so. + */ + public boolean canRecall() + { + return canRecall; + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag data = new CompoundTag(); + data.putInt(NbtTagConstants.TAG_CITIZEN, citizenId); + data.put(NbtTagConstants.TAG_TARGET, NbtUtils.writeBlockPos(target)); + data.putInt(NbtTagConstants.TAG_INITIAL_TRAVEL_TIME, initialTravelTime); + data.putInt(NbtTagConstants.TAG_REMAINING_TRAVEL_TIME, remainingTravelTime); + data.putBoolean(NbtTagConstants.TAG_CAN_RECALL, canRecall); + return data; + } + + @Override + public void deserializeNBT(final CompoundTag nbt) + { + this.citizenId = nbt.getInt(NbtTagConstants.TAG_CITIZEN); + this.target = NbtUtils.readBlockPos(nbt.getCompound(NbtTagConstants.TAG_TARGET)); + this.initialTravelTime = nbt.getInt(NbtTagConstants.TAG_INITIAL_TRAVEL_TIME); + this.remainingTravelTime = nbt.getInt(NbtTagConstants.TAG_REMAINING_TRAVEL_TIME); + this.canRecall = nbt.getBoolean(NbtTagConstants.TAG_CAN_RECALL); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java index b834828a9c9..316834b77a4 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java @@ -1,33 +1,39 @@ package com.minecolonies.core.colony.managers; import com.minecolonies.api.colony.ICitizenData; -import com.minecolonies.api.colony.ICivilianData; import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.IVisitorData; import com.minecolonies.api.colony.managers.interfaces.IVisitorManager; -import com.minecolonies.api.entity.ModEntities; import com.minecolonies.api.entity.citizen.AbstractCivilianEntity; import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; +import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; +import com.minecolonies.api.entity.visitor.IVisitorType; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; +import com.minecolonies.api.util.BlockPosUtil; import com.minecolonies.api.util.Log; import com.minecolonies.api.util.WorldUtil; import com.minecolonies.core.Network; import com.minecolonies.core.colony.VisitorData; -import com.minecolonies.core.entity.visitor.VisitorCitizen; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.colony.interactionhandling.ExpeditionInteraction; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DespawnTimeData.DespawnTime; import com.minecolonies.core.network.messages.client.colony.ColonyVisitorViewDataMessage; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; import net.minecraft.nbt.ListTag; -import net.minecraft.core.BlockPos; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; - import org.jetbrains.annotations.NotNull; import java.util.*; import static com.minecolonies.api.util.constant.Constants.SLIGHTLY_UP; import static com.minecolonies.api.util.constant.PathingConstants.HALF_A_BLOCK; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DEFAULT_DESPAWN_TIME; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_DESPAWN_TIME; /** * Manages all visiting entities to the colony @@ -40,22 +46,18 @@ public class VisitorManager implements IVisitorManager public static String TAG_VISIT_MANAGER = "visitManager"; public static String TAG_VISITORS = "visitors"; public static String TAG_NEXTID = "nextID"; - + /** + * The colony of the manager. + */ + private final IColony colony; /** * Map with visitor ID and data */ private Map visitorMap = new HashMap<>(); - /** * Whether this manager is dirty and needs re-serialize */ private boolean isDirty = false; - - /** - * The colony of the manager. - */ - private final IColony colony; - /** * The next free ID */ @@ -208,7 +210,7 @@ public void sendPackets(@NotNull final Set closeSubscribers, @NotN @NotNull @Override - public Map getCivilianDataMap() + public Map getCivilianDataMap() { return Collections.unmodifiableMap(visitorMap); } @@ -220,29 +222,71 @@ public IVisitorData getCivilian(final int citizenId) } @Override - public T getVisitor(int citizenId) + public void removeCivilian(@NotNull final IVisitorData citizen) + { + final IVisitorData data = visitorMap.remove(citizen.getId()); + if (data != null && data.getEntity().isPresent()) + { + data.getEntity().get().remove(Entity.RemovalReason.DISCARDED); + } + } + + @Override + public void markDirty() + { + this.isDirty = true; + } + + @Override + public void clearDirty() + { + this.isDirty = false; + } + + @Override + public void onColonyTick(final IColony colony) + { + if (colony.hasTownHall()) + { + for (final IVisitorData data : visitorMap.values()) + { + data.updateEntityIfNecessary(); + } + + if (colony.getExpeditionManager().canStartNewExpedition()) + { + spawnExpeditionary(); + } + } + } + + @Override + public boolean tickVisitorData(int tickRate) { - return (T) visitorMap.get(citizenId); + for (IVisitorData visitorData : this.getCivilianDataMap().values()) + { + visitorData.update(tickRate); + } + return false; } @Override - public IVisitorData spawnOrCreateCivilian(ICivilianData data, final Level world, final BlockPos spawnPos, final boolean force) + public IVisitorData spawnOrCreateVisitor(final IVisitorType visitorType, IVisitorData data, final Level world, final BlockPos spawnPos) { if (!WorldUtil.isEntityBlockLoaded(world, spawnPos)) { - return (IVisitorData) data; + return data; } if (data == null) { - data = createAndRegisterCivilianData(); + data = createAndRegisterVisitorData(visitorType); } - VisitorCitizen citizenEntity = (VisitorCitizen) ModEntities.VISITOR.create(colony.getWorld()); - + final AbstractEntityVisitor citizenEntity = visitorType.getEntityCreator().apply(world); if (citizenEntity == null) { - return (IVisitorData) data; + return data; } citizenEntity.setUUID(data.getUUID()); @@ -256,49 +300,37 @@ public IVisitorData spawnOrCreateCivilian(ICivilianData data, final Level world, citizenEntity.getCitizenColonyHandler().registerWithColony(data.getColony().getID(), data.getId()); } - return (IVisitorData) data; + return data; } @Override - public IVisitorData createAndRegisterCivilianData() + public IVisitorData createAndRegisterVisitorData(final IVisitorType visitorType) { markDirty(); - final IVisitorData data = new VisitorData(nextVisitorID--, colony); + final IVisitorData data = new VisitorData(nextVisitorID--, colony, visitorType); data.initForNewCivilian(); visitorMap.put(data.getId(), data); return data; } - @Override - public void removeCivilian(@NotNull final ICivilianData citizen) + /** + * Spawn an expeditionary citizen. + */ + public void spawnExpeditionary() { - final IVisitorData data = visitorMap.remove(citizen.getId()); - if (data != null && data.getEntity().isPresent()) + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getRandomExpeditionType(colony); + if (expeditionType != null) { - data.getEntity().get().remove(Entity.RemovalReason.DISCARDED); - } - } + final IVisitorData newVisitor = createAndRegisterVisitorData(ModVisitorTypes.expeditionary.get()); + newVisitor.setExtraDataValue(EXTRA_DATA_DESPAWN_TIME, DespawnTime.fromNow(colony.getWorld(), DEFAULT_DESPAWN_TIME)); + newVisitor.triggerInteraction(new ExpeditionInteraction()); - @Override - public void markDirty() - { - this.isDirty = true; - } - - @Override - public void clearDirty() - { - this.isDirty = false; - } - - @Override - public void onColonyTick(final IColony colony) - { - if (colony.hasTownHall()) - { - for (final IVisitorData data : visitorMap.values()) + if (colony.getExpeditionManager().addExpedition(newVisitor.getId(), expeditionType.id())) { - data.updateEntityIfNecessary(); + spawnOrCreateVisitor(ModVisitorTypes.expeditionary.get(), + newVisitor, + colony.getWorld(), + BlockPosUtil.findSpawnPosAround(colony.getWorld(), colony.getBuildingManager().getTownHall().getPosition())); } } } diff --git a/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenSpawnNew.java b/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenSpawnNew.java index 0e8cddcbee6..ff31693a559 100755 --- a/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenSpawnNew.java +++ b/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenSpawnNew.java @@ -40,7 +40,7 @@ public int onExecute(final CommandContext context) return 0; } - final ICitizenData newCitizen = colony.getCitizenManager().spawnOrCreateCivilian(null, colony.getWorld(), null, true); + final ICitizenData newCitizen = colony.getCitizenManager().spawnOrCreateCitizen(null, colony.getWorld(), null, true); context.getSource().sendSuccess(() -> Component.translatable(COMMAND_CITIZEN_SPAWN_SUCCESS, newCitizen.getName()), true); try diff --git a/src/main/java/com/minecolonies/core/datalistener/ColonyExpeditionTypeListener.java b/src/main/java/com/minecolonies/core/datalistener/ColonyExpeditionTypeListener.java new file mode 100644 index 00000000000..082e7448ced --- /dev/null +++ b/src/main/java/com/minecolonies/core/datalistener/ColonyExpeditionTypeListener.java @@ -0,0 +1,148 @@ +package com.minecolonies.core.datalistener; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.managers.interfaces.expeditions.IColonyExpeditionManager; +import com.minecolonies.api.util.Log; +import com.minecolonies.core.Network; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeParser; +import com.minecolonies.core.network.messages.client.GlobalColonyExpeditionTypeSyncMessage; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceKey; +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 org.apache.logging.log4j.message.FormattedMessage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Loader for json based expedition types. + */ +public class ColonyExpeditionTypeListener extends SimpleJsonResourceReloadListener +{ + /** + * The gson instance. + */ + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + + /** + * The map of all possible expedition types. + */ + private static Map POSSIBLE_TYPES = new HashMap<>(); + + /** + * Set up the core loading, with the directory in the datapack that contains this data + * Directory is: (namespace)/colony/expedition_types/(path) + */ + public ColonyExpeditionTypeListener() + { + super(GSON, "colony/expedition_types"); + } + + /** + * Get the provided expedition type from its id. + * + * @param id the id. + * @return the expedition type instance. + */ + @Nullable + public static ColonyExpeditionType getExpeditionType(final ResourceLocation id) + { + return POSSIBLE_TYPES.get(id); + } + + /** + * Obtain a random expedition type from the map of possible expedition types. + * The target dimension must be reachable according to {@link IColonyExpeditionManager#canGoToDimension(ResourceKey)}. + * This method can also return null, if there are no expedition types available at all. + * + * @param colony the colony reference to get the expedition manager for. + * @return the expedition type. + */ + @Nullable + public static ColonyExpeditionType getRandomExpeditionType(final IColony colony) + { + final IColonyExpeditionManager expeditionManager = colony.getExpeditionManager(); + final List expeditionTypes = new ArrayList<>(POSSIBLE_TYPES.values()); + + ColonyExpeditionType chosenExpeditionType = null; + while (!expeditionTypes.isEmpty() && chosenExpeditionType == null) + { + final ColonyExpeditionType colonyExpeditionType = expeditionTypes.get(colony.getWorld().getRandom().nextInt(expeditionTypes.size())); + if (!expeditionManager.canGoToDimension(colonyExpeditionType.dimension())) + { + expeditionTypes.removeIf(type -> type.dimension().equals(colonyExpeditionType.dimension())); + continue; + } + + chosenExpeditionType = colonyExpeditionType; + } + + return chosenExpeditionType; + } + + /** + * Sync to client. + * + * @param player to send it to. + */ + public static void sendGlobalExpeditionTypePacket(final ServerPlayer player) + { + final FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.buffer()); + byteBuf.writeInt(POSSIBLE_TYPES.size()); + for (final Map.Entry entry : POSSIBLE_TYPES.entrySet()) + { + ColonyExpeditionTypeParser.toBuffer(entry.getValue(), byteBuf); + } + Network.getNetwork().sendToPlayer(new GlobalColonyExpeditionTypeSyncMessage(byteBuf), player); + } + + /** + * Read the data from the packet and parse it. + * + * @param byteBuf pck. + */ + public static void readGlobalExpeditionTypePackets(final FriendlyByteBuf byteBuf) + { + final Map newTypes = new HashMap<>(); + final int size = byteBuf.readInt(); + for (int i = 0; i < size; i++) + { + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeParser.fromBuffer(byteBuf); + newTypes.put(expeditionType.id(), expeditionType); + } + POSSIBLE_TYPES = Collections.unmodifiableMap(newTypes); + } + + @Override + protected void apply( + @NotNull final Map object, @NotNull final ResourceManager resourceManager, @NotNull final ProfilerFiller profiler) + { + Log.getLogger().info("Beginning load of expedition types for colony."); + + final Map newTypes = new HashMap<>(); + for (final Map.Entry entry : object.entrySet()) + { + final ResourceLocation key = entry.getKey(); + try + { + final ColonyExpeditionType parsed = ColonyExpeditionTypeParser.parse(key, entry.getValue().getAsJsonObject()); + newTypes.put(key, parsed); + } + catch (final Exception e) + { + Log.getLogger().error(new FormattedMessage("Error parsing expedition type {}", new Object[] {key}, e)); + } + } + POSSIBLE_TYPES = Collections.unmodifiableMap(newTypes); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java b/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java index 7dae0e11dcb..4719972d080 100644 --- a/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java +++ b/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java @@ -6,29 +6,25 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.minecolonies.api.colony.IVisitorData; -import com.minecolonies.api.colony.buildings.registry.IBuildingRegistry; import com.minecolonies.api.colony.interactionhandling.ChatPriority; -import com.minecolonies.api.colony.jobs.registry.IJobRegistry; -import com.minecolonies.api.colony.jobs.registry.JobEntry; import com.minecolonies.api.entity.citizen.Skill; -import com.minecolonies.api.util.ColonyUtils; import com.minecolonies.api.util.ItemStackUtils; import com.minecolonies.api.util.Log; import com.minecolonies.api.util.MathUtils; -import com.minecolonies.api.util.constant.Constants; -import com.minecolonies.core.MineColonies; import com.minecolonies.core.colony.interactionhandling.RecruitmentInteraction; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; -import net.minecraft.world.item.ItemStack; import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; import java.util.Map; import java.util.Random; import java.util.UUID; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; + /** * Loads and listens to custom visitor data added */ @@ -243,7 +239,7 @@ public void applyToVisitor(final IVisitorData visitorData) if (recruitCost != null) { - visitorData.setRecruitCosts(recruitCost); + visitorData.setExtraDataValue(EXTRA_DATA_RECRUIT_COST, recruitCost); } if (storykey != null) diff --git a/src/main/java/com/minecolonies/core/datalistener/ExpeditionEncounterListener.java b/src/main/java/com/minecolonies/core/datalistener/ExpeditionEncounterListener.java new file mode 100644 index 00000000000..ad75f71f6d0 --- /dev/null +++ b/src/main/java/com/minecolonies/core/datalistener/ExpeditionEncounterListener.java @@ -0,0 +1,119 @@ +package com.minecolonies.core.datalistener; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.minecolonies.api.util.Log; +import com.minecolonies.core.Network; +import com.minecolonies.core.colony.expeditions.encounters.ExpeditionEncounter; +import com.minecolonies.core.colony.expeditions.encounters.ExpeditionEncounterParser; +import com.minecolonies.core.network.messages.client.GlobalExpeditionEncounterSyncMessage; +import io.netty.buffer.Unpooled; +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 org.apache.logging.log4j.message.FormattedMessage; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Loader for json based expedition encounters. + */ +public class ExpeditionEncounterListener extends SimpleJsonResourceReloadListener +{ + /** + * The gson instance. + */ + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + + /** + * The map of all possible expedition encounters. + */ + private static Map POSSIBLE_TYPES = new HashMap<>(); + + /** + * Set up the core loading, with the directory in the datapack that contains this data + * Directory is: (namespace)/colony/expedition_encounters/(path) + */ + public ExpeditionEncounterListener() + { + super(GSON, "colony/expedition_encounters"); + } + + /** + * Get an encounter from its id. + * + * @param encounter the encounter id. + * @return the encounter instance or null. + */ + public static ExpeditionEncounter getEncounter(final ResourceLocation encounter) + { + return POSSIBLE_TYPES.get(encounter); + } + + /** + * Sync to client. + * + * @param player to send it to. + */ + public static void sendGlobalExpeditionEncounterPacket(final ServerPlayer player) + { + final FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.buffer()); + byteBuf.writeInt(POSSIBLE_TYPES.size()); + for (final Map.Entry entry : POSSIBLE_TYPES.entrySet()) + { + ExpeditionEncounterParser.toBuffer(entry.getValue(), byteBuf); + } + Network.getNetwork().sendToPlayer(new GlobalExpeditionEncounterSyncMessage(byteBuf), player); + } + + /** + * Read the data from the packet and parse it. + * + * @param byteBuf pck. + */ + public static void readGlobalExpeditionEncounterPackets(final FriendlyByteBuf byteBuf) + { + final Map newTypes = new HashMap<>(); + final int size = byteBuf.readInt(); + for (int i = 0; i < size; i++) + { + final ExpeditionEncounter encounter = ExpeditionEncounterParser.fromBuffer(byteBuf); + newTypes.put(encounter.id(), encounter); + } + POSSIBLE_TYPES = Collections.unmodifiableMap(newTypes); + } + + @Override + protected void apply( + @NotNull final Map object, + @NotNull final ResourceManager resourceManager, + @NotNull final ProfilerFiller profiler) + { + Log.getLogger().info("Beginning load of expedition encounters."); + + final Map newTypes = new HashMap<>(); + for (final Map.Entry entry : object.entrySet()) + { + final ResourceLocation key = entry.getKey(); + try + { + final ExpeditionEncounter parsed = ExpeditionEncounterParser.parse(key, entry.getValue().getAsJsonObject()); + newTypes.put(key, parsed); + } + catch (final JsonParseException | NullPointerException e) + { + Log.getLogger().error(new FormattedMessage("Error parsing expedition encounter {}", new Object[] {key}, e)); + } + } + + POSSIBLE_TYPES = Collections.unmodifiableMap(newTypes); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIExpeditionary.java b/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIExpeditionary.java new file mode 100644 index 00000000000..34c288a6f03 --- /dev/null +++ b/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIExpeditionary.java @@ -0,0 +1,80 @@ +package com.minecolonies.core.entity.ai.visitor; + +import com.minecolonies.api.colony.buildings.IBuilding; +import com.minecolonies.api.entity.ai.statemachine.states.EntityState; +import com.minecolonies.api.entity.ai.statemachine.states.IState; +import com.minecolonies.api.entity.ai.statemachine.tickratestatemachine.ITickRateStateMachine; +import com.minecolonies.api.entity.ai.statemachine.tickratestatemachine.TickingTransition; +import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; +import com.minecolonies.api.util.WorldUtil; +import org.jetbrains.annotations.NotNull; + +import static com.minecolonies.api.util.constant.Constants.DEFAULT_SPEED; + +/** + * AI for expeditionaries, they hang around in the town hall and not much else. + */ +public class EntityAIExpeditionary implements IState +{ + /** + * The visitor entity we are attached to. + */ + private final AbstractEntityVisitor visitor; + + /** + * The townhall building reference. + */ + private IBuilding townhall; + + /** + * Constructor. + * + * @param entity current entity. + */ + public EntityAIExpeditionary(@NotNull final AbstractEntityVisitor entity) + { + super(); + this.visitor = entity; + + ITickRateStateMachine stateMachine = entity.getEntityStateController(); + stateMachine.addTransition(new TickingTransition<>(EntityState.INIT, this::isEntityLoaded, () -> VisitorState.WANDERING, 50)); + stateMachine.addTransition(new TickingTransition<>(VisitorState.WANDERING, () -> true, this::wander, 50)); + } + + /** + * Whether the entity is in a ticked chunk + * + * @return true if loaded + */ + private boolean isEntityLoaded() + { + if (visitor.getCitizenColonyHandler().getColony() == null || visitor.getCitizenData() == null) + { + return false; + } + + townhall = visitor.getCitizenColonyHandler().getColony().getBuildingManager().getTownHall(); + + return WorldUtil.isEntityBlockLoaded(visitor.level, visitor.blockPosition()); + } + + /** + * Decides on the next activity + * + * @return next state + */ + private VisitorState wander() + { + visitor.getNavigation().moveToRandomPos(10, DEFAULT_SPEED, townhall.getCorners()); + + return VisitorState.WANDERING; + } + + /** + * States of the expeditionary AI. + */ + public enum VisitorState implements IState + { + WANDERING + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIVisitor.java b/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIVisitor.java index 16195643d4a..c906850143a 100644 --- a/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIVisitor.java +++ b/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIVisitor.java @@ -9,7 +9,6 @@ import com.minecolonies.api.util.DamageSourceKeys; import com.minecolonies.api.util.Log; import com.minecolonies.api.util.WorldUtil; -import com.minecolonies.core.colony.VisitorData; import com.minecolonies.core.colony.buildings.DefaultBuildingInstance; import com.minecolonies.core.colony.buildings.modules.BuildingModules; import com.minecolonies.core.colony.buildings.modules.TavernBuildingModule; @@ -20,6 +19,8 @@ import net.minecraft.world.entity.Entity; import org.jetbrains.annotations.NotNull; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_SITTING_POSITION; + /** * AI for visitors, they do sometimes nap on their place, sit on their place, randomly walk around inside building outline */ @@ -174,7 +175,7 @@ private VisitorState decide() final BlockPos pos = tavern.getModule(BuildingModules.TAVERN_VISITOR).getFreeSitPosition(); if (pos != null) { - ((VisitorData) citizen.getCitizenData()).setSittingPosition(pos); + citizen.getCitizenData().setExtraDataValue(EXTRA_DATA_SITTING_POSITION, pos); citizen.isWorkerAtSiteWithMove(pos, 1); actionTimeoutCounter = citizen.getRandom().nextInt(2500) + 3000; return VisitorState.SITTING; @@ -199,7 +200,7 @@ private boolean sit() { if ((actionTimeoutCounter -= 50) <= 0) { - ((VisitorData) citizen.getCitizenData()).setSittingPosition(BlockPos.ZERO); + citizen.getCitizenData().setExtraDataValue(EXTRA_DATA_SITTING_POSITION, BlockPos.ZERO); return true; } @@ -208,7 +209,7 @@ private boolean sit() return false; } - final BlockPos moveTo = ((VisitorData) citizen.getCitizenData()).getSittingPosition(); + final BlockPos moveTo = citizen.getCitizenData().getExtraDataValue(EXTRA_DATA_SITTING_POSITION); if (citizen.isWorkerAtSiteWithMove(moveTo, 1)) { SittingEntity.sitDown(moveTo, citizen, actionTimeoutCounter); @@ -234,7 +235,7 @@ private boolean isEntityLoaded() tavern = (DefaultBuildingInstance) building; } - ((VisitorData) citizen.getCitizenData()).setSittingPosition(BlockPos.ZERO); + citizen.getCitizenData().setExtraDataValue(EXTRA_DATA_SITTING_POSITION, BlockPos.ZERO); return WorldUtil.isEntityBlockLoaded(citizen.level, citizen.blockPosition()); } @@ -272,7 +273,7 @@ private void onException(final RuntimeException e) */ private void resetLogic() { - ((VisitorData) citizen.getCitizenData()).setSittingPosition(BlockPos.ZERO); + citizen.getCitizenData().setExtraDataValue(EXTRA_DATA_SITTING_POSITION, BlockPos.ZERO); } /** diff --git a/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java new file mode 100644 index 00000000000..9d5835939f4 --- /dev/null +++ b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java @@ -0,0 +1,164 @@ +package com.minecolonies.core.entity.visitor; + +import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; +import com.minecolonies.api.entity.ModEntities; +import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; +import com.minecolonies.api.entity.visitor.*; +import com.minecolonies.api.util.MessageUtils; +import com.minecolonies.core.entity.ai.visitor.EntityAIExpeditionary; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DespawnTimeData.DespawnTime; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import static com.minecolonies.api.util.constant.Constants.TICKS_SECOND; +import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITION_FINISHED_LEAVING_MESSAGE; +import static net.minecraft.world.level.Level.TICKS_PER_DAY; + +/** + * Visitor type for expeditionary visitors in the town hall. + */ +public class ExpeditionaryVisitorType implements IVisitorType +{ + /** + * Extra data fields. + */ + public static final DespawnTimeData EXTRA_DATA_DESPAWN_TIME = new DespawnTimeData(); + + /** + * Despawn time of 1 day. + */ + public static final long DEFAULT_DESPAWN_TIME = TICKS_PER_DAY; + + @Override + public ResourceLocation getId() + { + return ModVisitorTypes.EXPEDITIONARY_VISITOR_TYPE_ID; + } + + @Override + public Function getEntityCreator() + { + return ModEntities.EXPEDITIONARY::create; + } + + @Override + public void createStateMachine(final AbstractEntityVisitor visitor) + { + new EntityAIExpeditionary(visitor); + } + + @Override + public List> getExtraDataKeys() + { + return List.of(EXTRA_DATA_DESPAWN_TIME); + } + + @Override + @NotNull + public InteractionResult onPlayerInteraction(final AbstractEntityVisitor visitor, final Player player, final Level level, final InteractionHand hand) + { + visitor.getEntityStateController().setCurrentDelay(TICKS_SECOND * 10); + visitor.getNavigation().stop(); + visitor.getLookControl().setLookAt(player); + return InteractionResult.PASS; + } + + @Override + public void update(final IVisitorData visitor) + { + final Optional entity = visitor.getEntity(); + if (entity.isEmpty()) + { + return; + } + + final ExpeditionStatus expeditionStatus = visitor.getColony().getExpeditionManager().getExpeditionStatus(visitor.getId()); + final DespawnTime despawnTime = visitor.getExtraDataValue(EXTRA_DATA_DESPAWN_TIME); + if (expeditionStatus.mayRemoveVisitor() && despawnTime.isElapsed(visitor.getColony().getWorld())) + { + visitor.getColony().getVisitorManager().removeCivilian(visitor); + + if (expeditionStatus == ExpeditionStatus.CREATED || expeditionStatus == ExpeditionStatus.ACCEPTED) + { + visitor.getColony().getExpeditionManager().removeCreatedExpedition(visitor.getId()); + } + } + + if (expeditionStatus == ExpeditionStatus.FINISHED && entity.get().getInventoryCitizen().isEmpty()) + { + visitor.getColony().getVisitorManager().removeCivilian(visitor); + MessageUtils.format(EXPEDITION_FINISHED_LEAVING_MESSAGE, visitor.getName()).sendTo(visitor.getColony()).forManagers(); + } + } + + /** + * Extra data for storing the despawn time. + */ + public static class DespawnTimeData extends AbstractVisitorExtraData + { + /** + * Holder for the despawn time data. + * + * @param start when the data was last set. + * @param duration how long the duration will be for. + */ + public record DespawnTime(long start, long duration) + { + /** + * Create a new instance for the despawn time. + * + * @param level the level calling from. + * @param duration the duration of the despawn timer. + * @return the created instance. + */ + public static DespawnTime fromNow(Level level, long duration) + { + return new DespawnTime(level.getGameTime(), duration); + } + + /** + * Check if the despawn timer has elapsed. + * + * @param level the level calling from. + * @return true if so. + */ + public boolean isElapsed(Level level) + { + return duration <= 0 || level.getGameTime() > start + duration; + } + } + + /** + * Default constructor. + */ + public DespawnTimeData() + { + super("despawn-time", new DespawnTime(0, 0)); + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + compound.putLong("start", getValue().start); + compound.putLong("time", getValue().duration); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + setValue(new DespawnTime(compoundTag.getLong("start"), compoundTag.getLong("time"))); + } + } +} diff --git a/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java new file mode 100644 index 00000000000..478e05daa75 --- /dev/null +++ b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java @@ -0,0 +1,180 @@ +package com.minecolonies.core.entity.visitor; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.entity.ModEntities; +import com.minecolonies.api.entity.visitor.*; +import com.minecolonies.api.util.BlockPosUtil; +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.MessageUtils; +import com.minecolonies.api.util.MessageUtils.MessagePriority; +import com.minecolonies.api.util.SoundUtils; +import com.minecolonies.core.Network; +import com.minecolonies.core.colony.buildings.modules.TavernBuildingModule; +import com.minecolonies.core.entity.ai.visitor.EntityAIVisitor; +import com.minecolonies.core.network.messages.client.ItemParticleEffectMessage; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Function; + +import static com.minecolonies.api.util.ItemStackUtils.ISFOOD; +import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INFO_COLONY_VISITOR_DIED; +import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INTERACTION_VISITOR_FOOD; + +/** + * Visitor type for "regular" visitors in the tavern. + */ +public class RegularVisitorType implements IVisitorType +{ + /** + * Extra data fields. + */ + public static final SittingPositionData EXTRA_DATA_SITTING_POSITION = new SittingPositionData(); + public static final RecruitCostData EXTRA_DATA_RECRUIT_COST = new RecruitCostData(); + + @Override + public ResourceLocation getId() + { + return ModVisitorTypes.VISITOR_TYPE_ID; + } + + @Override + public Function getEntityCreator() + { + return ModEntities.VISITOR::create; + } + + @Override + public void createStateMachine(final AbstractEntityVisitor visitor) + { + new EntityAIVisitor(visitor); + } + + @Override + public List> getExtraDataKeys() + { + return List.of(EXTRA_DATA_SITTING_POSITION, EXTRA_DATA_RECRUIT_COST); + } + + @Override + @NotNull + public InteractionResult onPlayerInteraction(final AbstractEntityVisitor visitor, final Player player, final Level level, final InteractionHand hand) + { + IVisitorType.super.onPlayerInteraction(visitor, player, level, hand); + + final ItemStack usedStack = player.getItemInHand(hand); + if (ISFOOD.test(usedStack)) + { + final ItemStack remainingItem = usedStack.finishUsingItem(level, visitor); + if (!remainingItem.isEmpty() && remainingItem.getItem() != usedStack.getItem() && (!player.getInventory().add(remainingItem))) + { + InventoryUtils.spawnItemStack(player.level, player.getX(), player.getY(), player.getZ(), remainingItem); + } + + if (!level.isClientSide()) + { + visitor.getCitizenData().increaseSaturation(usedStack.getItem().getFoodProperties(usedStack, visitor).getNutrition()); + + visitor.playSound(SoundEvents.GENERIC_EAT, 1.5f, (float) SoundUtils.getRandomPitch(visitor.getRandom())); + // Position needs to be centered on citizen, Eat AI wrong too? + Network.getNetwork() + .sendToTrackingEntity(new ItemParticleEffectMessage(usedStack, + visitor.getX(), + visitor.getY(), + visitor.getZ(), + visitor.getXRot(), + visitor.getYRot(), + visitor.getEyeHeight()), visitor); + + MessageUtils.forCitizen(visitor, MESSAGE_INTERACTION_VISITOR_FOOD).sendTo(player); + } + return InteractionResult.CONSUME; + } + return InteractionResult.PASS; + } + + @Override + public void onDied(final VisitorCitizen visitor, final DamageSource cause) + { + IColony colony = visitor.getCitizenColonyHandler().getColony(); + if (colony != null && visitor.getCitizenData() != null) + { + colony.getVisitorManager().removeCivilian(visitor.getCitizenData()); + if (visitor.getCitizenData().getHomeBuilding() instanceof final TavernBuildingModule tavern) + { + tavern.setNoVisitorTime(visitor.level.getRandom().nextInt(5000) + 30000); + } + + final String deathLocation = BlockPosUtil.getString(visitor.blockPosition()); + + MessageUtils.format(MESSAGE_INFO_COLONY_VISITOR_DIED, visitor.getCitizenData().getName(), cause.getMsgId(), deathLocation) + .withPriority(MessagePriority.DANGER) + .sendTo(colony) + .forManagers(); + } + } + + /** + * Extra data containing the sitting position of the visitor. + */ + public static class SittingPositionData extends AbstractVisitorExtraData + { + private static final String TAG_VALUE = "value"; + + public SittingPositionData() + { + super("sitting-pos", BlockPos.ZERO); + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + BlockPosUtil.write(compound, TAG_VALUE, getValue()); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + setValue(BlockPosUtil.read(compoundTag, TAG_VALUE)); + } + } + + /** + * Extra data containing the recruitment cost of the visitor. + */ + public static class RecruitCostData extends AbstractVisitorExtraData + { + private static final String TAG_VALUE = "value"; + + public RecruitCostData() + { + super("recruit-cost", ItemStack.EMPTY); + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + compound.put(TAG_VALUE, getValue().save(new CompoundTag())); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + setValue(ItemStack.of(compoundTag.getCompound(TAG_VALUE))); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java b/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java index 7b5745b1487..edc950d353e 100644 --- a/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java @@ -5,14 +5,14 @@ import com.minecolonies.api.colony.permissions.Action; import com.minecolonies.api.colony.requestsystem.StandardFactoryController; import com.minecolonies.api.colony.requestsystem.location.ILocation; -import com.minecolonies.api.entity.CustomGoalSelector; -import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; import com.minecolonies.api.entity.citizen.citizenhandlers.*; import com.minecolonies.api.entity.pathfinding.proxy.IWalkToProxy; +import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; +import com.minecolonies.api.entity.visitor.IVisitorType; import com.minecolonies.api.inventory.InventoryCitizen; import com.minecolonies.api.inventory.container.ContainerCitizenInventory; -import com.minecolonies.api.util.*; -import com.minecolonies.api.util.MessageUtils.MessagePriority; +import com.minecolonies.api.util.CompatibilityUtils; +import com.minecolonies.api.util.ItemStackUtils; import com.minecolonies.api.util.constant.TypeConstants; import com.minecolonies.core.MineColonies; import com.minecolonies.core.Network; @@ -22,12 +22,10 @@ import com.minecolonies.core.entity.ai.minimal.EntityAIInteractToggleAble; import com.minecolonies.core.entity.ai.minimal.LookAtEntityGoal; import com.minecolonies.core.entity.ai.minimal.LookAtEntityInteractGoal; -import com.minecolonies.core.entity.ai.visitor.EntityAIVisitor; import com.minecolonies.core.entity.citizen.EntityCitizen; import com.minecolonies.core.entity.citizen.citizenhandlers.*; import com.minecolonies.core.entity.pathfinding.navigation.MovementHandler; import com.minecolonies.core.entity.pathfinding.proxy.EntityCitizenWalkToProxy; -import com.minecolonies.core.network.messages.client.ItemParticleEffectMessage; import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; import com.minecolonies.core.util.citizenutils.CitizenItemUtils; import net.minecraft.core.BlockPos; @@ -35,7 +33,6 @@ import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; @@ -55,38 +52,47 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static com.minecolonies.api.util.ItemStackUtils.ISFOOD; import static com.minecolonies.api.util.constant.CitizenConstants.TICKS_20; import static com.minecolonies.api.util.constant.Constants.*; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_CITIZEN; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_COLONY_ID; -import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INFO_COLONY_VISITOR_DIED; -import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INTERACTION_VISITOR_FOOD; import static com.minecolonies.core.entity.ai.minimal.EntityAIInteractToggleAble.*; /** * Visitor citizen entity */ -public class VisitorCitizen extends AbstractEntityCitizen +public class VisitorCitizen extends AbstractEntityVisitor { /** - * The citizen experience handler + * The visitor type. */ - private ICitizenExperienceHandler citizenExperienceHandler; + private final IVisitorType visitorType; /** - * It's citizen Id. + * The citizen id. */ - private int citizenId = 0; + private int citizenId = 0; + /** * The Walk to proxy (Shortest path through intermediate blocks). */ private IWalkToProxy proxy; + + /** + * The location used for requests + */ + private ILocation location = null; + /** * Reference to the data representation inside the colony. */ @Nullable - private ICitizenData citizenData; + private IVisitorData visitorData; + + /** + * Citizen data view. + */ + private ICitizenDataView citizenDataView; /** * The citizen inv handler. @@ -97,10 +103,11 @@ public class VisitorCitizen extends AbstractEntityCitizen * The citizen colony handler. */ private ICitizenColonyHandler citizenColonyHandler; + /** * The citizen job handler. */ - private ICitizenJobHandler citizenJobHandler; + private ICitizenJobHandler citizenJobHandler; /** * The citizen sleep handler. @@ -108,14 +115,9 @@ public class VisitorCitizen extends AbstractEntityCitizen private ICitizenSleepHandler citizenSleepHandler; /** - * Citizen data view. - */ - private ICitizenDataView citizenDataView; - - /** - * The location used for requests + * The citizen experience handler */ - private ILocation location = null; + private ICitizenExperienceHandler citizenExperienceHandler; /** * Constructor for a new citizen typed entity. @@ -123,7 +125,7 @@ public class VisitorCitizen extends AbstractEntityCitizen * @param type the Entity type. * @param world the world. */ - public VisitorCitizen(final EntityType type, final Level world) + public VisitorCitizen(final EntityType type, final Level world, final IVisitorType visitorType) { super(type, world); this.citizenInventoryHandler = new CitizenInventoryHandler(this); @@ -132,12 +134,24 @@ public VisitorCitizen(final EntityType type, final Leve this.citizenSleepHandler = new CitizenSleepHandler(this); this.citizenExperienceHandler = new CitizenExperienceHandler(this); + this.visitorType = visitorType; this.moveControl = new MovementHandler(this); this.setPersistenceRequired(); this.setCustomNameVisible(MineColonies.getConfig().getServer().alwaysRenderNameTag.get()); initTasks(); } + /** + * Create a visitor citizen class for the given visitor type. + * + * @param visitorType the type of the visitor. + * @return the visitor instance. + */ + public static EntityType.EntityFactory forVisitorType(final IVisitorType visitorType) + { + return (type, level) -> new VisitorCitizen(type, level, visitorType); + } + private void initTasks() { int priority = 0; @@ -147,17 +161,7 @@ private void initTasks() this.goalSelector.addGoal(++priority, new LookAtEntityInteractGoal(this, Player.class, WATCH_CLOSEST2, 0.2F)); this.goalSelector.addGoal(++priority, new LookAtEntityInteractGoal(this, EntityCitizen.class, WATCH_CLOSEST2_FAR, WATCH_CLOSEST2_FAR_CHANCE)); this.goalSelector.addGoal(++priority, new LookAtEntityGoal(this, LivingEntity.class, WATCH_CLOSEST)); - new EntityAIVisitor(this); - } - - @Override - public ILocation getLocation() - { - if (location == null) - { - location = StandardFactoryController.getInstance().getNewInstance(TypeConstants.ILOCATION, this); - } - return location; + this.visitorType.createStateMachine(this); } @Override @@ -165,7 +169,7 @@ public boolean hurt(@NotNull final DamageSource damageSource, final float damage { if (!(damageSource.getEntity() instanceof EntityCitizen) && super.hurt(damageSource, damage)) { - if (damageSource.getEntity() instanceof LivingEntity && damage > 1.01f) + if (damageSource.getEntity() instanceof LivingEntity livingEntity && damage > 1.01F) { final IBuilding home = getCitizenData().getHomeBuilding(); if (home != null && home.hasModule(BuildingModules.TAVERN_VISITOR)) @@ -176,7 +180,7 @@ public boolean hurt(@NotNull final DamageSource damageSource, final float damage ICitizenData data = citizenColonyHandler.getColonyOrRegister().getVisitorManager().getCivilian(id); if (data != null && data.getEntity().isPresent() && data.getEntity().get().getLastHurtByMob() == null) { - data.getEntity().get().setLastHurtByMob((LivingEntity) damageSource.getEntity()); + data.getEntity().get().setLastHurtByMob(livingEntity); } } } @@ -200,46 +204,145 @@ public boolean hurt(@NotNull final DamageSource damageSource, final float damage return false; } - /** - * Checks if a worker is at his working site. If he isn't, sets it's path to the location - * - * @param site the place where he should walk to - * @param range Range to check in - * @return True if worker is at site, otherwise false. - */ @Override - public boolean isWorkerAtSiteWithMove(@NotNull final BlockPos site, final int range) + public void die(DamageSource cause) { - if (proxy == null) + super.die(cause); + if (!level.isClientSide()) { - proxy = new EntityCitizenWalkToProxy(this); + visitorType.onDied(this, cause); + } + } + + @Override + protected void dropEquipment() + { + //Drop actual inventory + for (int i = 0; i < getInventoryCitizen().getSlots(); i++) + { + final ItemStack itemstack = getCitizenData().getInventory().getStackInSlot(i); + if (ItemStackUtils.getSize(itemstack) > 0) + { + CitizenItemUtils.entityDropItem(this, itemstack); + } + } + } + + @Override + public void onSyncedDataUpdated(EntityDataAccessor dataAccessor) + { + super.onSyncedDataUpdated(dataAccessor); + if (citizenColonyHandler != null) + { + citizenColonyHandler.onSyncDataUpdate(dataAccessor); } - return proxy.walkToBlock(site, range, true); } @Nullable @Override - public ICitizenData getCitizenData() + public AbstractContainerMenu createMenu(final int id, final Inventory playerInventory, final Player playerEntity) { - return citizenData; + return new ContainerCitizenInventory(id, playerInventory, citizenColonyHandler.getColonyId(), citizenId); } @Override - public ICivilianData getCivilianData() + public ICitizenDataView getCitizenDataView() { - return citizenData; + if (this.citizenDataView == null) + { + citizenColonyHandler.updateColonyClient(); + if (citizenColonyHandler.getColonyId() != 0 && citizenId != 0) + { + final IColonyView colonyView = IColonyManager.getInstance().getColonyView(citizenColonyHandler.getColonyId(), level.dimension()); + if (colonyView != null) + { + this.citizenDataView = colonyView.getVisitor(citizenId); + return this.citizenDataView; + } + } + } + else + { + return this.citizenDataView; + } + + return null; } @Override - public void setCivilianData(@Nullable final ICivilianData data) + protected void defineSynchedData() + { + super.defineSynchedData(); + entityData.define(DATA_COLONY_ID, citizenColonyHandler == null ? 0 : citizenColonyHandler.getColonyId()); + entityData.define(DATA_CITIZEN_ID, citizenId); + } + + @Override + public void aiStep() { - if (data != null && data instanceof IVisitorData) + super.aiStep(); + + if (lastHurtByPlayerTime > 0) { - this.citizenData = (IVisitorData) data; - data.initEntityValues(); + markDirty(0); + } + + if (CompatibilityUtils.getWorldFromCitizen(this).isClientSide) + { + citizenColonyHandler.updateColonyClient(); + if (citizenColonyHandler.getColonyId() != 0 && citizenId != 0 && getOffsetTicks() % TICKS_20 == 0) + { + final IColonyView colonyView = IColonyManager.getInstance().getColonyView(citizenColonyHandler.getColonyId(), level.dimension()); + if (colonyView != null) + { + this.citizenDataView = colonyView.getVisitor(citizenId); + getEntityData().set(DATA_STYLE, colonyView.getTextureStyleId()); + } + } + } + else + { + citizenColonyHandler.registerWithColony(citizenColonyHandler.getColonyId(), citizenId); + if (tickCount % 500 == 0) + { + this.setCustomNameVisible(MineColonies.getConfig().getServer().alwaysRenderNameTag.get()); + } } } + @Override + public ILocation getLocation() + { + if (location == null) + { + location = StandardFactoryController.getInstance().getNewInstance(TypeConstants.ILOCATION, this); + } + return location; + } + + /** + * Checks if a worker is at his working site. If he isn't, sets it's path to the location + * + * @param site the place where he should walk to + * @param range Range to check in + * @return True if worker is at site, otherwise false. + */ + @Override + public boolean isWorkerAtSiteWithMove(@NotNull final BlockPos site, final int range) + { + if (proxy == null) + { + proxy = new EntityCitizenWalkToProxy(this); + } + return proxy.walkToBlock(site, range, true); + } + + @Override + public IVisitorData getCitizenData() + { + return visitorData; + } + /** * Return this citizens inventory. * @@ -259,18 +362,6 @@ public IItemHandler getItemHandlerCitizen() return getInventoryCitizen(); } - /** - * Mark the citizen dirty to synch the data with the client. - */ - @Override - public void markDirty(final int time) - { - if (citizenData != null) - { - citizenData.markDirty(time); - } - } - @Override public void setIsChild(final boolean isChild) { @@ -292,10 +383,10 @@ public IWalkToProxy getProxy() @Override public void decreaseSaturationForAction() { - if (citizenData != null) + if (visitorData != null) { - citizenData.decreaseSaturation(citizenColonyHandler.getPerBuildingFoodCost()); - citizenData.markDirty(20 * 20); + visitorData.decreaseSaturation(citizenColonyHandler.getPerBuildingFoodCost()); + visitorData.markDirty(20 * 20); } } @@ -305,35 +396,13 @@ public void decreaseSaturationForAction() @Override public void decreaseSaturationForContinuousAction() { - if (citizenData != null) + if (visitorData != null) { - citizenData.decreaseSaturation(citizenColonyHandler.getPerBuildingFoodCost() / 100.0); - citizenData.markDirty(20 * 60 * 2); + visitorData.decreaseSaturation(citizenColonyHandler.getPerBuildingFoodCost() / 100.0); + visitorData.markDirty(20 * 60 * 2); } } - /** - * Getter for the citizen id. - * - * @return the id. - */ - @Override - public int getCivilianID() - { - return citizenId; - } - - /** - * Setter for the citizen id. - * - * @param id the id to set. - */ - @Override - public void setCitizenId(final int id) - { - this.citizenId = id; - } - @Override public ICitizenExperienceHandler getCitizenExperienceHandler() { @@ -418,11 +487,44 @@ public void callForHelp(final Entity attacker, final int guardHelpRange) } - @Nullable @Override - public AbstractContainerMenu createMenu(final int id, final Inventory playerInventory, final Player playerEntity) + public void queueSound(final @NotNull SoundEvent soundEvent, final BlockPos pos, final int length, final int repetitions) { - return new ContainerCitizenInventory(id, playerInventory, citizenColonyHandler.getColonyId(), citizenId); + + } + + @Override + public void queueSound(final @NotNull SoundEvent soundEvent, final BlockPos pos, final int length, final int repetitions, final float volume, final float pitch) + { + + } + + @Override + public void addAdditionalSaveData(final CompoundTag compound) + { + super.addAdditionalSaveData(compound); + + compound.putInt(TAG_COLONY_ID, citizenColonyHandler.getColonyId()); + if (visitorData != null) + { + compound.putInt(TAG_CITIZEN, visitorData.getId()); + } + } + + @Override + public void readAdditionalSaveData(final CompoundTag compound) + { + super.readAdditionalSaveData(compound); + + if (compound.contains(TAG_COLONY_ID)) + { + citizenColonyHandler.setColonyId(compound.getInt(TAG_COLONY_ID)); + if (compound.contains(TAG_CITIZEN)) + { + citizenId = compound.getInt(TAG_CITIZEN); + citizenColonyHandler.registerWithColony(citizenColonyHandler.getColonyId(), citizenId); + } + } } /** @@ -445,8 +547,8 @@ public InteractionResult checkAndHandleImportantInteractions(final Player player return super.checkAndHandleImportantInteractions(player, hand); } - final InteractionResult result = directPlayerInteraction(player, hand); - if (result != null) + final InteractionResult result = visitorType.onPlayerInteraction(this, player, level, hand); + if (result.consumesAction()) { return result; } @@ -469,182 +571,54 @@ public InteractionResult checkAndHandleImportantInteractions(final Player player return InteractionResult.SUCCESS; } - /** - * Direct interaction on right click - * - * @param player - * @param hand - * @return - */ - private InteractionResult directPlayerInteraction(final Player player, final InteractionHand hand) - { - final ItemStack usedStack = player.getItemInHand(hand); - if (ISFOOD.test(usedStack)) - { - if (!level.isClientSide()) - { - playSound(SoundEvents.GENERIC_EAT, 1.5f, (float) SoundUtils.getRandomPitch(getRandom())); - Network.getNetwork().sendToTrackingEntity(new ItemParticleEffectMessage(usedStack, getX(), getY(), getZ(), getXRot(), getYRot(), getEyeHeight()), this); - ItemStackUtils.consumeFood(usedStack, this, player.getInventory()); - MessageUtils.forCitizen(this, MESSAGE_INTERACTION_VISITOR_FOOD).sendTo(player); - } - return InteractionResult.CONSUME; - } - return null; - } - - @Override - public ICitizenDataView getCitizenDataView() - { - if (this.citizenDataView == null) - { - citizenColonyHandler.updateColonyClient(); - if (citizenColonyHandler.getColonyId() != 0 && citizenId != 0) - { - final IColonyView colonyView = IColonyManager.getInstance().getColonyView(citizenColonyHandler.getColonyId(), level.dimension()); - if (colonyView != null) - { - this.citizenDataView = colonyView.getVisitor(citizenId); - return this.citizenDataView; - } - } - } - else - { - return this.citizenDataView; - } - - return null; - } - @Override - protected void defineSynchedData() + public IVisitorData getCivilianData() { - super.defineSynchedData(); - entityData.define(DATA_COLONY_ID, citizenColonyHandler == null ? 0 : citizenColonyHandler.getColonyId()); - entityData.define(DATA_CITIZEN_ID, citizenId); + return visitorData; } @Override - public void aiStep() - { - super.aiStep(); - - if (lastHurtByPlayerTime > 0) - { - markDirty(0); - } - - if (CompatibilityUtils.getWorldFromCitizen(this).isClientSide) - { - citizenColonyHandler.updateColonyClient(); - if (citizenColonyHandler.getColonyId() != 0 && citizenId != 0 && getOffsetTicks() % TICKS_20 == 0) - { - final IColonyView colonyView = IColonyManager.getInstance().getColonyView(citizenColonyHandler.getColonyId(), level.dimension()); - if (colonyView != null) - { - this.citizenDataView = colonyView.getVisitor(citizenId); - getEntityData().set(DATA_STYLE, colonyView.getTextureStyleId()); - } - } - } - else - { - citizenColonyHandler.registerWithColony(citizenColonyHandler.getColonyId(), citizenId); - if (tickCount % 500 == 0) - { - this.setCustomNameVisible(MineColonies.getConfig().getServer().alwaysRenderNameTag.get()); - } - } - } - - @Override - public void addAdditionalSaveData(final CompoundTag compound) - { - super.addAdditionalSaveData(compound); - - compound.putInt(TAG_COLONY_ID, citizenColonyHandler.getColonyId()); - if (citizenData != null) - { - compound.putInt(TAG_CITIZEN, citizenData.getId()); - } - } - - @Override - public void readAdditionalSaveData(final CompoundTag compound) - { - super.readAdditionalSaveData(compound); - - if (compound.contains(TAG_COLONY_ID)) - { - citizenColonyHandler.setColonyId(compound.getInt(TAG_COLONY_ID)); - if (compound.contains(TAG_CITIZEN)) - { - citizenId = compound.getInt(TAG_CITIZEN); - } - } - } - - @Override - public void die(DamageSource cause) + public void setCivilianData(@Nullable final ICivilianData data) { - super.die(cause); - if (!level.isClientSide()) + if (data instanceof IVisitorData newVisitorData) { - IColony colony = getCitizenColonyHandler().getColonyOrRegister(); - if (colony != null && getCitizenData() != null) - { - colony.getVisitorManager().removeCivilian(getCitizenData()); - if (getCitizenData().getHomeBuilding() instanceof TavernBuildingModule) - { - TavernBuildingModule tavern = (TavernBuildingModule) getCitizenData().getHomeBuilding(); - tavern.setNoVisitorTime(level.getRandom().nextInt(5000) + 30000); - } - - final String deathLocation = BlockPosUtil.getString(blockPosition()); - - MessageUtils.format(MESSAGE_INFO_COLONY_VISITOR_DIED, getCitizenData().getName(), cause.getMsgId(), deathLocation) - .withPriority(MessagePriority.DANGER) - .sendTo(colony) - .forManagers(); - } + this.visitorData = newVisitorData; + data.initEntityValues(); } } + /** + * Mark the citizen dirty to synch the data with the client. + */ @Override - protected void dropEquipment() + public void markDirty(final int time) { - //Drop actual inventory - for (int i = 0; i < getInventoryCitizen().getSlots(); i++) + if (visitorData != null) { - final ItemStack itemstack = getCitizenData().getInventory().getStackInSlot(i); - if (ItemStackUtils.getSize(itemstack) > 0) - { - CitizenItemUtils.entityDropItem(this, itemstack); - } + visitorData.markDirty(time); } } + /** + * Getter for the citizen id. + * + * @return the id. + */ @Override - public void queueSound(final @NotNull SoundEvent soundEvent, final BlockPos pos, final int length, final int repetitions) - { - - } - - @Override - public void queueSound(final @NotNull SoundEvent soundEvent, final BlockPos pos, final int length, final int repetitions, final float volume, final float pitch) + public int getCivilianID() { - + return citizenId; } + /** + * Setter for the citizen id. + * + * @param id the id to set. + */ @Override - public void onSyncedDataUpdated(EntityDataAccessor dataAccessor) + public void setCitizenId(final int id) { - super.onSyncedDataUpdated(dataAccessor); - if (citizenColonyHandler != null) - { - citizenColonyHandler.onSyncDataUpdate(dataAccessor); - } + this.citizenId = id; } @Override diff --git a/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java b/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java index d1be1713109..a04b40604c1 100644 --- a/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java +++ b/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java @@ -273,6 +273,7 @@ public static void doClientStuff(final EntityRenderersEvent.RegisterRenderers ev { event.registerEntityRenderer(ModEntities.CITIZEN, RenderBipedCitizen::new); event.registerEntityRenderer(ModEntities.VISITOR, RenderBipedCitizen::new); + event.registerEntityRenderer(ModEntities.EXPEDITIONARY, RenderBipedCitizen::new); event.registerEntityRenderer(ModEntities.FISHHOOK, RenderFishHook::new); event.registerEntityRenderer(ModEntities.FIREARROW, FireArrowRenderer::new); event.registerEntityRenderer(ModEntities.SPEAR, RendererSpear::new); diff --git a/src/main/java/com/minecolonies/core/event/DataPackSyncEventHandler.java b/src/main/java/com/minecolonies/core/event/DataPackSyncEventHandler.java index d89f476aad8..ad19766c6f4 100644 --- a/src/main/java/com/minecolonies/core/event/DataPackSyncEventHandler.java +++ b/src/main/java/com/minecolonies/core/event/DataPackSyncEventHandler.java @@ -6,6 +6,8 @@ import com.minecolonies.core.Network; import com.minecolonies.core.colony.crafting.CustomRecipeManager; import com.minecolonies.core.compatibility.CraftingTagAuditor; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.datalistener.ExpeditionEncounterListener; import com.minecolonies.core.datalistener.DiseasesListener; import com.minecolonies.core.datalistener.QuestJsonListener; import com.minecolonies.core.network.messages.client.UpdateClientWithCompatibilityMessage; @@ -71,6 +73,8 @@ private static void sendPackets(@NotNull final ServerPlayer player, IGlobalResearchTree.getInstance().sendGlobalResearchTreePackets(player); QuestJsonListener.sendGlobalQuestPackets(player); DiseasesListener.sendGlobalDiseasesPackets(player); + ExpeditionEncounterListener.sendGlobalExpeditionEncounterPacket(player); + ColonyExpeditionTypeListener.sendGlobalExpeditionTypePacket(player); } /** diff --git a/src/main/java/com/minecolonies/core/event/EventHandler.java b/src/main/java/com/minecolonies/core/event/EventHandler.java index b8812e661be..2456a8ada62 100755 --- a/src/main/java/com/minecolonies/core/event/EventHandler.java +++ b/src/main/java/com/minecolonies/core/event/EventHandler.java @@ -15,6 +15,7 @@ import com.minecolonies.api.entity.ai.statemachine.tickratestatemachine.TickRateStateMachine; import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; import com.minecolonies.api.entity.other.AbstractFastMinecoloniesEntity; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; import com.minecolonies.api.items.ModTags; import com.minecolonies.api.loot.EntityInBiomeTag; import com.minecolonies.api.loot.ModLootConditions; @@ -100,6 +101,7 @@ import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_EVENT_ID; import static com.minecolonies.api.util.constant.TranslationConstants.*; import static com.minecolonies.api.util.constant.translation.BaseGameTranslationConstants.BASE_BED_OCCUPIED; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; import static net.minecraftforge.eventbus.api.EventPriority.HIGHEST; import static net.minecraftforge.eventbus.api.EventPriority.LOWEST; @@ -238,7 +240,8 @@ public static void onAttachingCapabilitiesChunk(@NotNull final AttachCapabilitie public static void onAttachingCapabilitiesWorld(@NotNull final AttachCapabilitiesEvent event) { event.addCapability(new ResourceLocation(Constants.MOD_ID, "chunkupdate"), new MinecoloniesWorldCapabilityProvider()); - event.addCapability(new ResourceLocation(Constants.MOD_ID, "colonymanager"), new MinecoloniesWorldColonyManagerCapabilityProvider(event.getObject().dimension() == Level.OVERWORLD)); + event.addCapability(new ResourceLocation(Constants.MOD_ID, "colonymanager"), + new MinecoloniesWorldColonyManagerCapabilityProvider(event.getObject().dimension() == Level.OVERWORLD)); } /** @@ -810,7 +813,7 @@ public static void onEntityConverted(@NotNull final LivingConversionEvent.Pre ev event.setCanceled(true); if (ForgeEventFactory.canLivingConvert(entity, ModEntities.VISITOR, null)) { - IVisitorData visitorData = (IVisitorData) colony.getVisitorManager().createAndRegisterCivilianData(); + IVisitorData visitorData = colony.getVisitorManager().createAndRegisterVisitorData(ModVisitorTypes.visitor.get()); BlockPos tavernPos = colony.getBuildingManager().getRandomBuilding(b -> !b.getModulesByType(TavernBuildingModule.class).isEmpty()); IBuilding tavern = colony.getBuildingManager().getBuilding(tavernPos); @@ -822,7 +825,7 @@ public static void onEntityConverted(@NotNull final LivingConversionEvent.Pre ev List> recruitCosts = IColonyManager.getInstance().getCompatibilityManager().getRecruitmentCostsWeights(); visitorData.getCitizenSkillHandler().init(recruitLevel); - colony.getVisitorManager().spawnOrCreateCivilian(visitorData, world, entity.blockPosition(), false); + colony.getVisitorManager().spawnOrCreateVisitor(ModVisitorTypes.visitor.get(), visitorData, world, entity.blockPosition()); colony.getEventDescriptionManager().addEventDescription(new VisitorSpawnedEvent(entity.blockPosition(), visitorData.getName())); if (visitorData.getEntity().isPresent()) @@ -845,9 +848,9 @@ public static void onEntityConverted(@NotNull final LivingConversionEvent.Pre ev entity.remove(Entity.RemovalReason.DISCARDED); Tuple cost = recruitCosts.get(world.random.nextInt(recruitCosts.size())); - visitorData.setRecruitCosts(new ItemStack(cost.getA(), (int)(recruitLevel * 3.0 / cost.getB()))); + visitorData.setExtraDataValue(EXTRA_DATA_RECRUIT_COST, new ItemStack(cost.getA(), (int) (recruitLevel * 3.0 / cost.getB()))); visitorData.triggerInteraction(new RecruitmentInteraction(Component.translatable( - "com.minecolonies.coremod.gui.chat.recruitstorycured", visitorData.getName().split(" ")[0]), ChatPriority.IMPORTANT)); + "com.minecolonies.coremod.gui.chat.recruitstorycured", visitorData.getName().split(" ")[0]), ChatPriority.IMPORTANT)); } } } @@ -860,7 +863,8 @@ public static void onServerTick(TickEvent.ServerTickEvent event) if (lastTickMs > 50) { TickRateStateMachine.slownessFactor = Mth.clamp(lastTickMs / 50, 1.0D, 5.0D); - } else + } + else { TickRateStateMachine.slownessFactor = 1.0D; } diff --git a/src/main/java/com/minecolonies/core/event/FMLEventHandler.java b/src/main/java/com/minecolonies/core/event/FMLEventHandler.java index 465511f88d5..255716a0e0b 100755 --- a/src/main/java/com/minecolonies/core/event/FMLEventHandler.java +++ b/src/main/java/com/minecolonies/core/event/FMLEventHandler.java @@ -53,6 +53,8 @@ public static void onAddReloadListenerEvent(@NotNull final AddReloadListenerEven event.addListener(new ItemNbtListener()); event.addListener(new StudyItemListener()); event.addListener(new DiseasesListener()); + event.addListener(new ColonyExpeditionTypeListener()); + event.addListener(new ExpeditionEncounterListener()); } @SubscribeEvent diff --git a/src/main/java/com/minecolonies/core/event/GatherDataHandler.java b/src/main/java/com/minecolonies/core/event/GatherDataHandler.java index 51526c58e85..494722d3492 100755 --- a/src/main/java/com/minecolonies/core/event/GatherDataHandler.java +++ b/src/main/java/com/minecolonies/core/event/GatherDataHandler.java @@ -66,6 +66,10 @@ public static void dataGeneratorSetup(final GatherDataEvent event) generator.addProvider(event.includeServer(), new DefaultSifterCraftingProvider(generator.getPackOutput(), lootTableManager)); generator.addProvider(event.includeServer(), new DefaultStonemasonCraftingProvider(generator.getPackOutput())); generator.addProvider(event.includeServer(), new DefaultStoneSmelteryCraftingProvider(generator.getPackOutput())); + generator.addProvider(event.includeServer(), new DefaultExpeditionStructureLootProvider(generator.getPackOutput())); + generator.addProvider(event.includeServer(), new DefaultExpeditionEncountersProvider(generator.getPackOutput())); + generator.addProvider(event.includeServer(), new DefaultColonyExpeditionLootProvider(generator.getPackOutput())); + generator.addProvider(event.includeServer(), new DefaultColonyExpeditionTypesProvider(generator.getPackOutput())); generator.addProvider(event.includeClient() && event.includeServer(), new ItemNbtCalculator(generator.getPackOutput(), event.getLookupProvider())); } diff --git a/src/main/java/com/minecolonies/core/generation/ExpeditionResourceManager.java b/src/main/java/com/minecolonies/core/generation/ExpeditionResourceManager.java new file mode 100644 index 00000000000..52fc66195b9 --- /dev/null +++ b/src/main/java/com/minecolonies/core/generation/ExpeditionResourceManager.java @@ -0,0 +1,395 @@ +package com.minecolonies.core.generation; + +import com.minecolonies.api.items.ModItems; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeDifficulty; +import com.minecolonies.core.loot.ExpeditionDifficultyCondition; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.alchemy.Potion; +import net.minecraft.world.item.alchemy.PotionUtils; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.level.storage.loot.entries.LootItem; +import net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer; +import net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer.Builder; +import net.minecraft.world.level.storage.loot.entries.LootTableReference; +import net.minecraft.world.level.storage.loot.functions.EnchantRandomlyFunction; +import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction; +import net.minecraft.world.level.storage.loot.functions.SetItemDamageFunction; +import net.minecraft.world.level.storage.loot.functions.SetNbtFunction; +import net.minecraft.world.level.storage.loot.providers.number.NumberProvider; +import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +import static com.minecolonies.core.colony.events.ColonyExpeditionEvent.*; + +/** + * Manages creation of resource ids and other common blocks of information, for example for loot pools. + */ +public class ExpeditionResourceManager +{ + /** + * Expedition difficulties + */ + public static final ColonyExpeditionTypeDifficulty DIFF_2 = ColonyExpeditionTypeDifficulty.MEDIUM; + public static final ColonyExpeditionTypeDifficulty DIFF_3 = ColonyExpeditionTypeDifficulty.HARD; + + /** + * Number providers. + */ + private static final NumberProvider COMMON_ITEM_COUNT = UniformGenerator.between(4, 6); + private static final NumberProvider UNCOMMON_ITEM_COUNT = UniformGenerator.between(2, 4); + private static final NumberProvider RARE_ITEM_COUNT = UniformGenerator.between(1, 2); + private static final NumberProvider LOW_TOOL_DAMAGE = UniformGenerator.between(0.75f, 0.95f); + private static final NumberProvider MEDIUM_TOOL_DAMAGE = UniformGenerator.between(0.5f, 0.75f); + + /** + * Builder instance for simple expedition items. + * + * @param builder the underlying loot builder. + */ + public record SimpleItemBuilder(Builder builder) implements ToolItemBuilder + { + @Override + public SimpleItemBuilder common() + { + this.builder.apply(SetItemCountFunction.setCount(COMMON_ITEM_COUNT)); + return this; + } + + @Override + public SimpleItemBuilder uncommon() + { + this.builder.apply(SetItemCountFunction.setCount(UNCOMMON_ITEM_COUNT)); + return this; + } + + @Override + public SimpleItemBuilder rare() + { + this.builder.apply(SetItemCountFunction.setCount(RARE_ITEM_COUNT)); + return this; + } + + @Override + public SimpleItemBuilder diffOnly(@NotNull final ColonyExpeditionTypeDifficulty difficulty) + { + this.builder.when(ExpeditionDifficultyCondition.forDifficulty(difficulty)); + return this; + } + + @Override + public SimpleItemBuilder diffBefore(@NotNull final ColonyExpeditionTypeDifficulty difficulty) + { + final ColonyExpeditionTypeDifficulty[] difficulties = Arrays.stream(ColonyExpeditionTypeDifficulty.values()) + .filter(f -> f.getLevel() <= difficulty.getLevel()) + .toArray(ColonyExpeditionTypeDifficulty[]::new); + this.builder.when(ExpeditionDifficultyCondition.forDifficulty(difficulties)); + return this; + } + + @Override + public SimpleItemBuilder diffAfter(@NotNull final ColonyExpeditionTypeDifficulty difficulty) + { + final ColonyExpeditionTypeDifficulty[] difficulties = Arrays.stream(ColonyExpeditionTypeDifficulty.values()) + .filter(f -> f.getLevel() >= difficulty.getLevel()) + .toArray(ColonyExpeditionTypeDifficulty[]::new); + this.builder.when(ExpeditionDifficultyCondition.forDifficulty(difficulties)); + return this; + } + + @Override + public SimpleItemBuilder damageLow() + { + this.builder.apply(SetItemDamageFunction.setDamage(LOW_TOOL_DAMAGE)); + return this; + } + + @Override + public SimpleItemBuilder damageMid() + { + this.builder.apply(SetItemDamageFunction.setDamage(MEDIUM_TOOL_DAMAGE)); + return this; + } + + @Override + public SimpleItemBuilder enchant(final Enchantment... enchantments) + { + if (enchantments.length > 0) + { + final EnchantRandomlyFunction.Builder enchantmentBuilder = EnchantRandomlyFunction.randomEnchantment(); + for (final Enchantment enchantment : enchantments) + { + enchantmentBuilder.withEnchantment(enchantment); + } + this.builder.apply(enchantmentBuilder); + } + else + { + this.builder.apply(EnchantRandomlyFunction.randomApplicableEnchantment()); + } + return this; + } + + @Override + public Builder build() + { + return this.builder; + } + } + + /** + * Get the correct structure ID for the input base structure name. + * + * @param structureId the base structure name. + * @return the structure ID. + */ + public static ResourceLocation getStructureId(final ResourceLocation structureId) + { + return new ResourceLocation(Constants.MOD_ID, structureId.withPrefix("expeditions/structures/").getPath()); + } + + /** + * Create an adventure token loot item structure starts. + * + * @param structureId the structure id. + * @return the item builder. + */ + public static LootPoolSingletonContainer.Builder createStructureStartItem(final ResourceLocation structureId) + { + final CompoundTag structureStart = new CompoundTag(); + structureStart.putString(TOKEN_TAG_EXPEDITION_TYPE, TOKEN_TAG_EXPEDITION_TYPE_STRUCTURE_START); + structureStart.putString(TOKEN_TAG_EXPEDITION_STRUCTURE, structureId.toString()); + + return LootItem.lootTableItem(ModItems.adventureToken).apply(SetNbtFunction.setTag(structureStart)); + } + + /** + * Create an adventure token loot item structure ends. + * + * @param structureId the structure id. + * @return the item builder. + */ + public static LootPoolSingletonContainer.Builder createStructureEndItem(final ResourceLocation structureId) + { + final CompoundTag structureEnd = new CompoundTag(); + structureEnd.putString(TOKEN_TAG_EXPEDITION_TYPE, TOKEN_TAG_EXPEDITION_TYPE_STRUCTURE_END); + structureEnd.putString(TOKEN_TAG_EXPEDITION_STRUCTURE, structureId.toString()); + + return LootItem.lootTableItem(ModItems.adventureToken).apply(SetNbtFunction.setTag(structureEnd)); + } + + /** + * Create an adventure token loot item for encounters. + * + * @param encounterId the encounter id. + * @param weight the weight of the loot table entry. + * @return the item builder. + */ + public static DifficultyBuilder createEncounterLootItem(final ResourceLocation encounterId, final int weight) + { + return createEncounterLootItem(encounterId, weight, 1, true); + } + + /** + * Create an adventure token loot item for encounters. + * + * @param encounterId the encounter id. + * @param weight the weight of the loot table entry. + * @param amount the amount of encounters that will spawn. + * @param scale whether to scale the encounters with difficulty. + * @return the item builder. + */ + public static DifficultyBuilder createEncounterLootItem(final ResourceLocation encounterId, final int weight, final int amount, final boolean scale) + { + final CompoundTag encounter = new CompoundTag(); + encounter.putString(TOKEN_TAG_EXPEDITION_TYPE, TOKEN_TAG_EXPEDITION_TYPE_ENCOUNTER); + encounter.putString(TOKEN_TAG_EXPEDITION_ENCOUNTER, encounterId.toString()); + encounter.putInt(TOKEN_TAG_EXPEDITION_ENCOUNTER_AMOUNT, amount); + encounter.putBoolean(TOKEN_TAG_EXPEDITION_ENCOUNTER_SCALE, scale); + + return new SimpleItemBuilder(LootItem.lootTableItem(ModItems.adventureToken).setWeight(weight).apply(SetNbtFunction.setTag(encounter))); + } + + /** + * Create a loot table structure reference for the given structure name. + * + * @param structureId the base structure name. + * @param weight the weight of the loot table entry. + * @return the loot entry builder. + */ + public static DifficultyBuilder createStructureRef(final ResourceLocation structureId, final int weight) + { + return new SimpleItemBuilder(LootTableReference.lootTableReference(getStructureId(structureId)).setWeight(weight)); + } + + /** + * Create a potion loot item. + * + * @param potion the potion type. + * @param weight the weight of the loot table entry. + * @return the item builder. + */ + public static DifficultyBuilder createPotionItem(final Potion potion, final int weight) + { + final ItemStack stack = new ItemStack(Items.POTION); + PotionUtils.setPotion(stack, potion); + return new SimpleItemBuilder(LootItem.lootTableItem(Items.POTION).setWeight(weight).apply(SetNbtFunction.setTag(stack.getTag()))); + } + + /** + * Generate a simple loot item. + * + * @param item the item. + * @param weight the weight of the loot table entry. + * @return the builder + */ + public static BasicItemBuilder createSimpleItem(final Item item, final int weight) + { + return new SimpleItemBuilder(LootItem.lootTableItem(item).setWeight(weight)); + } + + /** + * Generate a simple loot tool item. + * + * @param item the item. + * @param weight the weight of the loot table entry. + * @return the builder + */ + public static EnchantItemBuilder createEnchantItem(final Item item, final int weight) + { + return new SimpleItemBuilder(LootItem.lootTableItem(item).setWeight(weight)); + } + + /** + * Generate a simple loot tool item. + * + * @param item the item. + * @param weight the weight of the loot table entry. + * @return the builder + */ + public static ToolItemBuilder createToolItem(final Item item, final int weight) + { + return new SimpleItemBuilder(LootItem.lootTableItem(item).setWeight(weight)); + } + + /** + * Root builder for expedition items. + */ + private interface RootBuilder + { + /** + * Return the original loot item builder for appending to a loot table builder. + * + * @return the loot item builder. + */ + Builder build(); + } + + /** + * Builder for difficulty based expedition items. + * + * @param the underlying builder implementation. + */ + public interface DifficultyBuilder> extends RootBuilder + { + /** + * Tell the item to only spawn on a given difficulty. + * + * @param difficulty the input difficulty. + * @return the builder + */ + T diffOnly(@NotNull final ColonyExpeditionTypeDifficulty difficulty); + + /** + * Tell the item to only spawn on a difficulties before or identical to the given difficulty. + * + * @param difficulty the input difficulty. + * @return the builder + */ + T diffBefore(@NotNull final ColonyExpeditionTypeDifficulty difficulty); + + /** + * Tell the item to only spawn on a difficulties after or identical to the given difficulty. + * + * @param difficulty the input difficulty. + * @return the builder + */ + T diffAfter(@NotNull final ColonyExpeditionTypeDifficulty difficulty); + } + + /** + * Builder for count based expedition items. + * + * @param the underlying builder implementation. + */ + public interface RarityBuilder> extends RootBuilder + { + /** + * Indicate the item is a common item, spawning many items in the output. + * + * @return the builder + */ + T common(); + + /** + * Indicate the item is an uncommon item, spawning few items in the output. + * + * @return the builder + */ + T uncommon(); + + /** + * Indicate the item is a rare item, spawning only one or two items in the output. + * + * @return the builder + */ + T rare(); + } + + /** + * Builder for basic expedition items. + */ + public interface BasicItemBuilder> extends DifficultyBuilder, RarityBuilder + { + } + + /** + * Builder for tool expedition items. + */ + public interface EnchantItemBuilder> extends BasicItemBuilder + { + /** + * Provides random enchantments on the given item. + * + * @param enchantments all possible enchantments on the tool, provide nothing for a random enchantment. + * @return the builder + */ + T enchant(Enchantment... enchantments); + } + + /** + * Builder for tool expedition items. + */ + public interface ToolItemBuilder> extends EnchantItemBuilder + { + /** + * Provide a low amount of damage to the tool, anywhere from 5% - 25%. + * + * @return the builder + */ + T damageLow(); + + /** + * Provide a medium amount of damage to the tool, anywhere from 25% - 50%. + * + * @return the builder + */ + T damageMid(); + } +} diff --git a/src/main/java/com/minecolonies/core/generation/defaults/DefaultColonyExpeditionLootProvider.java b/src/main/java/com/minecolonies/core/generation/defaults/DefaultColonyExpeditionLootProvider.java new file mode 100644 index 00000000000..2dcbfdb0674 --- /dev/null +++ b/src/main/java/com/minecolonies/core/generation/defaults/DefaultColonyExpeditionLootProvider.java @@ -0,0 +1,215 @@ +package com.minecolonies.core.generation.defaults; + +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.generation.SimpleLootTableProvider; +import net.minecraft.data.PackOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.storage.loot.*; +import net.minecraft.world.level.storage.loot.LootTable.Builder; +import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.function.Consumer; + +import static com.minecolonies.api.loot.ModLootConditions.EXPEDITION_PARAMS; +import static com.minecolonies.core.generation.ExpeditionResourceManager.*; +import static com.minecolonies.core.generation.defaults.DefaultExpeditionEncountersProvider.*; +import static com.minecolonies.core.generation.defaults.DefaultExpeditionStructureLootProvider.*; + +/** + * Loot table generator for expeditions. + */ +public class DefaultColonyExpeditionLootProvider extends SimpleLootTableProvider +{ + /** + * Expedition constants. + */ + public static final ResourceLocation EXPEDITION_OVERWORLD_LOOT = new ResourceLocation(Constants.MOD_ID, "expeditions/expedition_overworld"); + public static final ResourceLocation EXPEDITION_NETHER_LOOT = new ResourceLocation(Constants.MOD_ID, "expeditions/expedition_nether"); + public static final ResourceLocation EXPEDITION_END_LOOT = new ResourceLocation(Constants.MOD_ID, "expeditions/expedition_end"); + + /** + * Default constructor. + */ + public DefaultColonyExpeditionLootProvider(final PackOutput output) + { + super(output); + } + + @Override + @NotNull + public String getName() + { + return "Expedition Loot"; + } + + @Override + protected void registerTables(final @NotNull LootTableRegistrar registrar) + { + createExpeditionLootTable(EXPEDITION_OVERWORLD_LOOT, registrar, builder -> builder.withPool( + new LootPool.Builder() + .setRolls(UniformGenerator.between(10, 20)) + // Logs + .add(createSimpleItem(Items.OAK_LOG, 100).common().build()) + .add(createSimpleItem(Items.BIRCH_LOG, 100).common().build()) + .add(createSimpleItem(Items.SPRUCE_LOG, 100).common().build()) + .add(createSimpleItem(Items.DARK_OAK_LOG, 100).common().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.ACACIA_LOG, 100).common().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.CHERRY_LOG, 100).common().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.MANGROVE_LOG, 100).common().diffAfter(DIFF_2).build()) + // Ores and gems + .add(createSimpleItem(Items.COAL, 100).common().build()) + .add(createSimpleItem(Items.RAW_COPPER, 100).common().build()) + .add(createSimpleItem(Items.RAW_IRON, 100).common().build()) + .add(createSimpleItem(Items.RAW_GOLD, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.REDSTONE, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.LAPIS_LAZULI, 50).uncommon().diffAfter(DIFF_2).build()) + // Flowers + .add(createSimpleItem(Items.POPPY, 100).common().build()) + .add(createSimpleItem(Items.ROSE_BUSH, 100).common().build()) + .add(createSimpleItem(Items.DANDELION, 100).common().build()) + .add(createSimpleItem(Items.DANDELION, 100).common().build()) + .add(createSimpleItem(Items.CORNFLOWER, 100).common().build()) + .add(createSimpleItem(Items.AZURE_BLUET, 100).common().build()) + .add(createSimpleItem(Items.OXEYE_DAISY, 100).common().build()) + .add(createSimpleItem(Items.LILY_OF_THE_VALLEY, 100).common().build()) + .add(createSimpleItem(Items.RED_TULIP, 80).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.PINK_TULIP, 80).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.ORANGE_TULIP, 80).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.WHITE_TULIP, 80).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.SUNFLOWER, 80).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.TORCHFLOWER, 50).rare().diffAfter(DIFF_3).build()) + .add(createSimpleItem(Items.ALLIUM, 50).rare().diffAfter(DIFF_3).build()) + .add(createSimpleItem(Items.BLUE_ORCHID, 50).rare().diffAfter(DIFF_3).build()) + .add(createSimpleItem(Items.PITCHER_PLANT, 50).rare().diffAfter(DIFF_3).build()) + // Mushrooms + .add(createSimpleItem(Items.BROWN_MUSHROOM, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.RED_MUSHROOM, 50).uncommon().diffAfter(DIFF_2).build()) + // Structures - Friendly + .add(createStructureRef(VILLAGE_DESERT_ID, 50).diffBefore(DIFF_2).build()) + .add(createStructureRef(VILLAGE_PLAINS_ID, 50).diffBefore(DIFF_2).build()) + .add(createStructureRef(VILLAGE_SAVANNA_ID, 50).diffBefore(DIFF_2).build()) + .add(createStructureRef(VILLAGE_SNOWY_ID, 50).diffBefore(DIFF_2).build()) + .add(createStructureRef(VILLAGE_TAIGA_ID, 50).diffBefore(DIFF_2).build()) + .add(createStructureRef(IGLOO_ID, 50).diffBefore(DIFF_2).build()) + // Structures - Enemy - Simple - Overground + .add(createStructureRef(RUINED_PORTAL_ID, 25).diffBefore(DIFF_2).build()) + .add(createStructureRef(PILLAGER_OUTPOST_ID, 25).diffBefore(DIFF_2).build()) + .add(createStructureRef(DESERT_PYRAMID_ID, 25).diffBefore(DIFF_2).build()) + .add(createStructureRef(JUNGLE_TEMPLE_ID, 25).diffBefore(DIFF_2).build()) + .add(createStructureRef(SWAMP_HUT_ID, 25).diffBefore(DIFF_2).build()) + // Structures - Enemy - Simple - Underground/water + .add(createStructureRef(MINESHAFT_ID, 25).diffAfter(DIFF_2).build()) + .add(createStructureRef(SHIPWRECK_ID, 25).diffAfter(DIFF_2).build()) + .add(createStructureRef(BURIED_TREASURE_ID, 25).diffAfter(DIFF_2).build()) + // Structures - Enemy - Difficult + .add(createStructureRef(STRONGHOLD_ID, 25).diffAfter(DIFF_3).build()) + .add(createStructureRef(MONUMENT_ID, 25).diffAfter(DIFF_3).build()) + .add(createStructureRef(MANSION_ID, 25).diffAfter(DIFF_3).build()) + .add(createStructureRef(ANCIENT_CITY_ID, 25).diffAfter(DIFF_3).build()) + // Encounters + .add(createEncounterLootItem(ZOMBIE, 100).build()) + .add(createEncounterLootItem(SKELETON, 50).build()) + .add(createEncounterLootItem(CREEPER, 30).build()) + .add(createEncounterLootItem(SPIDER, 30).build()) + .add(createEncounterLootItem(CAVE_SPIDER, 30).build()) + .add(createEncounterLootItem(ENDERMAN, 15).build()) + )); + + createExpeditionLootTable(EXPEDITION_NETHER_LOOT, registrar, builder -> builder.withPool( + new LootPool.Builder() + .setRolls(UniformGenerator.between(10, 20)) + // Blocks + .add(createSimpleItem(Items.NETHERRACK, 100).common().build()) + .add(createSimpleItem(Items.BLACKSTONE, 100).common().build()) + .add(createSimpleItem(Items.BASALT, 100).common().build()) + .add(createSimpleItem(Items.SOUL_SAND, 100).common().build()) + .add(createSimpleItem(Items.SOUL_SOIL, 100).common().build()) + .add(createSimpleItem(Items.GRAVEL, 100).common().build()) + .add(createSimpleItem(Items.CRIMSON_NYLIUM, 100).common().build()) + .add(createSimpleItem(Items.WARPED_NYLIUM, 100).common().build()) + .add(createSimpleItem(Items.MAGMA_BLOCK, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.OBSIDIAN, 50).uncommon().diffAfter(DIFF_2).build()) + // Ores and gems + .add(createSimpleItem(Items.GOLD_NUGGET, 100).common().build()) + .add(createSimpleItem(Items.QUARTZ, 100).common().build()) + .add(createSimpleItem(Items.GLOWSTONE, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.ANCIENT_DEBRIS, 25).rare().diffAfter(DIFF_3).build()) + // Flowers + .add(createSimpleItem(Items.BROWN_MUSHROOM, 100).common().build()) + .add(createSimpleItem(Items.RED_MUSHROOM, 100).common().build()) + .add(createSimpleItem(Items.CRIMSON_FUNGUS, 100).common().build()) + .add(createSimpleItem(Items.WARPED_FUNGUS, 100).common().build()) + .add(createSimpleItem(Items.CRIMSON_STEM, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.WARPED_STEM, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.SHROOMLIGHT, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.TWISTING_VINES, 50).uncommon().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.WEEPING_VINES, 50).uncommon().diffAfter(DIFF_2).build()) + // Structures - Friendly + .add(createStructureRef(NETHER_FOSSIL_ID, 50).diffBefore(DIFF_2).build()) + // Structures - Enemy + .add(createStructureRef(FORTRESS_ID, 25).diffAfter(DIFF_2).build()) + .add(createStructureRef(BASTION_REMNANT_ID, 25).diffAfter(DIFF_2).build()) + // Encounters + .add(createEncounterLootItem(ZOMBIFIED_PIGLIN, 100).build()) + .add(createEncounterLootItem(PIGLIN, 50).build()) + .add(createEncounterLootItem(HOGLIN, 30).build()) + .add(createEncounterLootItem(WITHER_SKELETON, 30).build()) + .add(createEncounterLootItem(BLAZE, 30).build()) + )); + + createExpeditionLootTable(EXPEDITION_END_LOOT, registrar, builder -> builder.withPool( + new LootPool.Builder() + .setRolls(UniformGenerator.between(10, 20)) + // Blocks + .add(createSimpleItem(Items.END_STONE, 100).common().build()) + .add(createSimpleItem(Items.OBSIDIAN, 50).uncommon().build()) + // Plants + .add(createSimpleItem(Items.CHORUS_PLANT, 100).common().build()) + .add(createSimpleItem(Items.CHORUS_FLOWER, 100).common().build()) + // Structures - Enemy + .add(createStructureRef(END_CITY_ID, 25).diffAfter(DIFF_2).build()) + .add(createStructureRef(BASTION_REMNANT_ID, 25).diffAfter(DIFF_2).build()) + // Encounters + .add(createEncounterLootItem(ENDERMAN, 100).build()) + )); + } + + @Override + @SuppressWarnings("unchecked") + protected void validate(final @NotNull Map map, final @NotNull ValidationContext validationTracker) + { + ValidationContext newTracker = new ValidationContext(EXPEDITION_PARAMS, new LootDataResolver() + { + @Nullable + public T getElement(@NotNull LootDataId id) + { + if (id.location().getPath().startsWith("expeditions/structures")) + { + return (T) map.get(id.location()); + } + return null; + } + }); + + super.validate(map, newTracker); + } + + /** + * Simple builder to automatically build an expedition loot table. + * + * @param id the id of the expedition. + * @param registrar the loot table registrar. + * @param configure the further configuration handler. + */ + private void createExpeditionLootTable(final ResourceLocation id, final @NotNull LootTableRegistrar registrar, final Consumer configure) + { + final Builder builder = new Builder(); + configure.accept(builder); + + registrar.register(id, EXPEDITION_PARAMS, builder); + } +} diff --git a/src/main/java/com/minecolonies/core/generation/defaults/DefaultColonyExpeditionTypesProvider.java b/src/main/java/com/minecolonies/core/generation/defaults/DefaultColonyExpeditionTypesProvider.java new file mode 100644 index 00000000000..d909098cf25 --- /dev/null +++ b/src/main/java/com/minecolonies/core/generation/defaults/DefaultColonyExpeditionTypesProvider.java @@ -0,0 +1,199 @@ +package com.minecolonies.core.generation.defaults; + +import com.minecolonies.api.equipment.ModEquipmentTypes; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeBuilder; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeDifficulty; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeParser; +import net.minecraft.data.CachedOutput; +import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; +import net.minecraft.data.PackOutput.PathProvider; +import net.minecraft.data.PackOutput.Target; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import static com.minecolonies.core.generation.defaults.DefaultColonyExpeditionLootProvider.*; + +/** + * Generator for expedition types. + */ +public class DefaultColonyExpeditionTypesProvider implements DataProvider +{ + /** + * Expedition type constants. + */ + public static final ResourceLocation OVERWORLD_EASY = new ResourceLocation(Constants.MOD_ID, "overworld_easy"); + public static final ResourceLocation OVERWORLD_MEDIUM = new ResourceLocation(Constants.MOD_ID, "overworld_medium"); + public static final ResourceLocation OVERWORLD_HARD = new ResourceLocation(Constants.MOD_ID, "overworld_hard"); + public static final ResourceLocation NETHER_EASY = new ResourceLocation(Constants.MOD_ID, "nether_easy"); + public static final ResourceLocation NETHER_MEDIUM = new ResourceLocation(Constants.MOD_ID, "nether_medium"); + public static final ResourceLocation NETHER_HARD = new ResourceLocation(Constants.MOD_ID, "nether_hard"); + public static final ResourceLocation END_MEDIUM = new ResourceLocation(Constants.MOD_ID, "end_medium"); + public static final ResourceLocation END_HARD = new ResourceLocation(Constants.MOD_ID, "end_hard"); + + /** + * The pack output path generator. + */ + private final PackOutput output; + + /** + * Default constructor. + */ + public DefaultColonyExpeditionTypesProvider(final PackOutput output) + { + this.output = output; + } + + /** + * Generate all expedition type instances. + * + * @return the expedition types collection. + */ + private List generateTypes() + { + final List types = new ArrayList<>(); + + types.add(new ColonyExpeditionTypeBuilder(OVERWORLD_EASY, Level.OVERWORLD, EXPEDITION_OVERWORLD_LOOT) + .setName("com.minecolonies.core.expedition_types.overworld.name") + .setToText("com.minecolonies.core.expedition_types.overworld.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.EASY) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(32) + .setGuards(1)); + + types.add(new ColonyExpeditionTypeBuilder(OVERWORLD_MEDIUM, Level.OVERWORLD, EXPEDITION_OVERWORLD_LOOT) + .setName("com.minecolonies.core.expedition_types.overworld.name") + .setToText("com.minecolonies.core.expedition_types.overworld.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.MEDIUM) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(64) + .setGuards(2)); + + types.add(new ColonyExpeditionTypeBuilder(OVERWORLD_HARD, Level.OVERWORLD, EXPEDITION_OVERWORLD_LOOT) + .setName("com.minecolonies.core.expedition_types.overworld.name") + .setToText("com.minecolonies.core.expedition_types.overworld.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.HARD) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(128) + .setGuards(4)); + + types.add(new ColonyExpeditionTypeBuilder(NETHER_EASY, Level.NETHER, EXPEDITION_NETHER_LOOT) + .setName("com.minecolonies.core.expedition_types.nether.name") + .setToText("com.minecolonies.core.expedition_types.nether.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.EASY) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(32) + .setGuards(2)); + + types.add(new ColonyExpeditionTypeBuilder(NETHER_MEDIUM, Level.NETHER, EXPEDITION_NETHER_LOOT) + .setName("com.minecolonies.core.expedition_types.nether.name") + .setToText("com.minecolonies.core.expedition_types.nether.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.MEDIUM) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(64) + .setGuards(4)); + + types.add(new ColonyExpeditionTypeBuilder(NETHER_HARD, Level.NETHER, EXPEDITION_NETHER_LOOT) + .setName("com.minecolonies.core.expedition_types.nether.name") + .setToText("com.minecolonies.core.expedition_types.nether.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.HARD) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(128) + .setGuards(6)); + + types.add(new ColonyExpeditionTypeBuilder(END_MEDIUM, Level.END, EXPEDITION_END_LOOT) + .setName("com.minecolonies.core.expedition_types.end.name") + .setToText("com.minecolonies.core.expedition_types.end.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.MEDIUM) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(64) + .setGuards(4)); + + types.add(new ColonyExpeditionTypeBuilder(END_HARD, Level.END, EXPEDITION_END_LOOT) + .setName("com.minecolonies.core.expedition_types.end.name") + .setToText("com.minecolonies.core.expedition_types.end.to_text") + .setDifficulty(ColonyExpeditionTypeDifficulty.HARD) + .addEquipmentRequirement(ModEquipmentTypes.sword.get()) + .addEquipmentRequirement(ModEquipmentTypes.pickaxe.get()) + .addEquipmentRequirement(ModEquipmentTypes.shovel.get()) + .addEquipmentRequirement(ModEquipmentTypes.helmet.get()) + .addEquipmentRequirement(ModEquipmentTypes.chestplate.get()) + .addEquipmentRequirement(ModEquipmentTypes.leggings.get()) + .addEquipmentRequirement(ModEquipmentTypes.boots.get()) + .addFoodRequirement(128) + .setGuards(6)); + + return types; + } + + @Override + @NotNull + public CompletableFuture run(final @NotNull CachedOutput cachedOutput) + { + final PathProvider pathProvider = output.createPathProvider(Target.DATA_PACK, "colony/expedition_types"); + + final List colonyExpeditionTypes = generateTypes(); + final CompletableFuture[] futures = new CompletableFuture[colonyExpeditionTypes.size()]; + for (int i = 0; i < colonyExpeditionTypes.size(); i++) + { + futures[i] = + DataProvider.saveStable(cachedOutput, ColonyExpeditionTypeParser.toJson(colonyExpeditionTypes.get(i)), pathProvider.json(colonyExpeditionTypes.get(i).getId())); + } + return CompletableFuture.allOf(futures); + } + + @Override + @NotNull + public String getName() + { + return "Expedition Types Provider"; + } +} diff --git a/src/main/java/com/minecolonies/core/generation/defaults/DefaultExpeditionEncountersProvider.java b/src/main/java/com/minecolonies/core/generation/defaults/DefaultExpeditionEncountersProvider.java new file mode 100644 index 00000000000..3e5b68150a5 --- /dev/null +++ b/src/main/java/com/minecolonies/core/generation/defaults/DefaultExpeditionEncountersProvider.java @@ -0,0 +1,137 @@ +package com.minecolonies.core.generation.defaults; + +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.colony.expeditions.encounters.ExpeditionEncounter; +import com.minecolonies.core.colony.expeditions.encounters.ExpeditionEncounterParser; +import net.minecraft.data.CachedOutput; +import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; +import net.minecraft.data.PackOutput.PathProvider; +import net.minecraft.data.PackOutput.Target; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * Generator for expedition encounters. + */ +public class DefaultExpeditionEncountersProvider implements DataProvider +{ + /** + * Expedition encounter constants. + */ + public static final ResourceLocation ZOMBIE = new ResourceLocation(Constants.MOD_ID, "zombie"); + public static final ResourceLocation SKELETON = new ResourceLocation(Constants.MOD_ID, "skeleton"); + public static final ResourceLocation CREEPER = new ResourceLocation(Constants.MOD_ID, "creeper"); + public static final ResourceLocation SPIDER = new ResourceLocation(Constants.MOD_ID, "spider"); + public static final ResourceLocation CAVE_SPIDER = new ResourceLocation(Constants.MOD_ID, "cave_spider"); + public static final ResourceLocation ENDERMAN = new ResourceLocation(Constants.MOD_ID, "enderman"); + public static final ResourceLocation SHULKER = new ResourceLocation(Constants.MOD_ID, "shulker"); + public static final ResourceLocation PIGLIN = new ResourceLocation(Constants.MOD_ID, "piglin"); + public static final ResourceLocation PIGLIN_BRUTE = new ResourceLocation(Constants.MOD_ID, "piglin_brute"); + public static final ResourceLocation HOGLIN = new ResourceLocation(Constants.MOD_ID, "hoglin"); + public static final ResourceLocation DROWNED = new ResourceLocation(Constants.MOD_ID, "drowned"); + public static final ResourceLocation DROWNED_TRIDENT = new ResourceLocation(Constants.MOD_ID, "drowned_trident"); + public static final ResourceLocation BLAZE = new ResourceLocation(Constants.MOD_ID, "blaze"); + public static final ResourceLocation MAGMA_CUBE_SMALL = new ResourceLocation(Constants.MOD_ID, "magma_cube_small"); + public static final ResourceLocation MAGMA_CUBE_MEDIUM = new ResourceLocation(Constants.MOD_ID, "magma_cube_medium"); + public static final ResourceLocation MAGMA_CUBE_LARGE = new ResourceLocation(Constants.MOD_ID, "magma_cube_large"); + public static final ResourceLocation WITHER_SKELETON = new ResourceLocation(Constants.MOD_ID, "wither_skeleton"); + public static final ResourceLocation ZOMBIFIED_PIGLIN = new ResourceLocation(Constants.MOD_ID, "zombified_piglin"); + public static final ResourceLocation VINDICATOR = new ResourceLocation(Constants.MOD_ID, "vindicator"); + public static final ResourceLocation EVOKER = new ResourceLocation(Constants.MOD_ID, "evoker"); + public static final ResourceLocation VEX = new ResourceLocation(Constants.MOD_ID, "vex"); + public static final ResourceLocation GUARDIAN = new ResourceLocation(Constants.MOD_ID, "guardian"); + public static final ResourceLocation ELDER_GUARDIAN = new ResourceLocation(Constants.MOD_ID, "elder_guardian"); + public static final ResourceLocation PILLAGER = new ResourceLocation(Constants.MOD_ID, "pillager"); + public static final ResourceLocation PILLAGER_CAPTAIN = new ResourceLocation(Constants.MOD_ID, "pillager_captain"); + public static final ResourceLocation WITCH = new ResourceLocation(Constants.MOD_ID, "witch"); + + /** + * Boss encounter constants. + */ + public static final ResourceLocation WARDEN = new ResourceLocation(Constants.MOD_ID, "warden"); + + /** + * The pack output path generator. + */ + private final PackOutput output; + + /** + * Default constructor. + */ + public DefaultExpeditionEncountersProvider(final PackOutput output) + { + this.output = output; + } + + /** + * Generate all expedition encounter instances. + * + * @return the expedition encounters collection. + */ + private List generateTypes() + { + final List types = new ArrayList<>(); + + // Standard mobs + types.add(new ExpeditionEncounter(ZOMBIE, EntityType.ZOMBIE, 2.5f, 0, 20, 0, EntityType.ZOMBIE.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(SKELETON, EntityType.SKELETON, 3f, 0, 20, 0, EntityType.SKELETON.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(CREEPER, EntityType.CREEPER, 22f, 20, 20, 0, EntityType.CREEPER.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(SPIDER, EntityType.SPIDER, 2f, 0, 16, 0, EntityType.SPIDER.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(CAVE_SPIDER, EntityType.CAVE_SPIDER, 4f, 0, 12, 0, EntityType.CAVE_SPIDER.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(ENDERMAN, EntityType.ENDERMAN, 7f, 0, 40, 0, EntityType.ENDERMAN.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(SHULKER, EntityType.SHULKER, 4f, 0, 30, 0, EntityType.SHULKER.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(PIGLIN, EntityType.PIGLIN, 8f, 0, 16, 0, EntityType.PIGLIN.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(PIGLIN_BRUTE, EntityType.PIGLIN_BRUTE, 13f, 0, 50, 0, EntityType.PIGLIN_BRUTE.getDefaultLootTable(), 20)); + types.add(new ExpeditionEncounter(HOGLIN, EntityType.HOGLIN, 8f, 0, 40, 0, EntityType.HOGLIN.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(DROWNED, EntityType.DROWNED, 3f, 0, 20, 0, EntityType.DROWNED.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(DROWNED_TRIDENT, EntityType.DROWNED, 11f, 0, 20, 0, EntityType.DROWNED.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(BLAZE, EntityType.BLAZE, 6f, 0, 20, 0, EntityType.BLAZE.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(MAGMA_CUBE_SMALL, EntityType.MAGMA_CUBE, 3f, 0, 1, 0, EntityType.MAGMA_CUBE.getDefaultLootTable(), 1)); + types.add(new ExpeditionEncounter(MAGMA_CUBE_MEDIUM, EntityType.MAGMA_CUBE, 4f, 0, 4, 0, EntityType.MAGMA_CUBE.getDefaultLootTable(), 2)); + types.add(new ExpeditionEncounter(MAGMA_CUBE_LARGE, EntityType.MAGMA_CUBE, 6f, 0, 16, 0, EntityType.MAGMA_CUBE.getDefaultLootTable(), 4)); + types.add(new ExpeditionEncounter(WITHER_SKELETON, EntityType.WITHER_SKELETON, 11f, 0, 20, 0, EntityType.WITHER_SKELETON.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(ZOMBIFIED_PIGLIN, EntityType.ZOMBIFIED_PIGLIN, 11f, 0, 20, 0, EntityType.ZOMBIFIED_PIGLIN.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(VINDICATOR, EntityType.VINDICATOR, 13f, 0, 24, 0, EntityType.VINDICATOR.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(EVOKER, EntityType.EVOKER, 6f, 0, 24, 0, EntityType.EVOKER.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(VEX, EntityType.VEX, 9f, 0, 14, 0, EntityType.VEX.getDefaultLootTable(), 5)); + types.add(new ExpeditionEncounter(GUARDIAN, EntityType.GUARDIAN, 6f, 0, 30, 0, EntityType.GUARDIAN.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(ELDER_GUARDIAN, EntityType.ELDER_GUARDIAN, 8f, 0, 80, 0, EntityType.ELDER_GUARDIAN.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(PILLAGER, EntityType.PILLAGER, 8f, 0, 80, 0, EntityType.PILLAGER.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(PILLAGER_CAPTAIN, EntityType.PILLAGER, 8f, 0, 80, 0, EntityType.PILLAGER.getDefaultLootTable(), 10)); + types.add(new ExpeditionEncounter(WITCH, EntityType.WITCH, 4f, 0, 26, 0, EntityType.WITCH.getDefaultLootTable(), 10)); + + // Boss mobs + types.add(new ExpeditionEncounter(WARDEN, EntityType.WARDEN, 30f, 0, 500, 0, EntityType.WARDEN.getDefaultLootTable(), 5)); + + return types; + } + + @Override + @NotNull + public CompletableFuture run(final @NotNull CachedOutput cachedOutput) + { + final PathProvider pathProvider = output.createPathProvider(Target.DATA_PACK, "colony/expedition_encounters"); + + final List expeditionEncounters = generateTypes(); + final CompletableFuture[] futures = new CompletableFuture[expeditionEncounters.size()]; + for (int i = 0; i < expeditionEncounters.size(); i++) + { + futures[i] = + DataProvider.saveStable(cachedOutput, ExpeditionEncounterParser.toJson(expeditionEncounters.get(i)), pathProvider.json(expeditionEncounters.get(i).id())); + } + return CompletableFuture.allOf(futures); + } + + @Override + @NotNull + public String getName() + { + return "Expedition Encounters Provider"; + } +} diff --git a/src/main/java/com/minecolonies/core/generation/defaults/DefaultExpeditionStructureLootProvider.java b/src/main/java/com/minecolonies/core/generation/defaults/DefaultExpeditionStructureLootProvider.java new file mode 100644 index 00000000000..fd7c5915bdb --- /dev/null +++ b/src/main/java/com/minecolonies/core/generation/defaults/DefaultExpeditionStructureLootProvider.java @@ -0,0 +1,471 @@ +package com.minecolonies.core.generation.defaults; + +import com.minecolonies.api.blocks.ModBlocks; +import com.minecolonies.api.items.ModItems; +import com.minecolonies.api.items.ModTags; +import com.minecolonies.core.blocks.MinecoloniesCropBlock; +import com.minecolonies.core.generation.SimpleLootTableProvider; +import net.minecraft.data.PackOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.alchemy.Potions; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.storage.loot.LootPool; +import net.minecraft.world.level.storage.loot.LootTable.Builder; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet; +import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; +import net.minecraft.world.level.storage.loot.providers.number.NumberProvider; +import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import static com.minecolonies.api.loot.ModLootConditions.EXPEDITION_DIFFICULTY_PARAM; +import static com.minecolonies.core.generation.ExpeditionResourceManager.*; +import static com.minecolonies.core.generation.defaults.DefaultExpeditionEncountersProvider.*; + +/** + * Loot table generator for expeditions. + */ +public class DefaultExpeditionStructureLootProvider extends SimpleLootTableProvider +{ + /** + * Expedition structure constants. + */ + public static final ResourceLocation ANCIENT_CITY_ID = new ResourceLocation("ancient_city"); + public static final ResourceLocation BASTION_REMNANT_ID = new ResourceLocation("bastion_remnant"); + public static final ResourceLocation BURIED_TREASURE_ID = new ResourceLocation("buried_treasure"); + public static final ResourceLocation END_CITY_ID = new ResourceLocation("end_city"); + public static final ResourceLocation FORTRESS_ID = new ResourceLocation("fortress"); + public static final ResourceLocation MANSION_ID = new ResourceLocation("mansion"); + public static final ResourceLocation MINESHAFT_ID = new ResourceLocation("mineshaft"); + public static final ResourceLocation MONUMENT_ID = new ResourceLocation("monument"); + public static final ResourceLocation NETHER_FOSSIL_ID = new ResourceLocation("nether_fossil"); + public static final ResourceLocation PILLAGER_OUTPOST_ID = new ResourceLocation("pillager_outpost"); + public static final ResourceLocation RUINED_PORTAL_ID = new ResourceLocation("ruined_portal"); + public static final ResourceLocation SHIPWRECK_ID = new ResourceLocation("shipwreck"); + public static final ResourceLocation STRONGHOLD_ID = new ResourceLocation("stronghold"); + public static final ResourceLocation DESERT_PYRAMID_ID = new ResourceLocation("desert_pyramid"); + public static final ResourceLocation IGLOO_ID = new ResourceLocation("igloo"); + public static final ResourceLocation JUNGLE_TEMPLE_ID = new ResourceLocation("jungle_temple"); + public static final ResourceLocation SWAMP_HUT_ID = new ResourceLocation("swamp_hut"); + public static final ResourceLocation VILLAGE_DESERT_ID = new ResourceLocation("village_desert"); + public static final ResourceLocation VILLAGE_PLAINS_ID = new ResourceLocation("village_plains"); + public static final ResourceLocation VILLAGE_SAVANNA_ID = new ResourceLocation("village_savanna"); + public static final ResourceLocation VILLAGE_SNOWY_ID = new ResourceLocation("village_snowy"); + public static final ResourceLocation VILLAGE_TAIGA_ID = new ResourceLocation("village_taiga"); + + /** + * Number providers. + */ + private static final NumberProvider COMMON_STRUCTURE_ROLLS = UniformGenerator.between(5, 8); + private static final NumberProvider UNCOMMON_STRUCTURE_ROLLS = UniformGenerator.between(3, 5); + private static final NumberProvider SPECIAL_STRUCTURE_ROLLS = UniformGenerator.between(1, 3); + + /** + * Default constructor. + */ + public DefaultExpeditionStructureLootProvider(final PackOutput output) + { + super(output); + } + + @Override + @NotNull + public String getName() + { + return "Expedition Structure Loot"; + } + + /** + * Simple builder to automatically build a structure loot table. + * + * @param id the id of the structure. + * @param registrar the loot table registrar. + * @param configure the further configuration handler. + */ + private void createStructureLootTable(final ResourceLocation id, final @NotNull LootTableRegistrar registrar, final Consumer configure) + { + final NumberProvider singleRoll = ConstantValue.exactly(1); + + final Builder builder = new Builder(); + builder.withPool(new LootPool.Builder().setRolls(singleRoll).add(createStructureStartItem(id))); + configure.accept(builder); + builder.withPool(new LootPool.Builder().setRolls(singleRoll).add(createStructureEndItem(id))); + + final LootContextParamSet paramSet = LootContextParamSet.builder().required(EXPEDITION_DIFFICULTY_PARAM).build(); + + registrar.register(getStructureId(id), paramSet, builder); + } + + @Override + protected void registerTables(final @NotNull LootTableRegistrar registrar) + { + createStructureLootTable(ANCIENT_CITY_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(SPECIAL_STRUCTURE_ROLLS) + // Blocks + .add(createSimpleItem(Items.DEEPSLATE_BRICK_SLAB, 100).common().build()) + // Items + .add(createSimpleItem(Items.BOOK, 100).common().build()) + .add(createSimpleItem(Items.NAME_TAG, 75).build()) + .add(createSimpleItem(Items.LEAD, 75).build()) + .add(createSimpleItem(Items.EXPERIENCE_BOTTLE, 40).build()) + .add(createSimpleItem(Items.SCULK, 40).build()) + .add(createSimpleItem(Items.SCULK_SENSOR, 30).build()) + .add(createSimpleItem(Items.SCULK_CATALYST, 20).build()) + // Tools + .add(createToolItem(Items.IRON_LEGGINGS, 40).damageLow().enchant().build()) + .add(createToolItem(Items.DIAMOND_LEGGINGS, 5).damageMid().enchant().diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.ENCHANTED_GOLDEN_APPLE, 5).diffAfter(DIFF_3).build()) + .add(createEnchantItem(Items.ENCHANTED_BOOK, 5).enchant(Enchantments.MENDING).diffAfter(DIFF_3).build()) + // Mob encounters + .add(createEncounterLootItem(WARDEN, 10).diffAfter(DIFF_3).build()) + )); + + createStructureLootTable(BASTION_REMNANT_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + // Blocks + .add(createSimpleItem(Items.POLISHED_BLACKSTONE_BRICKS, 100).common().build()) + .add(createSimpleItem(Items.GOLD_BLOCK, 50).build()) + .add(createSimpleItem(Items.CRYING_OBSIDIAN, 25).build()) + .add(createSimpleItem(Items.GILDED_BLACKSTONE, 15).diffAfter(DIFF_3).build()) + // Items + .add(createSimpleItem(Items.IRON_INGOT, 100).uncommon().build()) + .add(createSimpleItem(Items.GOLD_INGOT, 100).uncommon().build()) + .add(createSimpleItem(Items.SPECTRAL_ARROW, 50).build()) + .add(createSimpleItem(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, 5).diffAfter(DIFF_3).build()) + // Tools + .add(createToolItem(Items.CROSSBOW, 100).damageLow().build()) + // Mob encounters + .add(createEncounterLootItem(PIGLIN, 150).build()) + .add(createEncounterLootItem(PIGLIN_BRUTE, 150).build()) + .add(createEncounterLootItem(HOGLIN, 100).diffAfter(DIFF_3).build()) + )); + + createStructureLootTable(BURIED_TREASURE_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.COOKED_COD, 100).build()) + .add(createSimpleItem(Items.IRON_INGOT, 75).uncommon().build()) + .add(createSimpleItem(Items.GOLD_INGOT, 75).rare().build()) + .add(createSimpleItem(Items.DIAMOND, 25).diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.EMERALD, 25).diffAfter(DIFF_2).build()) + .add(createPotionItem(Potions.WATER_BREATHING, 15).diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.HEART_OF_THE_SEA, 5).diffAfter(DIFF_3).build()) + .add(createEncounterLootItem(DROWNED, 100).build()) + )); + + createStructureLootTable(END_CITY_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.END_STONE_BRICKS, 100).common().build()) + .add(createSimpleItem(Items.PURPUR_BLOCK, 100).common().build()) + .add(createSimpleItem(Items.IRON_INGOT, 200).uncommon().build()) + .add(createSimpleItem(Items.GOLD_INGOT, 200).rare().build()) + .add(createSimpleItem(Items.SADDLE, 100).build()) + .add(createSimpleItem(Items.DIAMOND, 50).build()) + .add(createSimpleItem(Items.EMERALD, 50).build()) + .add(createToolItem(Items.IRON_HELMET, 25).enchant().damageLow().build()) + .add(createToolItem(Items.IRON_CHESTPLATE, 25).enchant().damageLow().build()) + .add(createToolItem(Items.IRON_LEGGINGS, 25).enchant().damageLow().build()) + .add(createToolItem(Items.IRON_BOOTS, 25).enchant().damageLow().build()) + .add(createToolItem(Items.IRON_SWORD, 25).enchant().damageLow().build()) + .add(createToolItem(Items.IRON_PICKAXE, 25).enchant().damageLow().build()) + .add(createToolItem(Items.IRON_SHOVEL, 25).enchant().damageLow().build()) + .add(createToolItem(Items.DIAMOND_HELMET, 5).enchant().damageMid().diffAfter(DIFF_3).build()) + .add(createToolItem(Items.DIAMOND_CHESTPLATE, 5).enchant().damageMid().diffAfter(DIFF_3).build()) + .add(createToolItem(Items.DIAMOND_LEGGINGS, 5).enchant().damageMid().diffAfter(DIFF_3).build()) + .add(createToolItem(Items.DIAMOND_BOOTS, 5).enchant().damageMid().diffAfter(DIFF_3).build()) + .add(createToolItem(Items.DIAMOND_SWORD, 5).enchant().damageMid().diffAfter(DIFF_3).build()) + .add(createToolItem(Items.DIAMOND_PICKAXE, 5).enchant().damageMid().diffAfter(DIFF_3).build()) + .add(createToolItem(Items.DIAMOND_SHOVEL, 5).enchant().damageMid().diffAfter(DIFF_3).build()) + .add(createSimpleItem(Items.ELYTRA, 1).diffAfter(DIFF_3).build()) + .add(createEncounterLootItem(ENDERMAN, 200).build()) + .add(createEncounterLootItem(SHULKER, 300).build()) + )); + + createStructureLootTable(FORTRESS_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.NETHER_BRICKS, 100).common().build()) + .add(createSimpleItem(Items.IRON_INGOT, 100).uncommon().build()) + .add(createSimpleItem(Items.GOLD_INGOT, 100).rare().build()) + .add(createSimpleItem(Items.NETHER_WART, 75).uncommon().build()) + .add(createSimpleItem(Items.OBSIDIAN, 60).build()) + .add(createSimpleItem(Items.DIAMOND, 50).build()) + .add(createSimpleItem(Items.SADDLE, 50).build()) + .add(createEncounterLootItem(ZOMBIFIED_PIGLIN, 20).build()) + .add(createEncounterLootItem(SKELETON, 20).build()) + .add(createEncounterLootItem(WITHER_SKELETON, 10).build()) + .add(createEncounterLootItem(BLAZE, 10).build()) + .add(createEncounterLootItem(MAGMA_CUBE_LARGE, 5).build()) + .add(createEncounterLootItem(MAGMA_CUBE_MEDIUM, 10).build()) + .add(createEncounterLootItem(MAGMA_CUBE_SMALL, 20).build()) + )); + + createStructureLootTable(MANSION_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.IRON_INGOT, 100).uncommon().build()) + .add(createSimpleItem(Items.GOLD_INGOT, 100).rare().build()) + .add(createSimpleItem(Items.NAME_TAG, 75).build()) + .add(createSimpleItem(Items.LEAD, 75).build()) + .add(createSimpleItem(Items.REDSTONE, 50).build()) + .add(createSimpleItem(Items.GOLDEN_APPLE, 25).diffAfter(DIFF_2).build()) + .add(createToolItem(Items.DIAMOND_CHESTPLATE, 20).enchant().damageMid().diffAfter(DIFF_2).build()) + .add(createToolItem(Items.ENCHANTED_BOOK, 15).enchant().diffAfter(DIFF_3).build()) + .add(createSimpleItem(Items.ENCHANTED_GOLDEN_APPLE, 5).diffAfter(DIFF_3).build()) + .add(createEncounterLootItem(VINDICATOR, 100).build()) + .add(createEncounterLootItem(EVOKER, 50).diffAfter(DIFF_2).build()) + .add(createEncounterLootItem(VEX, 50).diffAfter(DIFF_2).build()) + )); + + createStructureLootTable(MINESHAFT_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.RAIL, 100).common().build()) + .add(createSimpleItem(Items.POWERED_RAIL, 75).rare().build()) + .add(createSimpleItem(Items.DETECTOR_RAIL, 50).rare().build()) + .add(createSimpleItem(Items.ACTIVATOR_RAIL, 50).rare().build()) + .add(createSimpleItem(Items.IRON_INGOT, 50).uncommon().build()) + .add(createSimpleItem(Items.GOLD_INGOT, 50).rare().build()) + .add(createSimpleItem(Items.DIAMOND, 25).build()) + .add(createSimpleItem(Items.NAME_TAG, 25).build()) + .add(createSimpleItem(Items.GOLDEN_APPLE, 25).diffAfter(DIFF_2).build()) + .add(createToolItem(Items.ENCHANTED_BOOK, 20).enchant().diffAfter(DIFF_3).build()) + .add(createSimpleItem(Items.ENCHANTED_GOLDEN_APPLE, 5).diffAfter(DIFF_3).build()) + .add(createEncounterLootItem(ZOMBIE, 100).build()) + .add(createEncounterLootItem(SKELETON, 50).build()) + .add(createEncounterLootItem(CREEPER, 30).build()) + .add(createEncounterLootItem(SPIDER, 30).build()) + .add(createEncounterLootItem(CAVE_SPIDER, 30).build()) + .add(createEncounterLootItem(ENDERMAN, 15).build()) + )); + + createStructureLootTable(MONUMENT_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.PRISMARINE_BRICKS, 50).build()) + .add(createSimpleItem(Items.DARK_PRISMARINE, 25).build()) + .add(createSimpleItem(Items.SEA_LANTERN, 15).build()) + .add(createSimpleItem(Items.WET_SPONGE, 15).diffAfter(DIFF_2).build()) + .add(createEncounterLootItem(GUARDIAN, 50).build()) + .add(createEncounterLootItem(ELDER_GUARDIAN, 5).diffAfter(DIFF_3).build()) + )); + + createStructureLootTable(NETHER_FOSSIL_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.AIR, 25).build()) + .add(createSimpleItem(Items.BONE_BLOCK, 50).build()) + .add(createSimpleItem(Items.BONE_MEAL, 10).build()) + .add(createEncounterLootItem(ZOMBIFIED_PIGLIN, 25).build()) + )); + + createStructureLootTable(PILLAGER_OUTPOST_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.IRON_INGOT, 50).uncommon().build()) + .add(createSimpleItem(Items.EXPERIENCE_BOTTLE, 40).build()) + .add(createToolItem(Items.CROSSBOW, 40).damageLow().build()) + .add(createToolItem(Items.ENCHANTED_BOOK, 10).enchant().diffAfter(DIFF_3).build()) + .add(createEncounterLootItem(PILLAGER, 25).build()) + .add(createEncounterLootItem(PILLAGER_CAPTAIN, 5).build()) + )); + + createStructureLootTable(RUINED_PORTAL_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.NETHERRACK, 100).common().build()) + .add(createSimpleItem(Items.OBSIDIAN, 100).common().build()) + .add(createSimpleItem(Items.FIRE_CHARGE, 50).build()) + .add(createSimpleItem(Items.GOLDEN_APPLE, 25).build()) + .add(createToolItem(Items.GOLDEN_HELMET, 25).damageLow().enchant().build()) + .add(createToolItem(Items.GOLDEN_CHESTPLATE, 25).damageLow().enchant().build()) + .add(createToolItem(Items.GOLDEN_LEGGINGS, 25).damageLow().enchant().build()) + .add(createToolItem(Items.GOLDEN_BOOTS, 25).damageLow().enchant().build()) + .add(createToolItem(Items.GOLDEN_SWORD, 25).damageLow().enchant().build()) + .add(createToolItem(Items.GOLDEN_PICKAXE, 25).damageLow().enchant().build()) + .add(createToolItem(Items.GOLDEN_SHOVEL, 25).damageLow().enchant().build()) + .add(createSimpleItem(Items.GOLD_BLOCK, 10).build()) + .add(createEncounterLootItem(ZOMBIFIED_PIGLIN, 25).build()) + )); + + createStructureLootTable(SHIPWRECK_ID, registrar, builder -> { + MinecoloniesCropBlock[] crops = ModBlocks.getCrops(); + final LootPool.Builder pool = new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.OAK_PLANKS, crops.length * 50).common().build()) + .add(createSimpleItem(Items.SPRUCE_PLANKS, crops.length * 50).common().build()) + .add(createSimpleItem(Items.IRON_INGOT, crops.length * 15).build()) + .add(createSimpleItem(Items.GOLD_INGOT, crops.length * 10).build()) + .add(createSimpleItem(Items.DIAMOND, crops.length * 5).diffAfter(DIFF_2).build()) + .add(createEncounterLootItem(DROWNED, crops.length * 20).build()) + .add(createEncounterLootItem(DROWNED_TRIDENT, crops.length * 10).build()); + for (MinecoloniesCropBlock crop : crops) + { + pool.add(createSimpleItem(crop.asItem(), 75).common().build()); + } + builder.withPool(pool); + }); + + createStructureLootTable(STRONGHOLD_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(SPECIAL_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.STONE_BRICKS, 100).common().build()) + .add(createSimpleItem(Items.CRACKED_STONE_BRICKS, 100).common().build()) + .add(createSimpleItem(Items.ENDER_PEARL, 75).build()) + .add(createSimpleItem(Items.BOOK, 50).uncommon().build()) + .add(createSimpleItem(Items.BOOKSHELF, 25).build()) + .add(createSimpleItem(Items.DIAMOND, 25).diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.EMERALD, 25).diffAfter(DIFF_2).build()) + .add(createToolItem(Items.ENCHANTED_BOOK, 20).enchant().diffAfter(DIFF_3).build()) + .add(createEncounterLootItem(ZOMBIE, 50).build()) + .add(createEncounterLootItem(SKELETON, 30).build()) + .add(createEncounterLootItem(CREEPER, 10).build()) + .add(createEncounterLootItem(ENDERMAN, 10).build()) + )); + + createStructureLootTable(DESERT_PYRAMID_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.SANDSTONE, 100).common().build()) + .add(createSimpleItem(Items.CUT_SANDSTONE, 100).common().build()) + .add(createSimpleItem(Items.IRON_INGOT, 75).uncommon().build()) + .add(createSimpleItem(Items.GOLD_INGOT, 50).uncommon().build()) + .add(createSimpleItem(Items.GOLDEN_APPLE, 25).build()) + .add(createSimpleItem(Items.DIAMOND, 25).build()) + .add(createSimpleItem(Items.EMERALD, 25).build()) + .add(createToolItem(Items.ENCHANTED_BOOK, 20).enchant().build()) + .add(createEncounterLootItem(ZOMBIE, 50).build()) + .add(createEncounterLootItem(SKELETON, 30).build()) + .add(createEncounterLootItem(CREEPER, 10).build()) + )); + + createStructureLootTable(IGLOO_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.SNOW, 100).common().build()) + .add(createSimpleItem(Items.ICE, 100).common().build()) + .add(createSimpleItem(Items.SNOWBALL, 75).uncommon().build()) + .add(createSimpleItem(ModItems.lamb_stew, 50).uncommon().build()) + .add(createSimpleItem(Items.EMERALD, 25).build()) + )); + + createStructureLootTable(JUNGLE_TEMPLE_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.COBBLESTONE, 100).common().build()) + .add(createSimpleItem(Items.MOSSY_COBBLESTONE, 100).common().build()) + .add(createSimpleItem(Items.COBWEB, 75).uncommon().build()) + .add(createSimpleItem(ModItems.pita_hummus, 50).rare().build()) + .add(createSimpleItem(ModItems.pepper_hummus, 50).rare().build()) + .add(createSimpleItem(Items.DIAMOND, 35).diffAfter(DIFF_2).build()) + .add(createSimpleItem(Items.EMERALD, 35).diffAfter(DIFF_2).build()) + .add(createToolItem(Items.ENCHANTED_BOOK, 20).enchant().diffAfter(DIFF_3).build()) + .add(createEncounterLootItem(ZOMBIE, 50).build()) + .add(createEncounterLootItem(SKELETON, 30).build()) + .add(createEncounterLootItem(CREEPER, 10).build()) + )); + + createStructureLootTable(SWAMP_HUT_ID, registrar, builder -> builder.withPool( + new LootPool.Builder().setRolls(UNCOMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.SPRUCE_PLANKS, 100).common().build()) + .add(createSimpleItem(Items.SPRUCE_LOG, 100).common().build()) + .add(createSimpleItem(Items.RED_MUSHROOM, 50).uncommon().build()) + .add(createSimpleItem(Items.BROWN_MUSHROOM, 50).uncommon().build()) + .add(createSimpleItem(Items.CAULDRON, 50).build()) + .add(createSimpleItem(Items.BREWING_STAND, 50).build()) + .add(createEncounterLootItem(WITCH, 100).build()) + .add(createEncounterLootItem(ZOMBIE, 50).build()) + )); + + createStructureLootTable(VILLAGE_DESERT_ID, registrar, builder -> { + List crops = + Arrays.stream(ModBlocks.getCrops()).filter(f -> f.getPreferredBiome() == null || f.getPreferredBiome().equals(ModTags.dryBiomes)).toList(); + final LootPool.Builder pool = new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.SANDSTONE, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.SMOOTH_SANDSTONE, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.CACTUS, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.DEAD_BUSH, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.CLAY_BALL, crops.size() * 20).rare().build()) + .add(createSimpleItem(ModItems.flatbread, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.pottage, crops.size() * 20).rare().build()) + .add(createSimpleItem(ModItems.pepper_hummus, crops.size() * 30).rare().build()) + .add(createSimpleItem(ModItems.pita_hummus, crops.size() * 20).build()); + for (MinecoloniesCropBlock crop : crops) + { + pool.add(createSimpleItem(crop.asItem(), 75).common().build()); + } + builder.withPool(pool); + }); + + createStructureLootTable(VILLAGE_PLAINS_ID, registrar, builder -> { + List crops = + Arrays.stream(ModBlocks.getCrops()).filter(f -> f.getPreferredBiome() == null || f.getPreferredBiome().equals(ModTags.temperateBiomes)).toList(); + final LootPool.Builder pool = new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.COBBLESTONE, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.OAK_PLANKS, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.OAK_SAPLING, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.DANDELION, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.POPPY, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.cheddar_cheese, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.pasta_plain, crops.size() * 20).rare().build()) + .add(createSimpleItem(ModItems.pasta_tomato, crops.size() * 30).rare().build()) + .add(createSimpleItem(ModItems.stuffed_pita, crops.size() * 20).build()); + for (MinecoloniesCropBlock crop : crops) + { + pool.add(createSimpleItem(crop.asItem(), 75).common().build()); + } + builder.withPool(pool); + }); + + createStructureLootTable(VILLAGE_SAVANNA_ID, registrar, builder -> { + List crops = + Arrays.stream(ModBlocks.getCrops()).filter(f -> f.getPreferredBiome() == null || f.getPreferredBiome().equals(ModTags.dryBiomes)).toList(); + final LootPool.Builder pool = new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.YELLOW_TERRACOTTA, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.ACACIA_PLANKS, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.ACACIA_SAPLING, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.GRASS, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.TALL_GRASS, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.tofu, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.lembas_scone, crops.size() * 20).rare().build()) + .add(createSimpleItem(ModItems.pepper_hummus, crops.size() * 30).rare().build()) + .add(createSimpleItem(ModItems.pita_hummus, crops.size() * 20).build()); + for (MinecoloniesCropBlock crop : crops) + { + pool.add(createSimpleItem(crop.asItem(), 75).common().build()); + } + builder.withPool(pool); + }); + + createStructureLootTable(VILLAGE_SNOWY_ID, registrar, builder -> { + List crops = + Arrays.stream(ModBlocks.getCrops()).filter(f -> f.getPreferredBiome() == null || f.getPreferredBiome().equals(ModTags.coldBiomes)).toList(); + final LootPool.Builder pool = new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.SNOW_BLOCK, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.ICE, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.SNOWBALL, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.BEETROOT, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.BEETROOT_SOUP, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.feta_cheese, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.manchet_bread, crops.size() * 20).rare().build()) + .add(createSimpleItem(ModItems.cabochis, crops.size() * 30).rare().build()) + .add(createSimpleItem(ModItems.lamb_stew, crops.size() * 20).build()); + for (MinecoloniesCropBlock crop : crops) + { + pool.add(createSimpleItem(crop.asItem(), 75).common().build()); + } + builder.withPool(pool); + }); + + createStructureLootTable(VILLAGE_TAIGA_ID, registrar, builder -> { + List crops = + Arrays.stream(ModBlocks.getCrops()).filter(f -> f.getPreferredBiome() == null || f.getPreferredBiome().equals(ModTags.coldBiomes)).toList(); + final LootPool.Builder pool = new LootPool.Builder().setRolls(COMMON_STRUCTURE_ROLLS) + .add(createSimpleItem(Items.COBBLESTONE, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.SPRUCE_PLANKS, crops.size() * 50).common().build()) + .add(createSimpleItem(Items.FERN, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.LARGE_FERN, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(Items.SWEET_BERRIES, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.cooked_rice, crops.size() * 30).uncommon().build()) + .add(createSimpleItem(ModItems.muffin, crops.size() * 20).rare().build()) + .add(createSimpleItem(ModItems.cabochis, crops.size() * 30).rare().build()) + .add(createSimpleItem(ModItems.lamb_stew, crops.size() * 20).build()); + for (MinecoloniesCropBlock crop : crops) + { + pool.add(createSimpleItem(crop.asItem(), 75).common().build()); + } + builder.withPool(pool); + }); + } +} diff --git a/src/main/java/com/minecolonies/core/generation/defaults/QuestTranslationProvider.java b/src/main/java/com/minecolonies/core/generation/defaults/QuestTranslationProvider.java index 9678ae2da29..9990a7d6885 100644 --- a/src/main/java/com/minecolonies/core/generation/defaults/QuestTranslationProvider.java +++ b/src/main/java/com/minecolonies/core/generation/defaults/QuestTranslationProvider.java @@ -57,7 +57,8 @@ public CompletableFuture run(@NotNull final CachedOutput cache) final PackOutput.PathProvider questProvider = packOutput.createPathProvider(PackOutput.Target.DATA_PACK, COLONY_QUESTS_DIR); final List> quests = new ArrayList<>(); - try (final PackResources pack = new PathPackResources(MOD_ID + ".src", false, Path.of("..", "src", "main", "resources"))) + final Path root = packOutput.getOutputFolder().resolve(Path.of("..", "..", "..", "main", "resources")); + try (final PackResources pack = new PathPackResources(MOD_ID + ".src", false, root)) { pack.listResources(PackType.SERVER_DATA, MOD_ID, COLONY_QUESTS_DIR, (questId, stream) -> { diff --git a/src/main/java/com/minecolonies/core/items/ItemAdventureToken.java b/src/main/java/com/minecolonies/core/items/ItemAdventureToken.java index 56a09369c9b..a066a17ca34 100644 --- a/src/main/java/com/minecolonies/core/items/ItemAdventureToken.java +++ b/src/main/java/com/minecolonies/core/items/ItemAdventureToken.java @@ -21,6 +21,7 @@ public class ItemAdventureToken extends AbstractItemMinecolonies { /** * This item is purely for matching, and carrying data in Tags + * * @param properties */ public ItemAdventureToken(Properties properties) @@ -28,26 +29,27 @@ public ItemAdventureToken(Properties properties) super("adventure_token", properties); } + @Override + public void appendHoverText(@NotNull final ItemStack stack, @Nullable final Level worldIn, @NotNull final List tooltip, @NotNull final TooltipFlag flagIn) + { + final MutableComponent guiHint = Component.translatable(COM_MINECOLONIES_COREMOD_ADVENTURE_TOKEN_TOOLTIP_GUI); + guiHint.setStyle(Style.EMPTY.withColor(ChatFormatting.GRAY)); + tooltip.add(guiHint); + + tooltip.add(Component.literal(stack.getTag().getAsString()).withStyle(ChatFormatting.GRAY)); + + super.appendHoverText(stack, worldIn, tooltip, flagIn); + } + @Override public Component getName(ItemStack stack) { - if(stack.hasTag() && stack.getTag().contains(TAG_ENTITY_TYPE)) + if (stack.hasTag() && stack.getTag().contains(TAG_ENTITY_TYPE)) { EntityType mobType = EntityType.byString(stack.getTag().getString(TAG_ENTITY_TYPE)).orElse(EntityType.ZOMBIE); return Component.translatable(COM_MINECOLONIES_COREMOD_ADVENTURE_TOKEN_NAME_GUI, mobType.getDescription()); } - - return super.getName(stack); - } - - @Override - public void appendHoverText( - @NotNull final ItemStack stack, @Nullable final Level worldIn, @NotNull final List tooltip, @NotNull final TooltipFlag flagIn) - { - final MutableComponent guiHint = Component.translatable(COM_MINECOLONIES_COREMOD_ADVENTURE_TOKEN_TOOLTIP_GUI); - guiHint.setStyle(Style.EMPTY.withColor(ChatFormatting.GRAY)); - tooltip.add(guiHint); - super.appendHoverText(stack, worldIn, tooltip, flagIn); + return super.getName(stack); } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/items/ItemExpeditionSheet.java b/src/main/java/com/minecolonies/core/items/ItemExpeditionSheet.java new file mode 100644 index 00000000000..5abeb50c25d --- /dev/null +++ b/src/main/java/com/minecolonies/core/items/ItemExpeditionSheet.java @@ -0,0 +1,282 @@ +package com.minecolonies.core.items; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.IColonyManager; +import com.minecolonies.api.colony.IColonyView; +import com.minecolonies.api.colony.IVisitorViewData; +import com.minecolonies.api.colony.managers.interfaces.expeditions.CreatedExpedition; +import com.minecolonies.api.items.AbstractItemExpeditionSheet; +import com.minecolonies.api.items.ModItems; +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.core.client.gui.expedition_sheet.WindowExpeditionSheet; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.DespawnTimeData.DespawnTime; +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraftforge.items.wrapper.InvWrapper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.IntStream; + +import static com.minecolonies.api.util.constant.ExpeditionConstants.*; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_COLONY_ID; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_DESPAWN_TIME; + +/** + * Class handling expedition sheets. + */ +public class ItemExpeditionSheet extends AbstractItemExpeditionSheet +{ + /** + * Nbt tags. + */ + public static final String TAG_EXPEDITION_ID = "expedition_id"; + + /** + * Sets the name, creative tab, and registers the expedition sheet item. + * + * @param properties the properties. + */ + public ItemExpeditionSheet(final Properties properties) + { + super("expedition_sheet", properties.stacksTo(1)); + } + + /** + * Delete an expedition sheet item stack and drop its contents. + * + * @param itemStack the input item stack. + */ + public static void reduceAndDropContents(final @NotNull Player player, final ItemStack itemStack) + { + itemStack.shrink(1); + if (itemStack.isEmpty()) + { + final ExpeditionSheetContainerManager container = new ExpeditionSheetContainerManager(itemStack); + InventoryUtils.dropItemHandler(new InvWrapper(container), player.level, player.getBlockX(), player.getBlockY() + 1, player.getBlockZ()); + } + } + + @Override + @NotNull + public InteractionResultHolder use(final @NotNull Level level, final @NotNull Player player, final @NotNull InteractionHand hand) + { + final ItemStack itemStack = player.getItemInHand(hand); + final ExpeditionSheetInfo expeditionSheetInfo = getExpeditionSheetInfo(itemStack); + // If any of the required info is missing, delete the sheet. + if (expeditionSheetInfo == null) + { + reduceAndDropContents(player, itemStack); + return InteractionResultHolder.fail(itemStack); + } + + // The colony does not exist, this is the only case in which we do not delete the sheet. + // This can appear when the player enters another dimension. + final IColony colony; + if (level.isClientSide) + { + colony = IColonyManager.getInstance().getColonyView(expeditionSheetInfo.colonyId(), level.dimension()); + } + else + { + colony = IColonyManager.getInstance().getColonyByDimension(expeditionSheetInfo.colonyId(), level.dimension()); + } + if (colony == null) + { + return InteractionResultHolder.fail(itemStack); + } + + // The expedition instance does not exist, drop the contents in this case as this sheet is no longer useful. + final CreatedExpedition createdExpedition = colony.getExpeditionManager().getCreatedExpedition(expeditionSheetInfo.expeditionId()); + if (createdExpedition == null) + { + reduceAndDropContents(player, itemStack); + return InteractionResultHolder.fail(itemStack); + } + + // The expedition type this sheet was made for no longer exists, delete the sheet. + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeListener.getExpeditionType(createdExpedition.expeditionTypeId()); + if (expeditionType == null) + { + reduceAndDropContents(player, itemStack); + return InteractionResultHolder.fail(itemStack); + } + + // If we're not on the client side, we cannot open the GUI, so we can pass. + if (!level.isClientSide()) + { + return InteractionResultHolder.pass(itemStack); + } + + final WindowExpeditionSheet windowExpeditionSheet = new WindowExpeditionSheet((IColonyView) colony, expeditionType, hand, new ExpeditionSheetContainerManager(itemStack)); + windowExpeditionSheet.open(); + return InteractionResultHolder.success(itemStack); + } + + @Override + public void appendHoverText(@NotNull final ItemStack stack, @Nullable final Level level, @NotNull final List lines, @NotNull final TooltipFlag flag) + { + if (level == null) + { + return; + } + + final ExpeditionSheetInfo expeditionSheetInfo = getExpeditionSheetInfo(stack); + if (expeditionSheetInfo == null) + { + return; + } + + final IColonyView colony = IColonyManager.getInstance().getColonyView(expeditionSheetInfo.colonyId(), level.dimension()); + if (colony == null) + { + return; + } + + final IVisitorViewData visitor = colony.getVisitor(expeditionSheetInfo.expeditionId()); + if (visitor != null) + { + lines.add(Component.translatable(EXPEDITION_SHEET_DESCRIPTION_VISITOR).append(Component.literal(visitor.getName())).withStyle(ChatFormatting.GRAY)); + + final DespawnTime despawnTime = visitor.getExtraDataValue(EXTRA_DATA_DESPAWN_TIME); + final int timeRemainingInSeconds = (int) Math.max(0, despawnTime.duration() - (level.getGameTime() - despawnTime.start())) / 20; + final int timeMinutes = (int) Math.floor(timeRemainingInSeconds / 60d); + final int timeSeconds = timeRemainingInSeconds - (timeMinutes * 60); + final MutableComponent timeComponent = Component.literal(String.format("%02d:%02d", timeMinutes, timeSeconds)) + .withStyle(timeMinutes < 5 ? ChatFormatting.RED : ChatFormatting.WHITE) + .append(" ") + .append(Component.translatable(EXPEDITION_SHEET_DESCRIPTION_TIMEOUT_MINUTES).withStyle(ChatFormatting.GRAY)); + lines.add(Component.translatable(EXPEDITION_SHEET_DESCRIPTION_TIMEOUT, timeComponent).withStyle(ChatFormatting.GRAY)); + } + else + { + lines.add(Component.translatable(EXPEDITION_SHEET_DESCRIPTION_VISITOR) + .append(Component.translatable(EXPEDITION_SHEET_DESCRIPTION_VISITOR_LEFT)) + .withStyle(ChatFormatting.GRAY)); + lines.add(Component.translatable(EXPEDITION_SHEET_DESCRIPTION_TIMEOUT_EXPIRED).withStyle(ChatFormatting.RED)); + } + } + + @Override + @Nullable + public AbstractItemExpeditionSheet.ExpeditionSheetInfo getExpeditionSheetInfo(final ItemStack stack) + { + final CompoundTag tag = stack.getTag(); + if (tag == null || !tag.contains(TAG_COLONY_ID) || !tag.contains(TAG_EXPEDITION_ID)) + { + return null; + } + return new ExpeditionSheetInfo(tag.getInt(TAG_COLONY_ID), tag.getInt(TAG_EXPEDITION_ID)); + } + + @Override + @NotNull + public ItemStack createItemStackForExpedition(final ExpeditionSheetInfo expeditionSheetInfo) + { + final ItemStack itemStack = new ItemStack(ModItems.expeditionSheet, 1); + final CompoundTag compound = itemStack.getOrCreateTag(); + compound.putInt(TAG_COLONY_ID, expeditionSheetInfo.colonyId()); + compound.putInt(TAG_EXPEDITION_ID, expeditionSheetInfo.expeditionId()); + return itemStack; + } + + /** + * Container class for managing an expedition sheet. + */ + public static class ExpeditionSheetContainerManager extends SimpleContainer + { + /** + * Nbt tags. + */ + private static final String TAG_SHEET_DATA = "sheetData"; + private static final String TAG_INVENTORY = "inventory"; + private static final String TAG_MEMBERS = "members"; + + /** + * The size of the inventory. + */ + private static final int INVENTORY_SIZE = 27; + + /** + * The input item stack to fetch the data from. + */ + private final ItemStack stack; + + /** + * The set of members. + */ + private final Set members; + + /** + * Default constructor. + * + * @param stack the input item stack to fetch the data from. + */ + public ExpeditionSheetContainerManager(final ItemStack stack) + { + super(INVENTORY_SIZE); + this.stack = stack; + this.members = new HashSet<>(); + + final CompoundTag rootTag = stack.getOrCreateTag().getCompound(TAG_SHEET_DATA); + members.addAll(rootTag.contains(TAG_MEMBERS) ? IntStream.of(rootTag.getIntArray(TAG_MEMBERS)).boxed().toList() : new ArrayList<>()); + fromTag(rootTag.contains(TAG_INVENTORY) ? rootTag.getList(TAG_INVENTORY, Tag.TAG_COMPOUND) : new ListTag()); + } + + @Override + public void setChanged() + { + final CompoundTag rootTag = new CompoundTag(); + rootTag.putIntArray(TAG_MEMBERS, members.stream().toList()); + rootTag.put(TAG_INVENTORY, createTag()); + stack.getOrCreateTag().put(TAG_SHEET_DATA, rootTag); + super.setChanged(); + } + + /** + * Get the set of members that are assigned. + * + * @return the set of member ids. + */ + public Set getMembers() + { + return members; + } + + /** + * Toggle if a given member is assigned or not. + * + * @param memberId the member id. + * @param assign whether this member should be assigned or not. + */ + public void toggleMember(final int memberId, final boolean assign) + { + if (assign) + { + this.members.add(memberId); + } + else + { + this.members.remove(memberId); + } + setChanged(); + } + } +} diff --git a/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java b/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java index 15e2465d1dd..93a23f17351 100644 --- a/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java +++ b/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java @@ -107,7 +107,7 @@ protected ItemStack onItemUseSuccess( citizenData.getEntity().get().remove(Entity.RemovalReason.DISCARDED); } - colony.getCitizenManager().spawnOrCreateCivilian(citizenData, world, player.blockPosition(), true); + colony.getCitizenManager().spawnOrCreateCitizen(citizenData, world, player.blockPosition(), true); citizenData.setNextRespawnPosition(buildingPos); building.getSetting(AbstractBuildingGuards.GUARD_TASK).set(GuardTaskSetting.FOLLOW); diff --git a/src/main/java/com/minecolonies/core/loot/ExpeditionDifficultyCondition.java b/src/main/java/com/minecolonies/core/loot/ExpeditionDifficultyCondition.java new file mode 100644 index 00000000000..feb9f203a1d --- /dev/null +++ b/src/main/java/com/minecolonies/core/loot/ExpeditionDifficultyCondition.java @@ -0,0 +1,116 @@ +package com.minecolonies.core.loot; + +import com.google.gson.*; +import com.minecolonies.api.loot.ModLootConditions; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionTypeDifficulty; +import net.minecraft.util.GsonHelper; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; +import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +import static com.minecolonies.api.loot.ModLootConditions.EXPEDITION_DIFFICULTY_PARAM; + +/** + * A loot condition that depends on the expedition difficulty. + */ +public class ExpeditionDifficultyCondition implements LootItemCondition +{ + /** + * The required difficulty. + */ + @NotNull + private final List difficulties; + + /** + * Internal constructor. + * + * @param difficulties the required difficulties. + */ + private ExpeditionDifficultyCondition(@NotNull final List difficulties) + { + this.difficulties = difficulties; + } + + /** + * Generate a condition for a given expedition difficulty. + * + * @param difficulties the required difficulties. + * @return the loot condition builder. + */ + public static Builder forDifficulty(@NotNull final ColonyExpeditionTypeDifficulty... difficulties) + { + return () -> new ExpeditionDifficultyCondition(List.of(difficulties)); + } + + @NotNull + @Override + public LootItemConditionType getType() + { + return ModLootConditions.expeditionDifficulty.get(); + } + + @Override + public boolean test(@NotNull final LootContext lootContext) + { + if (difficulties.isEmpty()) + { + return true; + } + + final ColonyExpeditionTypeDifficulty actualDifficulty = lootContext.getParamOrNull(EXPEDITION_DIFFICULTY_PARAM); + if (actualDifficulty != null) + { + return difficulties.contains(actualDifficulty); + } + + return false; + } + + /** + * Serializer for {@link ExpeditionDifficultyCondition}. + */ + public static class Serializer implements net.minecraft.world.level.storage.loot.Serializer + { + @Override + public void serialize(@NotNull final JsonObject json, @NotNull final ExpeditionDifficultyCondition condition, @NotNull final JsonSerializationContext context) + { + if (!condition.difficulties.isEmpty()) + { + final JsonArray array = new JsonArray(); + for (final ColonyExpeditionTypeDifficulty difficulty : condition.difficulties) + { + array.add(difficulty.getKey()); + } + json.add("difficulties", array); + } + } + + @NotNull + @Override + public ExpeditionDifficultyCondition deserialize(@NotNull final JsonObject json, @NotNull final JsonDeserializationContext context) + { + if (json.has("difficulties")) + { + final List difficulties = new ArrayList<>(); + final JsonArray array = GsonHelper.getAsJsonArray(json, "difficulties"); + for (final JsonElement element : array) + { + if (GsonHelper.isStringValue(element)) + { + final ColonyExpeditionTypeDifficulty difficulty = ColonyExpeditionTypeDifficulty.fromKey(element.getAsString()); + if (difficulty != null) + { + difficulties.add(difficulty); + } + } + } + return new ExpeditionDifficultyCondition(difficulties); + } + return new ExpeditionDifficultyCondition(List.of()); + } + } +} diff --git a/src/main/java/com/minecolonies/core/network/NetworkChannel.java b/src/main/java/com/minecolonies/core/network/NetworkChannel.java index 76759c65569..59af8275308 100755 --- a/src/main/java/com/minecolonies/core/network/NetworkChannel.java +++ b/src/main/java/com/minecolonies/core/network/NetworkChannel.java @@ -26,6 +26,8 @@ import com.minecolonies.core.network.messages.server.colony.building.warehouse.UpgradeWarehouseMessage; import com.minecolonies.core.network.messages.server.colony.building.worker.*; import com.minecolonies.core.network.messages.server.colony.citizen.*; +import com.minecolonies.core.network.messages.server.colony.visitor.expeditionary.AssignGuardMessage; +import com.minecolonies.core.network.messages.server.colony.visitor.expeditionary.TransferItemsMessage; import com.minecolonies.core.network.messages.splitting.SplitPacketMessage; import com.minecolonies.core.research.GlobalResearchTreeMessage; import io.netty.buffer.ByteBuf; @@ -204,6 +206,9 @@ public void registerCommonMessages() registerMessage(++idx, PickupBlockMessage.class, PickupBlockMessage::new); registerMessage(++idx, MarkStoryReadOnItem.class, MarkStoryReadOnItem::new); registerMessage(++idx, AlterRestaurantMenuItemMessage.class, AlterRestaurantMenuItemMessage::new); + registerMessage(++idx, OpenExpeditionSheetInventoryMessage.class, OpenExpeditionSheetInventoryMessage::new); + registerMessage(++idx, AssignGuardMessage.class, AssignGuardMessage::new); + registerMessage(++idx, TransferItemsMessage.class, TransferItemsMessage::new); //Client side only registerMessage(++idx, BlockParticleEffectMessage.class, BlockParticleEffectMessage::new); @@ -230,6 +235,8 @@ public void registerCommonMessages() registerMessage(++idx, SaveStructureNBTMessage.class, SaveStructureNBTMessage::new); registerMessage(++idx, GlobalQuestSyncMessage.class, GlobalQuestSyncMessage::new); registerMessage(++idx, GlobalDiseaseSyncMessage.class, GlobalDiseaseSyncMessage::new); + registerMessage(++idx, GlobalExpeditionEncounterSyncMessage.class, GlobalExpeditionEncounterSyncMessage::new); + registerMessage(++idx, GlobalColonyExpeditionTypeSyncMessage.class, GlobalColonyExpeditionTypeSyncMessage::new); registerMessage(++idx, OpenColonyFoundingCovenantMessage.class, OpenColonyFoundingCovenantMessage::new); registerMessage(++idx, OpenBuildingUIMessage.class, OpenBuildingUIMessage::new); registerMessage(++idx, OpenCantFoundColonyWarningMessage.class, OpenCantFoundColonyWarningMessage::new); diff --git a/src/main/java/com/minecolonies/core/network/messages/client/GlobalColonyExpeditionTypeSyncMessage.java b/src/main/java/com/minecolonies/core/network/messages/client/GlobalColonyExpeditionTypeSyncMessage.java new file mode 100644 index 00000000000..2530d72d727 --- /dev/null +++ b/src/main/java/com/minecolonies/core/network/messages/client/GlobalColonyExpeditionTypeSyncMessage.java @@ -0,0 +1,72 @@ +package com.minecolonies.core.network.messages.client; + +import com.minecolonies.api.network.IMessage; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.network.NetworkEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The message used to synchronize global colony expedition type data from a server to a remote client. + */ +public class GlobalColonyExpeditionTypeSyncMessage implements IMessage +{ + /** + * The buffer with the data. + */ + private FriendlyByteBuf buffer; + + /** + * Empty constructor used when registering the message + */ + public GlobalColonyExpeditionTypeSyncMessage() + { + super(); + } + + /** + * Add or Update expedition type data on the client. + * + * @param buf the bytebuffer. + */ + public GlobalColonyExpeditionTypeSyncMessage(final FriendlyByteBuf buf) + { + this.buffer = new FriendlyByteBuf(buf.copy()); + } + + @Override + public void fromBytes(@NotNull final FriendlyByteBuf buf) + { + buffer = new FriendlyByteBuf(buf.retain()); + } + + @Override + public void toBytes(@NotNull final FriendlyByteBuf buf) + { + buffer.resetReaderIndex(); + buf.writeBytes(buffer); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + + @OnlyIn(Dist.CLIENT) + @Override + public void onExecute(final NetworkEvent.Context ctxIn, final boolean isLogicalServer) + { + if (Minecraft.getInstance().level != null) + { + ColonyExpeditionTypeListener.readGlobalExpeditionTypePackets(buffer); + } + buffer.release(); + } +} diff --git a/src/main/java/com/minecolonies/core/network/messages/client/GlobalExpeditionEncounterSyncMessage.java b/src/main/java/com/minecolonies/core/network/messages/client/GlobalExpeditionEncounterSyncMessage.java new file mode 100644 index 00000000000..e91607f9649 --- /dev/null +++ b/src/main/java/com/minecolonies/core/network/messages/client/GlobalExpeditionEncounterSyncMessage.java @@ -0,0 +1,72 @@ +package com.minecolonies.core.network.messages.client; + +import com.minecolonies.api.network.IMessage; +import com.minecolonies.core.datalistener.ExpeditionEncounterListener; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.network.NetworkEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The message used to synchronize global expedition type data from a server to a remote client. + */ +public class GlobalExpeditionEncounterSyncMessage implements IMessage +{ + /** + * The buffer with the data. + */ + private FriendlyByteBuf buffer; + + /** + * Empty constructor used when registering the message + */ + public GlobalExpeditionEncounterSyncMessage() + { + super(); + } + + /** + * Add or Update expedition type data on the client. + * + * @param buf the bytebuffer. + */ + public GlobalExpeditionEncounterSyncMessage(final FriendlyByteBuf buf) + { + this.buffer = new FriendlyByteBuf(buf.copy()); + } + + @Override + public void fromBytes(@NotNull final FriendlyByteBuf buf) + { + buffer = new FriendlyByteBuf(buf.retain()); + } + + @Override + public void toBytes(@NotNull final FriendlyByteBuf buf) + { + buffer.resetReaderIndex(); + buf.writeBytes(buffer); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + + @OnlyIn(Dist.CLIENT) + @Override + public void onExecute(final NetworkEvent.Context ctxIn, final boolean isLogicalServer) + { + if (Minecraft.getInstance().level != null) + { + ExpeditionEncounterListener.readGlobalExpeditionEncounterPackets(buffer); + } + buffer.release(); + } +} diff --git a/src/main/java/com/minecolonies/core/network/messages/server/OpenExpeditionSheetInventoryMessage.java b/src/main/java/com/minecolonies/core/network/messages/server/OpenExpeditionSheetInventoryMessage.java new file mode 100644 index 00000000000..1609fa430da --- /dev/null +++ b/src/main/java/com/minecolonies/core/network/messages/server/OpenExpeditionSheetInventoryMessage.java @@ -0,0 +1,68 @@ +package com.minecolonies.core.network.messages.server; + +import com.minecolonies.api.inventory.container.ContainerExpeditionSheet; +import com.minecolonies.api.network.IMessage; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.network.NetworkEvent.Context; +import net.minecraftforge.network.NetworkHooks; + +import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITION_SHEET_INVENTORY; + +/** + * Message for opening the inventory of the expedition sheet. + */ +public class OpenExpeditionSheetInventoryMessage implements IMessage +{ + /** + * The hand that the sheet is in. + */ + private InteractionHand hand; + + /** + * Deserialization constructor. + */ + public OpenExpeditionSheetInventoryMessage() + { + } + + /** + * Default constructor. + * + * @param hand the hand the sheet is in. + */ + public OpenExpeditionSheetInventoryMessage(final InteractionHand hand) + { + this.hand = hand; + } + + @Override + public void toBytes(final FriendlyByteBuf buf) + { + buf.writeEnum(hand); + } + + @Override + public void fromBytes(final FriendlyByteBuf buf) + { + hand = buf.readEnum(InteractionHand.class); + } + + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + + @Override + public void onExecute(final Context ctxIn, final boolean isLogicalServer) + { + NetworkHooks.openScreen(ctxIn.getSender(), + new SimpleMenuProvider((windowId, inventory, player1) -> new ContainerExpeditionSheet(windowId, inventory, player1.getItemInHand(hand)), + Component.translatable(EXPEDITION_SHEET_INVENTORY)), + packetBuffer -> packetBuffer.writeEnum(hand)); + } +} diff --git a/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionClose.java b/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionClose.java index d1c3c7ee867..c96d3ac22e3 100755 --- a/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionClose.java +++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionClose.java @@ -82,7 +82,7 @@ protected void onExecute(final NetworkEvent.Context ctxIn, final boolean isLogic ICitizenData citizenData = colony.getCitizenManager().getCivilian(citizenId); if (citizenData == null) { - citizenData = colony.getVisitorManager().getVisitor(citizenId); + citizenData = colony.getVisitorManager().getCivilian(citizenId); } if (citizenData != null && ctxIn.getSender() != null) diff --git a/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionResponse.java b/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionResponse.java index aef20040edd..487dfb510c0 100755 --- a/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionResponse.java +++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/InteractionResponse.java @@ -92,7 +92,7 @@ protected void onExecute(final NetworkEvent.Context ctxIn, final boolean isLogic ICitizenData citizenData = colony.getCitizenManager().getCivilian(citizenId); if (citizenData == null) { - citizenData = colony.getVisitorManager().getVisitor(citizenId); + citizenData = colony.getVisitorManager().getCivilian(citizenId); } if (citizenData != null && ctxIn.getSender() != null) diff --git a/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/AssignGuardMessage.java b/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/AssignGuardMessage.java new file mode 100644 index 00000000000..017da04b3b8 --- /dev/null +++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/AssignGuardMessage.java @@ -0,0 +1,96 @@ +package com.minecolonies.core.network.messages.server.colony.visitor.expeditionary; + +import com.minecolonies.api.colony.ICitizenData; +import com.minecolonies.api.colony.ICitizenDataView; +import com.minecolonies.api.colony.IColony; +import com.minecolonies.core.items.ItemExpeditionSheet.ExpeditionSheetContainerManager; +import com.minecolonies.core.network.messages.server.AbstractColonyServerMessage; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.network.NetworkEvent.Context; + +/** + * Message for assigning a guard to an expedition. + */ +public class AssignGuardMessage extends AbstractColonyServerMessage +{ + /** + * The id of the guard citizen. + */ + private int id; + + /** + * Whether to assign or unassign the guard. + */ + private boolean assign; + + /** + * In which hand the expedition sheet is. + */ + private InteractionHand hand; + + /** + * Deserialization constructor. + */ + public AssignGuardMessage() + { + } + + /** + * Create a new guard assignment message for the given guard. + * + * @param guard the guard to (un)assign. + * @param assign whether to assign or unassign. + * @param hand in which hand the expedition sheet is. + */ + public AssignGuardMessage(final ICitizenDataView guard, final boolean assign, final InteractionHand hand) + { + super(guard.getColony()); + this.id = guard.getId(); + this.assign = assign; + this.hand = hand; + } + + @Override + protected void onExecute(final Context ctxIn, final boolean isLogicalServer, final IColony colony) + { + if (!isLogicalServer) + { + return; + } + + final ICitizenData guard = colony.getCitizenManager().getCivilian(id); + if (guard == null) + { + colony.getCitizenManager().markDirty(); + return; + } + + + if (!guard.getJob().isGuard() || !guard.getJob().isCombatGuard()) + { + return; + } + + final ItemStack itemInHand = ctxIn.getSender().getItemInHand(hand); + final ExpeditionSheetContainerManager expeditionSheetContainerManager = new ExpeditionSheetContainerManager(itemInHand); + expeditionSheetContainerManager.toggleMember(guard.getId(), assign); + } + + @Override + protected void toBytesOverride(final FriendlyByteBuf buf) + { + buf.writeInt(id); + buf.writeBoolean(assign); + buf.writeEnum(hand); + } + + @Override + protected void fromBytesOverride(final FriendlyByteBuf buf) + { + id = buf.readInt(); + assign = buf.readBoolean(); + hand = buf.readEnum(InteractionHand.class); + } +} diff --git a/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/TransferItemsMessage.java b/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/TransferItemsMessage.java new file mode 100644 index 00000000000..0fa9ec773c3 --- /dev/null +++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/TransferItemsMessage.java @@ -0,0 +1,125 @@ +package com.minecolonies.core.network.messages.server.colony.visitor.expeditionary; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.IColonyView; +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.Log; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement.RequirementHandler; +import com.minecolonies.core.colony.expeditions.colony.types.ColonyExpeditionType; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import com.minecolonies.core.items.ItemExpeditionSheet.ExpeditionSheetContainerManager; +import com.minecolonies.core.network.messages.server.AbstractColonyServerMessage; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.wrapper.InvWrapper; +import net.minecraftforge.network.NetworkEvent.Context; + +import java.util.Optional; + +/** + * Network message for transferring items to the expeditionary. + */ +public class TransferItemsMessage extends AbstractColonyServerMessage +{ + /** + * The id of the expedition type. + */ + private ResourceLocation expeditionTypeId; + + /** + * The id of the requirement to fulfill. + */ + private ResourceLocation requirementId; + + /** + * In which hand the expedition sheet is. + */ + private InteractionHand hand; + + /** + * Deserialization constructor. + */ + public TransferItemsMessage() + { + super(); + } + + /** + * Default constructor. + * + * @param colonyView the colony view. + * @param requirementId the requirement to fulfill. + */ + public TransferItemsMessage(final IColonyView colonyView, final ResourceLocation expeditionTypeId, final ResourceLocation requirementId, final InteractionHand hand) + { + super(colonyView); + this.expeditionTypeId = expeditionTypeId; + this.requirementId = requirementId; + this.hand = hand; + } + + @Override + protected void onExecute(final Context ctxIn, final boolean isLogicalServer, final IColony colony) + { + if (!isLogicalServer) + { + return; + } + + final ItemStack itemInHand = ctxIn.getSender().getItemInHand(hand); + final ExpeditionSheetContainerManager expeditionSheetContainerManager = new ExpeditionSheetContainerManager(itemInHand); + + final ColonyExpeditionType colonyExpeditionType = ColonyExpeditionTypeListener.getExpeditionType(expeditionTypeId); + if (colonyExpeditionType == null) + { + Log.getLogger().warn("Transferring items for expedition failed, expedition type '{}' does not exist on the server side.", expeditionTypeId); + return; + } + + final Optional requirement = colonyExpeditionType.requirements().stream() + .filter(f -> f.getId().equals(requirementId)) + .findFirst(); + if (requirement.isPresent()) + { + final IItemHandler inventoryHandler = new InvWrapper(expeditionSheetContainerManager); + + final RequirementHandler handler = requirement.get().createHandler(inventoryHandler); + final int needed = handler.getAmount() - handler.getAmountAvailable(); + + if (needed > 0) + { + if (ctxIn.getSender().isCreative()) + { + InventoryUtils.addItemStackToItemHandler(inventoryHandler, handler.getDefaultItemStack().copyWithCount(needed)); + } + else + { + InventoryUtils.transferItemStackIntoNextFreeSlotFromItemHandler(new InvWrapper(ctxIn.getSender().getInventory()), + handler.getItemPredicate(), + needed, + inventoryHandler); + } + } + } + } + + @Override + protected void toBytesOverride(final FriendlyByteBuf buf) + { + buf.writeResourceLocation(expeditionTypeId); + buf.writeResourceLocation(requirementId); + buf.writeEnum(hand); + } + + @Override + protected void fromBytesOverride(final FriendlyByteBuf buf) + { + expeditionTypeId = buf.readResourceLocation(); + requirementId = buf.readResourceLocation(); + hand = buf.readEnum(InteractionHand.class); + } +} diff --git a/src/main/resources/assets/minecolonies/gui/generic/resource.xml b/src/main/resources/assets/minecolonies/gui/generic/resource.xml new file mode 100644 index 00000000000..54d67707ae8 --- /dev/null +++ b/src/main/resources/assets/minecolonies/gui/generic/resource.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/assets/minecolonies/gui/layouthuts/layoutbuilderres.xml b/src/main/resources/assets/minecolonies/gui/layouthuts/layoutbuilderres.xml index 4ece8866027..a4b6993065a 100644 --- a/src/main/resources/assets/minecolonies/gui/layouthuts/layoutbuilderres.xml +++ b/src/main/resources/assets/minecolonies/gui/layouthuts/layoutbuilderres.xml @@ -6,17 +6,8 @@