From 88ce1d8b2c40f6ac41b54444a6f9e80f185a8581 Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Mon, 15 Jan 2024 00:33:05 +0100 Subject: [PATCH 01/94] Temp --- .../minecolonies/api/IMinecoloniesAPI.java | 3 + .../api/MinecoloniesAPIProxy.java | 7 + .../api/colony/ICitizenDataView.java | 8 + .../com/minecolonies/api/colony/IColony.java | 7 + .../minecolonies/api/colony/IVisitorData.java | 35 +- .../api/colony/IVisitorViewData.java | 17 +- .../ModInteractionResponseHandlers.java | 2 +- .../managers/interfaces/ICitizenManager.java | 2 +- .../managers/interfaces/IEntityManager.java | 6 +- .../managers/interfaces/IVisitorManager.java | 10 +- .../minecolonies/api/entity/ModEntities.java | 3 +- .../entity/visitor/AbstractEntityVisitor.java | 23 + .../visitor/AbstractVisitorExtraData.java | 24 + .../api/entity/visitor/IVisitorExtraData.java | 31 + .../api/entity/visitor/IVisitorType.java | 74 +++ .../api/entity/visitor/ModVisitorTypes.java | 21 + .../api/util/constant/WindowConstants.java | 12 +- .../apiimp/CommonMinecoloniesAPIImpl.java | 15 + .../apiimp/initializer/EntityInitializer.java | 12 +- .../ModVisitorTypesInitializer.java | 25 + .../minecolonies/coremod/MineColonies.java | 3 +- .../coremod/colony/CitizenDataView.java | 4 +- .../coremod/colony/VisitorData.java | 165 +++-- .../coremod/colony/VisitorDataView.java | 74 +-- .../buildings/modules/FarmerFieldModule.java | 0 .../colony/managers/CitizenManager.java | 16 +- .../colony/managers/VisitorManager.java | 14 +- .../entity/ai/visitor/EntityAIVisitor.java | 22 +- .../coremod/entity/citizen/EntityCitizen.java | 1 - .../entity/citizen/VisitorCitizen.java | 574 +++++++++--------- .../entity/visitor/RegularVisitorType.java | 156 +++++ 31 files changed, 869 insertions(+), 497 deletions(-) create mode 100644 src/api/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java create mode 100644 src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java create mode 100644 src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java create mode 100644 src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java create mode 100644 src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java create mode 100644 src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java delete mode 100644 src/main/java/com/minecolonies/coremod/colony/buildings/modules/FarmerFieldModule.java create mode 100644 src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java diff --git a/src/api/java/com/minecolonies/api/IMinecoloniesAPI.java b/src/api/java/com/minecolonies/api/IMinecoloniesAPI.java index f7020dacf5f..6cfaa5a75d3 100755 --- a/src/api/java/com/minecolonies/api/IMinecoloniesAPI.java +++ b/src/api/java/com/minecolonies/api/IMinecoloniesAPI.java @@ -20,6 +20,7 @@ import com.minecolonies.api.crafting.registry.RecipeTypeEntry; import com.minecolonies.api.entity.ai.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.quests.registries.QuestRegistries; import com.minecolonies.api.research.IGlobalResearchTree; @@ -95,4 +96,6 @@ static IMinecoloniesAPI getInstance() IForgeRegistry getHappinessTypeRegistry(); IForgeRegistry getHappinessFunctionRegistry(); + + IForgeRegistry getVisitorTypeRegistry(); } diff --git a/src/api/java/com/minecolonies/api/MinecoloniesAPIProxy.java b/src/api/java/com/minecolonies/api/MinecoloniesAPIProxy.java index a73348ed506..2d85895da1a 100755 --- a/src/api/java/com/minecolonies/api/MinecoloniesAPIProxy.java +++ b/src/api/java/com/minecolonies/api/MinecoloniesAPIProxy.java @@ -20,6 +20,7 @@ import com.minecolonies.api.crafting.registry.RecipeTypeEntry; import com.minecolonies.api.entity.ai.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.quests.registries.QuestRegistries; import com.minecolonies.api.research.IGlobalResearchTree; @@ -221,4 +222,10 @@ public IForgeRegistry getHappinessFunc { return apiInstance.getHappinessFunctionRegistry(); } + + @Override + public IForgeRegistry getVisitorTypeRegistry() + { + return apiInstance.getVisitorTypeRegistry(); + } } diff --git a/src/api/java/com/minecolonies/api/colony/ICitizenDataView.java b/src/api/java/com/minecolonies/api/colony/ICitizenDataView.java index 75bfed63deb..2705539e9b4 100755 --- a/src/api/java/com/minecolonies/api/colony/ICitizenDataView.java +++ b/src/api/java/com/minecolonies/api/colony/ICitizenDataView.java @@ -220,4 +220,12 @@ public interface ICitizenDataView extends ICitizen * @param position the pos of the home building. */ void setHomeBuilding(BlockPos position); + + /** + * Get the colony this citizen belongs to. + * + * @return the colony view. + */ + @Override + IColonyView getColony(); } diff --git a/src/api/java/com/minecolonies/api/colony/IColony.java b/src/api/java/com/minecolonies/api/colony/IColony.java index e21f3d0619b..4bfee1ec3d1 100755 --- a/src/api/java/com/minecolonies/api/colony/IColony.java +++ b/src/api/java/com/minecolonies/api/colony/IColony.java @@ -490,6 +490,13 @@ default List getWayPoints(@NotNull BlockPos position, @NotNull BlockPo */ IQuestManager getQuestManager(); + /** + * Get the expedition manager of the colony. + * + * @return the expedition manager. + */ + IExpeditionManager getExpeditionManager(); + /** * Get citizen from colony. * @param id the id of the cit. diff --git a/src/api/java/com/minecolonies/api/colony/IVisitorData.java b/src/api/java/com/minecolonies/api/colony/IVisitorData.java index 3874115d1f4..a0b635cd446 100644 --- a/src/api/java/com/minecolonies/api/colony/IVisitorData.java +++ b/src/api/java/com/minecolonies/api/colony/IVisitorData.java @@ -1,40 +1,25 @@ package com.minecolonies.api.colony; -import net.minecraft.world.item.ItemStack; -import net.minecraft.core.BlockPos; - -import java.util.UUID; +import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; +import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import net.minecraft.world.entity.EntityType; /** - * 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 entity type for this visitor. * - * @return itemstack + * @return the entity type. */ - ItemStack getRecruitCost(); + EntityType getEntityType(); /** - * The position the visitor is sitting on + * Get any bit of additional information for this visitor. * - * @return sitting pos + * @return the extra data container. */ - BlockPos getSittingPosition(); - - /** - * Sets the sitting position - * - * @param pos sitting pos - */ - void setSittingPosition(final BlockPos pos); - - void setCustomTexture(UUID texture); + T getExtraDataValue(final IVisitorExtraData extraData); } diff --git a/src/api/java/com/minecolonies/api/colony/IVisitorViewData.java b/src/api/java/com/minecolonies/api/colony/IVisitorViewData.java index 19df62a1976..76ed84344d9 100644 --- a/src/api/java/com/minecolonies/api/colony/IVisitorViewData.java +++ b/src/api/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.AbstractEntityVisitor; +import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import net.minecraft.world.entity.EntityType; /** * View data for visitors @@ -8,9 +10,16 @@ public interface IVisitorViewData extends ICitizenDataView { /** - * Gets the visitors recruitment cost + * Get the entity type for this visitor. * - * @return stack to pay + * @return the entity type. */ - ItemStack getRecruitCost(); + EntityType getEntityType(); + + /** + * Get any bit of additional information for this visitor. + * + * @return the extra data container. + */ + T getExtraDataValue(final IVisitorExtraData extraData); } diff --git a/src/api/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java b/src/api/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java index 098afc2d877..6ec6eb410e3 100644 --- a/src/api/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java +++ b/src/api/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java @@ -34,6 +34,6 @@ public final class ModInteractionResponseHandlers 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/api/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java b/src/api/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java index 071d514be4d..e65a17c902f 100644 --- a/src/api/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java +++ b/src/api/java/com/minecolonies/api/colony/managers/interfaces/ICitizenManager.java @@ -13,7 +13,7 @@ /** * The interface of the citizen manager. */ -public interface ICitizenManager extends IEntityManager +public interface ICitizenManager extends IEntityManager { /** diff --git a/src/api/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java index d3f5e4d1804..766d81f5d5c 100644 --- a/src/api/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java +++ b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IEntityManager.java @@ -15,7 +15,7 @@ /** * Manager interface for managing entities for a colony */ -public interface IEntityManager +public interface IEntityManager { /** * Register a civilian entity with the colony @@ -69,7 +69,7 @@ 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); + T getCivilian(int civilianId); /** * Spawns a civilian with the specific civilian data. @@ -80,7 +80,7 @@ void sendPackets( * @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); + T spawnOrCreateCivilian(T data, Level world, BlockPos spawnPos, boolean force); /** * Creates Civilian Data for a new civilian diff --git a/src/api/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java index fbf69d763d8..a53b6f3c9e3 100644 --- a/src/api/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java +++ b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IVisitorManager.java @@ -5,14 +5,6 @@ /** * Visitor manager to manage visiting entities */ -public interface IVisitorManager extends IEntityManager +public interface IVisitorManager extends IEntityManager { - /** - * Gets the visitor data for the given citizen - * - * @param citizenId id to get data for - * @param data type - * @return visitor data - */ - T getVisitor(int citizenId); } diff --git a/src/api/java/com/minecolonies/api/entity/ModEntities.java b/src/api/java/com/minecolonies/api/entity/ModEntities.java index e5b420e19f2..3c4b056545d 100755 --- a/src/api/java/com/minecolonies/api/entity/ModEntities.java +++ b/src/api/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; @@ -18,7 +19,7 @@ public class ModEntities { public static EntityType CITIZEN; - public static EntityType VISITOR; + public static EntityType VISITOR; public static EntityType FISHHOOK; diff --git a/src/api/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java b/src/api/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java new file mode 100644 index 00000000000..41bf9c88898 --- /dev/null +++ b/src/api/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/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java b/src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java new file mode 100644 index 00000000000..e0cb44ee43e --- /dev/null +++ b/src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java @@ -0,0 +1,24 @@ +package com.minecolonies.api.entity.visitor; + +public abstract class AbstractVisitorExtraData implements IVisitorExtraData +{ + private String key; + + private S value; + + public AbstractVisitorExtraData() + { + + } + + @Override + public final S getValue() + { + return value; + } + + protected final void setValue(final S value) + { + this.value = value; + } +} diff --git a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java new file mode 100644 index 00000000000..0e10a715840 --- /dev/null +++ b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java @@ -0,0 +1,31 @@ +package com.minecolonies.api.entity.visitor; + +import net.minecraft.nbt.CompoundTag; +import net.minecraftforge.common.util.INBTSerializable; + +/** + * Interface for extra visitor data. + */ +public interface IVisitorExtraData extends INBTSerializable +{ + /** + * The unique key which the data gets stored under. + * + * @return the key. + */ + String getKey(); + + /** + * Get the visitor data value. + * + * @return the value. + */ + S getValue(); + + /** + * Get the default value in case no value was explicitly set yet. + * + * @return the value. + */ + S getDefaultValue(); +} \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java new file mode 100644 index 00000000000..5a58547c0fb --- /dev/null +++ b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java @@ -0,0 +1,74 @@ +package com.minecolonies.api.entity.visitor; + +import com.minecolonies.api.colony.IVisitorViewData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.EntityType; +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.Consumer; + +/** + * 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 entity type for this visitor type. + * + * @return the entity type. + */ + EntityType getEntityType(); + + /** + * 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) + { + // Nothing should happen by default. + return InteractionResult.PASS; + } + + /** + * Get the custom texture for a visitor. + * + * @param visitor the visitor view data instance. + * @param cachedTexture the currently cached texture. + * @param cacheSetter the callable action to update the cached value. + */ + void getCustomTexture(final IVisitorViewData visitor, final ResourceLocation cachedTexture, final Consumer cacheSetter); +} \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java b/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java new file mode 100644 index 00000000000..b31ee505072 --- /dev/null +++ b/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java @@ -0,0 +1,21 @@ +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"); + + /** + * Registry objects. + */ + public static RegistryObject visitor; +} \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/util/constant/WindowConstants.java b/src/api/java/com/minecolonies/api/util/constant/WindowConstants.java index b2d6a0f6edc..e51d57f1ed4 100755 --- a/src/api/java/com/minecolonies/api/util/constant/WindowConstants.java +++ b/src/api/java/com/minecolonies/api/util/constant/WindowConstants.java @@ -583,32 +583,32 @@ 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"; diff --git a/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java b/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java index 31e61d7a36e..19c0c6b1d54 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.ai.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.quests.registries.QuestRegistries; import com.minecolonies.api.research.IGlobalResearchTree; @@ -78,6 +79,8 @@ public class CommonMinecoloniesAPIImpl implements IMinecoloniesAPI private IForgeRegistry questDialogueAnswerRegistry; private IForgeRegistry happinessFactorTypeRegistry; private IForgeRegistry happinessFunctionRegistry; + private IForgeRegistry visitorTypeRegistry; + private IForgeRegistry visitorExtraDataRegistry; @Override @NotNull @@ -200,6 +203,12 @@ public IForgeRegistry getResearchCostRegistry() return researchCostRegistry; } + @Override + public IForgeRegistry getVisitorTypeRegistry() + { + return visitorTypeRegistry; + } + public void onRegistryNewRegistry(final NewRegistryEvent event) { event.create(new RegistryBuilder() @@ -316,6 +325,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 4a62c9abd42..f0652121437 100644 --- a/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java @@ -22,6 +22,7 @@ import com.minecolonies.coremod.entity.mobs.pirates.EntityArcherPirate; import com.minecolonies.coremod.entity.mobs.pirates.EntityCaptainPirate; import com.minecolonies.coremod.entity.mobs.pirates.EntityPirate; +import com.minecolonies.coremod.entity.visitor.RegularVisitorType; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; @@ -61,11 +62,12 @@ 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.MERCENARY = build(registry, "mercenary", EntityType.Builder.of(EntityMercenary::new, MobCategory.CREATURE) 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..eafb11d9974 --- /dev/null +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java @@ -0,0 +1,25 @@ +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.coremod.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); + } + 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/coremod/MineColonies.java b/src/main/java/com/minecolonies/coremod/MineColonies.java index ae38cdacf21..dc0dfbc7e33 100755 --- a/src/main/java/com/minecolonies/coremod/MineColonies.java +++ b/src/main/java/com/minecolonies/coremod/MineColonies.java @@ -32,8 +32,6 @@ import com.minecolonies.coremod.proxy.IProxy; import com.minecolonies.coremod.proxy.ServerProxy; import com.minecolonies.coremod.structures.MineColoniesStructures; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.capabilities.Capability; @@ -96,6 +94,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()); diff --git a/src/main/java/com/minecolonies/coremod/colony/CitizenDataView.java b/src/main/java/com/minecolonies/coremod/colony/CitizenDataView.java index 46aae5fa3d0..b16ef377855 100644 --- a/src/main/java/com/minecolonies/coremod/colony/CitizenDataView.java +++ b/src/main/java/com/minecolonies/coremod/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; @@ -33,7 +32,6 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.stream.Collectors; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_OFFHAND_HELD_ITEM_SLOT; import static com.minecolonies.api.util.constant.TranslationConstants.COM_MINECOLONIES_COREMOD_GUI_TOWNHALL_CITIZEN_UNEMPLOYED; @@ -208,7 +206,7 @@ public boolean isPaused() } @Override - public IColony getColony() + public IColonyView getColony() { return colonyView; } diff --git a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java index 20c8a69716b..2f35209e8b2 100644 --- a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java @@ -1,19 +1,26 @@ package com.minecolonies.coremod.colony; +import com.minecolonies.api.IMinecoloniesAPI; import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; +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.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.UUID; +import java.util.List; -import static com.minecolonies.api.util.constant.NbtTagConstants.*; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_TEXTURE_UUID; import static com.minecolonies.api.util.constant.SchematicTagConstants.TAG_SITTING; /** @@ -22,76 +29,35 @@ 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 - */ - private BlockPos sittingPosition = BlockPos.ZERO; - - /** - * The recruitment level, used for stats/equipment and costs + * The type of the visitor. */ - private ItemStack recruitCost = ItemStack.EMPTY; + private final IVisitorType visitorType; /** - * Texture UUID. + * The extra data instances. */ - private UUID textureUUID; + private 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); - } - - @Override - public CompoundTag serializeNBT() - { - 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); - if (textureUUID != null) - { - compoundNBT.putUUID(TAG_TEXTURE_UUID, textureUUID); - } - return compoundNBT; - } - - @Override - public void deserializeNBT(final CompoundTag nbtTagCompound) - { - 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)); - if (nbtTagCompound.contains(TAG_TEXTURE_UUID)) - { - this.textureUUID = nbtTagCompound.getUUID(TAG_TEXTURE_UUID); - } - } - - @Override - public void setRecruitCosts(final ItemStack item) - { - this.recruitCost = item; - } - - @Override - public ItemStack getRecruitCost() - { - return recruitCost; + this.visitorType = visitorType; + this.extraData = visitorType.getExtraDataKeys(); } /** @@ -103,38 +69,23 @@ public ItemStack getRecruitCost() */ public static IVisitorData loadVisitorFromNBT(final IColony colony, final CompoundTag nbt) { - final IVisitorData data = new VisitorData(nbt.getInt(TAG_ID), colony); + final ResourceLocation visitorTypeKey = new ResourceLocation(nbt.contains(TAG_VISITOR_TYPE) ? nbt.getString(TAG_VISITOR_TYPE) : ""); + 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 serializeViewNetworkData(@NotNull final FriendlyByteBuf buf) + public EntityType getEntityType() { - super.serializeViewNetworkData(buf); - buf.writeItem(recruitCost); - buf.writeInt(recruitCost.getCount()); - if (textureUUID == null) - { - buf.writeBoolean(false); - } - else - { - buf.writeBoolean(true); - buf.writeUUID(textureUUID); - } + return visitorType.getEntityType(); } @Override - public BlockPos getSittingPosition() + public T getExtraDataValue(final IVisitorExtraData extraData) { - return sittingPosition; - } - - @Override - public void setSittingPosition(final BlockPos pos) - { - this.sittingPosition = pos; + return this.extraData.stream().filter(f -> f.getKey().equals(extraData.getKey())).findFirst().orElse(extraData.getDefaultValue()); } @Override @@ -168,20 +119,56 @@ else if (getHomeBuilding() != null) } @Override - public void applyResearchEffects() + public void serializeViewNetworkData(@NotNull final FriendlyByteBuf buf) { - // no research effects for now + super.serializeViewNetworkData(buf); + buf.writeNbt(serializeNBT()); } @Override - public void setCustomTexture(final UUID texture) + public CompoundTag serializeNBT() { - this.textureUUID = texture; + final CompoundTag compound = super.serializeNBT(); + compound.putString(TAG_VISITOR_TYPE, visitorType.getId().toString()); + + 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 boolean hasCustomTexture() + public void deserializeNBT(final CompoundTag nbtTagCompound) { - return textureUUID != null; + super.deserializeNBT(nbtTagCompound); + for (final IVisitorExtraData extraDataKey : extraData) + { + if (nbtTagCompound.contains(extraDataKey.getKey())) + { + extraDataKey.deserializeNBT(nbtTagCompound.getCompound(extraDataKey.getKey())); + } + } + + // TODO: 1.20.2 Remove backwards compat for old visitor data + if (nbtTagCompound.contains(TAG_SITTING)) + { + regularVisitorData.setSittingPosition(BlockPosUtil.read(nbtTagCompound, TAG_SITTING)); + final ItemStack itemStack = ItemStack.of(nbtTagCompound.getCompound(TAG_RECRUIT_COST)); + itemStack.setCount(nbtTagCompound.getInt(TAG_RECRUIT_COST_QTY)); + regularVisitorData.setRecruitCost(itemStack); + if (nbtTagCompound.contains(TAG_TEXTURE_UUID)) + { + regularVisitorData.setTextureUUID(nbtTagCompound.getUUID(TAG_TEXTURE_UUID)); + } + } + } + + @Override + public void applyResearchEffects() + { + // no research effects for now } } diff --git a/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java b/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java index 0083185ea5b..ee2aac5cbc0 100644 --- a/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java +++ b/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java @@ -1,19 +1,21 @@ package com.minecolonies.coremod.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.AbstractEntityVisitor; +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 net.minecraft.world.entity.EntityType; import org.jetbrains.annotations.NotNull; -import java.util.Map; -import java.util.UUID; +import java.util.List; + +import static com.minecolonies.coremod.colony.VisitorData.TAG_EXTRA_DATA; +import static com.minecolonies.coremod.colony.VisitorData.TAG_VISITOR_TYPE; /** * View data for visitors @@ -21,19 +23,14 @@ public class VisitorDataView extends CitizenDataView implements IVisitorViewData { /** - * The recruitment costs - */ - private ItemStack recruitmentCosts; - - /** - * Texture UUID. + * The type of the visitor. */ - private UUID textureUUID; + 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. @@ -50,42 +47,31 @@ 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()); - if (buf.readBoolean()) + final CompoundTag compoundTag = buf.readNbt(); + if (compoundTag != null) { - textureUUID = buf.readUUID(); + final ResourceLocation visitorTypeKey = new ResourceLocation(compoundTag.getString(TAG_VISITOR_TYPE)); + visitorType = IMinecoloniesAPI.getInstance().getVisitorTypeRegistry().getValue(visitorTypeKey); + if (visitorType != null) + { + extraData = visitorType.createExtraData(); + if (extraData != null) + { + extraData.deserializeNBT(compoundTag.getCompound(TAG_EXTRA_DATA)); + } + } } } @Override - public ItemStack getRecruitCost() + public EntityType getEntityType() { - return recruitmentCosts; + return visitorType.getEntityType(); } @Override - public ResourceLocation getCustomTexture() + 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 null; } } diff --git a/src/main/java/com/minecolonies/coremod/colony/buildings/modules/FarmerFieldModule.java b/src/main/java/com/minecolonies/coremod/colony/buildings/modules/FarmerFieldModule.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/com/minecolonies/coremod/colony/managers/CitizenManager.java b/src/main/java/com/minecolonies/coremod/colony/managers/CitizenManager.java index 99d65bc88c4..1c136d07720 100755 --- a/src/main/java/com/minecolonies/coremod/colony/managers/CitizenManager.java +++ b/src/main/java/com/minecolonies/coremod/colony/managers/CitizenManager.java @@ -18,12 +18,14 @@ import com.minecolonies.api.util.NBTUtils; import com.minecolonies.api.util.WorldUtil; import com.minecolonies.api.util.constant.CitizenConstants; -import com.minecolonies.api.util.constant.Constants; import com.minecolonies.coremod.MineColonies; import com.minecolonies.coremod.Network; import com.minecolonies.coremod.colony.CitizenData; import com.minecolonies.coremod.colony.Colony; -import com.minecolonies.coremod.colony.buildings.modules.*; +import com.minecolonies.coremod.colony.buildings.modules.AbstractAssignedCitizenModule; +import com.minecolonies.coremod.colony.buildings.modules.BuildingModules; +import com.minecolonies.coremod.colony.buildings.modules.LivingBuildingModule; +import com.minecolonies.coremod.colony.buildings.modules.WorkAtHomeBuildingModule; import com.minecolonies.coremod.colony.colonyEvents.citizenEvents.CitizenSpawnedEvent; import com.minecolonies.coremod.colony.jobs.AbstractJobGuard; import com.minecolonies.coremod.colony.jobs.JobUndertaker; @@ -230,11 +232,11 @@ public void sendPackets( } @Override - public ICitizenData spawnOrCreateCivilian(@Nullable final ICivilianData data, final Level world, final BlockPos spawnPos, final boolean force) + public ICitizenData spawnOrCreateCivilian(@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; @@ -248,7 +250,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 { @@ -257,7 +259,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); } } @@ -265,7 +267,7 @@ public ICitizenData spawnOrCreateCivilian(@Nullable final ICivilianData data, fi } } - return (ICitizenData) data; + return data; } @NotNull diff --git a/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java b/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java index 660d2b2d70d..f5882e9566a 100644 --- a/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java +++ b/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java @@ -200,17 +200,11 @@ public IVisitorData getCivilian(final int citizenId) } @Override - public T getVisitor(int citizenId) - { - return (T) visitorMap.get(citizenId); - } - - @Override - public IVisitorData spawnOrCreateCivilian(ICivilianData data, final Level world, final BlockPos spawnPos, final boolean force) + public IVisitorData spawnOrCreateCivilian(IVisitorData data, final Level world, final BlockPos spawnPos, final boolean force) { if (!WorldUtil.isEntityBlockLoaded(world, spawnPos)) { - return (IVisitorData) data; + return data; } if (data == null) @@ -222,7 +216,7 @@ public IVisitorData spawnOrCreateCivilian(ICivilianData data, final Level world, if (citizenEntity == null) { - return (IVisitorData) data; + return data; } citizenEntity.setPos(spawnPos.getX() + HALF_A_BLOCK, spawnPos.getY() + SLIGHTLY_UP, spawnPos.getZ() + HALF_A_BLOCK); @@ -230,7 +224,7 @@ public IVisitorData spawnOrCreateCivilian(ICivilianData data, final Level world, citizenEntity.getCitizenColonyHandler().registerWithColony(data.getColony().getID(), data.getId()); - return (IVisitorData) data; + return data; } @Override diff --git a/src/main/java/com/minecolonies/coremod/entity/ai/visitor/EntityAIVisitor.java b/src/main/java/com/minecolonies/coremod/entity/ai/visitor/EntityAIVisitor.java index 6b14f21bed1..c487905f9c7 100644 --- a/src/main/java/com/minecolonies/coremod/entity/ai/visitor/EntityAIVisitor.java +++ b/src/main/java/com/minecolonies/coremod/entity/ai/visitor/EntityAIVisitor.java @@ -92,11 +92,11 @@ public EntityAIVisitor(@NotNull final AbstractEntityCitizen entity) */ private boolean reduceTime() { - citizen.getCitizenData().decreaseSaturation(0.02); - citizen.getCitizenData().markDirty(20 * 20); - if (citizen.getCitizenData().getSaturation() <= 0) + citizen.getVisitorData().decreaseSaturation(0.02); + citizen.getVisitorData().markDirty(20 * 20); + if (citizen.getVisitorData().getSaturation() <= 0) { - citizen.getCitizenColonyHandler().getColony().getVisitorManager().removeCivilian(citizen.getCitizenData()); + citizen.getCitizenColonyHandler().getColony().getVisitorManager().removeCivilian(citizen.getVisitorData()); if (tavern != null) { tavern.getFirstModuleOccurance(TavernBuildingModule.class).removeCitizen(citizen.getCivilianID()); @@ -174,7 +174,7 @@ private VisitorState decide() final BlockPos pos = tavern.getModule(BuildingModules.TAVERN_VISITOR).getFreeSitPosition(); if (pos != null) { - ((VisitorData) citizen.getCitizenData()).setSittingPosition(pos); + ((VisitorData) citizen.getVisitorData()).setSittingPosition(pos); citizen.isWorkerAtSiteWithMove(pos, 1); actionTimeoutCounter = citizen.getRandom().nextInt(2500) + 3000; return VisitorState.SITTING; @@ -199,7 +199,7 @@ private boolean sit() { if ((actionTimeoutCounter -= 50) <= 0) { - ((VisitorData) citizen.getCitizenData()).setSittingPosition(BlockPos.ZERO); + ((VisitorData) citizen.getVisitorData()).setSittingPosition(BlockPos.ZERO); return true; } @@ -208,7 +208,7 @@ private boolean sit() return false; } - final BlockPos moveTo = ((VisitorData) citizen.getCitizenData()).getSittingPosition(); + final BlockPos moveTo = ((VisitorData) citizen.getVisitorData()).getSittingPosition(); if (citizen.isWorkerAtSiteWithMove(moveTo, 1)) { SittingEntity.sitDown(moveTo, citizen, actionTimeoutCounter); @@ -223,18 +223,18 @@ private boolean sit() */ private boolean isEntityLoaded() { - if (citizen.getCitizenColonyHandler().getColony() == null || citizen.getCitizenData() == null || citizen.getCitizenData().getHomeBuilding() == null) + if (citizen.getCitizenColonyHandler().getColony() == null || citizen.getVisitorData() == null || citizen.getVisitorData().getHomeBuilding() == null) { return false; } - IBuilding building = citizen.getCitizenData().getHomeBuilding(); + IBuilding building = citizen.getVisitorData().getHomeBuilding(); if (building.hasModule(BuildingModules.TAVERN_VISITOR)) { tavern = (DefaultBuildingInstance) building; } - ((VisitorData) citizen.getCitizenData()).setSittingPosition(BlockPos.ZERO); + ((VisitorData) citizen.getVisitorData()).setSittingPosition(BlockPos.ZERO); return WorldUtil.isEntityBlockLoaded(citizen.level, citizen.blockPosition()); } @@ -272,7 +272,7 @@ private void onException(final RuntimeException e) */ private void resetLogic() { - ((VisitorData) citizen.getCitizenData()).setSittingPosition(BlockPos.ZERO); + ((VisitorData) citizen.getVisitorData()).setSittingPosition(BlockPos.ZERO); } /** diff --git a/src/main/java/com/minecolonies/coremod/entity/citizen/EntityCitizen.java b/src/main/java/com/minecolonies/coremod/entity/citizen/EntityCitizen.java index 51922fd4b36..d961ba574c7 100755 --- a/src/main/java/com/minecolonies/coremod/entity/citizen/EntityCitizen.java +++ b/src/main/java/com/minecolonies/coremod/entity/citizen/EntityCitizen.java @@ -1029,7 +1029,6 @@ public boolean isWorkerAtSiteWithMove(@NotNull final BlockPos site, final int ra * * @return the data. */ - @Override public ICitizenData getCitizenData() { return citizenData; diff --git a/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java b/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java index b31b6ace327..9d7c2c499be 100644 --- a/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java +++ b/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java @@ -7,8 +7,9 @@ import com.minecolonies.api.colony.requestsystem.location.ILocation; import com.minecolonies.api.entity.CustomGoalSelector; import com.minecolonies.api.entity.ai.pathfinding.IWalkToProxy; -import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; import com.minecolonies.api.entity.citizen.citizenhandlers.*; +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.*; @@ -20,7 +21,6 @@ import com.minecolonies.coremod.colony.buildings.modules.TavernBuildingModule; import com.minecolonies.coremod.entity.ai.minimal.EntityAIInteractToggleAble; import com.minecolonies.coremod.entity.ai.minimal.LookAtEntityGoal; -import com.minecolonies.coremod.entity.ai.visitor.EntityAIVisitor; import com.minecolonies.coremod.entity.citizen.citizenhandlers.*; import com.minecolonies.coremod.entity.pathfinding.EntityCitizenWalkToProxy; import com.minecolonies.coremod.entity.pathfinding.MovementHandler; @@ -64,36 +64,49 @@ /** * 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 chat handler. */ - private ICitizenChatHandler citizenChatHandler; + private ICitizenChatHandler citizenChatHandler; + /** * The citizen item handler. */ - private ICitizenItemHandler citizenItemHandler; + private ICitizenItemHandler citizenItemHandler; + /** * The citizen inv handler. */ @@ -103,10 +116,11 @@ * The citizen colony handler. */ private ICitizenColonyHandler citizenColonyHandler; + /** * The citizen job handler. */ - private ICitizenJobHandler citizenJobHandler; + private ICitizenJobHandler citizenJobHandler; /** * The citizen sleep handler. @@ -114,14 +128,13 @@ private ICitizenSleepHandler citizenSleepHandler; /** - * Citizen data view. + * The citizen experience handler */ - private ICitizenDataView citizenDataView; + private ICitizenExperienceHandler citizenExperienceHandler; /** - * The location used for requests + * The citizen disease handler. */ - private ILocation location = null; private ICitizenDiseaseHandler citizenDiseaseHandler; /** @@ -130,7 +143,7 @@ * @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.goalSelector = new CustomGoalSelector(this.goalSelector); @@ -144,12 +157,24 @@ public VisitorCitizen(final EntityType type, final Leve this.citizenExperienceHandler = new CitizenExperienceHandler(this); this.citizenDiseaseHandler = new CitizenDiseaseHandler(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; @@ -159,17 +184,7 @@ private void initTasks() this.goalSelector.addGoal(++priority, new InteractGoal(this, Player.class, WATCH_CLOSEST2, 1.0F)); this.goalSelector.addGoal(++priority, new InteractGoal(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 @@ -177,7 +192,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.hasModule(BuildingModules.TAVERN_VISITOR)) @@ -188,7 +203,7 @@ public boolean hurt(@NotNull final DamageSource damageSource, final float damage ICitizenData data = citizenColonyHandler.getColony().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); } } } @@ -212,44 +227,208 @@ public boolean hurt(@NotNull final DamageSource damageSource, final float damage return false; } + @Override + public void die(DamageSource cause) + { + super.die(cause); + if (!level.isClientSide()) + { + IColony colony = getCitizenColonyHandler().getColony(); + 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(); + } + } + } + + @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) + { + citizenItemHandler.entityDropItem(itemstack); + } + } + } + + @Override + public void onSyncedDataUpdated(EntityDataAccessor dataAccessor) + { + super.onSyncedDataUpdated(dataAccessor); + if (citizenColonyHandler != null) + { + citizenColonyHandler.onSyncDataUpdate(dataAccessor); + } + } + + @Nullable + @Override + public AbstractContainerMenu createMenu(final int id, final Inventory playerInventory, final Player playerEntity) + { + return new ContainerCitizenInventory(id, playerInventory, citizenColonyHandler.getColonyId(), citizenId); + } + /** - * Checks if a worker is at his working site. If he isn't, sets it's path to the location + * Direct interaction on right click * - * @param site the place where he should walk to - * @param range Range to check in - * @return True if worker is at site, otherwise false. + * @param player + * @param hand + * @return */ + private InteractionResult directPlayerInteraction(final Player player, final InteractionHand hand) + { + final ItemStack usedStack = player.getItemInHand(hand); + if (ISFOOD.test(usedStack)) + { + final ItemStack remainingItem = usedStack.finishUsingItem(level, this); + if (!remainingItem.isEmpty() && remainingItem.getItem() != usedStack.getItem()) + { + if (!player.getInventory().add(remainingItem)) + { + InventoryUtils.spawnItemStack( + player.level, + player.getX(), + player.getY(), + player.getZ(), + remainingItem + ); + } + } + + if (!level.isClientSide()) + { + getCitizenData().increaseSaturation(usedStack.getItem().getFoodProperties(usedStack, this).getNutrition()); + + playSound(SoundEvents.GENERIC_EAT, 1.5F, (float) SoundUtils.getRandomPitch(getRandom())); + // Position needs to be centered on citizen, Eat AI wrong too? + Network.getNetwork() + .sendToTrackingEntity(new ItemParticleEffectMessage(usedStack, + getX(), + getY(), + getZ(), + getXRot(), + getYRot(), + getEyeHeight()), this); + + citizenChatHandler.sendLocalizedChat(MESSAGE_INTERACTION_VISITOR_FOOD); + } + return InteractionResult.CONSUME; + } + return null; + } + @Override - public boolean isWorkerAtSiteWithMove(@NotNull final BlockPos site, final int range) + public ICitizenDataView getCitizenDataView() { - if (proxy == null) + if (this.citizenDataView == null) { - proxy = new EntityCitizenWalkToProxy(this); + 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; + } + } } - return proxy.walkToBlock(site, range, true); + else + { + return this.citizenDataView; + } + + return null; } - @Nullable @Override - public ICitizenData getCitizenData() + protected void defineSynchedData() { - return citizenData; + super.defineSynchedData(); + entityData.define(DATA_COLONY_ID, citizenColonyHandler == null ? 0 : citizenColonyHandler.getColonyId()); + entityData.define(DATA_CITIZEN_ID, citizenId); } @Override - public ICivilianData getCivilianData() + public void aiStep() { - return citizenData; + 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 setCivilianData(@Nullable final ICivilianData data) + public ILocation getLocation() { - if (data != null && data instanceof IVisitorData) + if (location == null) { - this.citizenData = (IVisitorData) data; - data.initEntityValues(); + 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; } /** @@ -271,18 +450,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) { @@ -304,10 +471,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); } } @@ -317,35 +484,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() { @@ -466,11 +611,48 @@ 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()); + } + + citizenDiseaseHandler.write(compound); + } + + @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); + } + } + + citizenDiseaseHandler.read(compound); } /** @@ -517,211 +699,53 @@ 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)) - { - final ItemStack remainingItem = usedStack.finishUsingItem(level, this); - if (!remainingItem.isEmpty() && remainingItem.getItem() != usedStack.getItem()) - { - if (!player.getInventory().add(remainingItem)) - { - InventoryUtils.spawnItemStack( - player.level, - player.getX(), - player.getY(), - player.getZ(), - remainingItem - ); - } - } - - if (!level.isClientSide()) - { - getCitizenData().increaseSaturation(usedStack.getItem().getFoodProperties(usedStack, this).getNutrition()); - - playSound(SoundEvents.GENERIC_EAT, 1.5f, (float) SoundUtils.getRandomPitch(getRandom())); - // Position needs to be centered on citizen, Eat AI wrong too? - Network.getNetwork() - .sendToTrackingEntity(new ItemParticleEffectMessage(usedStack, - getX(), - getY(), - getZ(), - getXRot(), - getYRot(), - getEyeHeight()), this); - - citizenChatHandler.sendLocalizedChat(MESSAGE_INTERACTION_VISITOR_FOOD); - } - 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() - { - super.defineSynchedData(); - entityData.define(DATA_COLONY_ID, citizenColonyHandler == null ? 0 : citizenColonyHandler.getColonyId()); - entityData.define(DATA_CITIZEN_ID, citizenId); - } - @Override - public void aiStep() + public IVisitorData getCivilianData() { - 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()); - } - - citizenDiseaseHandler.write(compound); - } - - @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); - } - } - - citizenDiseaseHandler.read(compound); + return visitorData; } @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().getColony(); - 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) - { - citizenItemHandler.entityDropItem(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; } -} +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java b/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java new file mode 100644 index 00000000000..b9ae6824bf3 --- /dev/null +++ b/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java @@ -0,0 +1,156 @@ +package com.minecolonies.coremod.entity.visitor; + +import com.minecolonies.api.colony.IVisitorViewData; +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.SoundUtils; +import com.minecolonies.coremod.Network; +import com.minecolonies.coremod.entity.ai.visitor.EntityAIVisitor; +import com.minecolonies.coremod.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.entity.EntityType; +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.Consumer; + +import static com.minecolonies.api.util.ItemStackUtils.ISFOOD; +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 +{ + public static final SittingPositionData EXTRA_DATA_SITTING_POSITION = new SittingPositionData(); + + @Override + public ResourceLocation getId() + { + return ModVisitorTypes.VISITOR_TYPE_ID; + } + + @Override + public EntityType getEntityType() + { + return ModEntities.VISITOR; + } + + @Override + public void createStateMachine(final AbstractEntityVisitor visitor) + { + new EntityAIVisitor(visitor); + } + + @Override + public List> getExtraDataKeys() + { + return List.of(EXTRA_DATA_SITTING_POSITION); + } + + @Override + public @NotNull InteractionResult onPlayerInteraction(final AbstractEntityVisitor visitor, final Player player, final Level level, final InteractionHand 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); + + visitor.getCitizenChatHandler().sendLocalizedChat(MESSAGE_INTERACTION_VISITOR_FOOD); + } + return InteractionResult.CONSUME; + } + return InteractionResult.PASS; + } + + @Override + public void getCustomTexture(final IVisitorViewData visitor, final ResourceLocation cachedTexture, final Consumer cacheSetter) + { + if (cachedTexture != null) + { + return; + } + + + // + //if (!(visitor.getExtraData() instanceof RegularVisitorType.RegularVisitorData regularVisitorData)) + //{ + // return; + //} + //if (regularVisitorData.textureUUID == null) + //{ + // return; + //} + // + //cacheSetter.accept(DefaultPlayerSkin.getDefaultSkin(regularVisitorData.textureUUID)); + //Util.backgroundExecutor().execute(() -> + //{ + // Minecraft minecraft = Minecraft.getInstance(); + // final GameProfile profile = new GameProfile(regularVisitorData.textureUUID, "mcoltexturequery"); + // minecraft.getMinecraftSessionService().fillProfileProperties(profile, true); + // Map map = minecraft.getSkinManager().getInsecureSkinInformation(profile); + // if (!map.isEmpty()) + // { + // cacheSetter.accept(minecraft.getSkinManager().registerTexture(map.get(MinecraftProfileTexture.Type.SKIN), MinecraftProfileTexture.Type.SKIN)); + // } + //}); + } + + public static class SittingPositionData extends AbstractVisitorExtraData + { + @Override + public String getKey() + { + return "sitting-pos"; + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + BlockPosUtil.write(compound, "value", getValue()); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + setValue(BlockPosUtil.read(compoundTag, "value")); + } + } +} \ No newline at end of file From da9e661996a8e7219ac98b932ecf3e71f7446f1f Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Mon, 15 Jan 2024 16:13:57 +0100 Subject: [PATCH 02/94] Finish off visitor type system --- .../com/minecolonies/api/colony/IColony.java | 7 -- .../minecolonies/api/colony/IVisitorData.java | 11 ++- .../visitor/AbstractVisitorExtraData.java | 54 ++++++++++- .../api/entity/visitor/IVisitorExtraData.java | 11 +++ .../api/entity/visitor/IVisitorType.java | 11 --- .../coremod/colony/VisitorData.java | 31 +++++-- .../coremod/colony/VisitorDataView.java | 17 +++- .../modules/TavernBuildingModule.java | 24 +++-- .../RecruitmentInteraction.java | 23 +++-- .../colony/managers/VisitorManager.java | 22 ++++- .../datalistener/CustomVisitorListener.java | 16 ++-- .../entity/ai/visitor/EntityAIVisitor.java | 25 ++--- .../entity/citizen/VisitorCitizen.java | 62 ++----------- .../entity/visitor/RegularVisitorType.java | 92 ++++++++++++------- .../coremod/event/EventHandler.java | 10 +- .../server/colony/InteractionClose.java | 2 +- .../server/colony/InteractionResponse.java | 2 +- 17 files changed, 242 insertions(+), 178 deletions(-) diff --git a/src/api/java/com/minecolonies/api/colony/IColony.java b/src/api/java/com/minecolonies/api/colony/IColony.java index 4bfee1ec3d1..e21f3d0619b 100755 --- a/src/api/java/com/minecolonies/api/colony/IColony.java +++ b/src/api/java/com/minecolonies/api/colony/IColony.java @@ -490,13 +490,6 @@ default List getWayPoints(@NotNull BlockPos position, @NotNull BlockPo */ IQuestManager getQuestManager(); - /** - * Get the expedition manager of the colony. - * - * @return the expedition manager. - */ - IExpeditionManager getExpeditionManager(); - /** * Get citizen from colony. * @param id the id of the cit. diff --git a/src/api/java/com/minecolonies/api/colony/IVisitorData.java b/src/api/java/com/minecolonies/api/colony/IVisitorData.java index a0b635cd446..138fc6574e6 100644 --- a/src/api/java/com/minecolonies/api/colony/IVisitorData.java +++ b/src/api/java/com/minecolonies/api/colony/IVisitorData.java @@ -19,7 +19,16 @@ public interface IVisitorData extends ICitizenData /** * Get any bit of additional information for this visitor. * + * @param extraData the extra data key. * @return the extra data container. */ - T getExtraDataValue(final IVisitorExtraData extraData); + T getExtraDataValue(final IVisitorExtraData extraData); + + /** + * Set any bit of additional information for this visitor. + * + * @param extraData the extra data key. + * @param value the new value for the extra data key. + */ + void setExtraDataValue(final IVisitorExtraData extraData, final T value); } diff --git a/src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java b/src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java index e0cb44ee43e..5e26ed541b2 100644 --- a/src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java +++ b/src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java @@ -1,24 +1,70 @@ package com.minecolonies.api.entity.visitor; +import org.jetbrains.annotations.NotNull; + public abstract class AbstractVisitorExtraData implements IVisitorExtraData { - private String key; + @NotNull + private final String key; + + @NotNull + private final S defaultValue; + @NotNull private S value; - public AbstractVisitorExtraData() + protected AbstractVisitorExtraData(@NotNull final String key, @NotNull final S defaultValue) { + this.key = key; + this.defaultValue = defaultValue; + this.value = defaultValue; + } + @Override + public final @NotNull String getKey() + { + return key; } @Override - public final S getValue() + public final @NotNull S getValue() { return value; } - protected final void setValue(final S value) + @Override + public final void setValue(@NotNull final S value) { this.value = value; } + + @NotNull + @Override + public final S getDefaultValue() + { + return defaultValue; + } + + @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/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java index 0e10a715840..b1a9ea4ccb2 100644 --- a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java +++ b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java @@ -2,6 +2,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraftforge.common.util.INBTSerializable; +import org.jetbrains.annotations.NotNull; /** * Interface for extra visitor data. @@ -13,6 +14,7 @@ public interface IVisitorExtraData extends INBTSerializable * * @return the key. */ + @NotNull String getKey(); /** @@ -20,12 +22,21 @@ public interface IVisitorExtraData extends INBTSerializable * * @return the value. */ + @NotNull S getValue(); + /** + * Set the new value for this extra data key. + * + * @param value the new value. + */ + void setValue(S value); + /** * Get the default value in case no value was explicitly set yet. * * @return the value. */ + @NotNull S getDefaultValue(); } \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java index 5a58547c0fb..9d4bc49b643 100644 --- a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java +++ b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java @@ -1,6 +1,5 @@ package com.minecolonies.api.entity.visitor; -import com.minecolonies.api.colony.IVisitorViewData; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -10,7 +9,6 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -import java.util.function.Consumer; /** * Specific handler actions for interacting with different types of visitors. @@ -62,13 +60,4 @@ default InteractionResult onPlayerInteraction(final AbstractEntityVisitor visito // Nothing should happen by default. return InteractionResult.PASS; } - - /** - * Get the custom texture for a visitor. - * - * @param visitor the visitor view data instance. - * @param cachedTexture the currently cached texture. - * @param cacheSetter the callable action to update the cached value. - */ - void getCustomTexture(final IVisitorViewData visitor, final ResourceLocation cachedTexture, final Consumer cacheSetter); } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java index 2f35209e8b2..3c2e9d769f9 100644 --- a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java @@ -18,10 +18,12 @@ import org.jetbrains.annotations.NotNull; import java.util.List; +import java.util.Optional; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_TEXTURE_UUID; import static com.minecolonies.api.util.constant.SchematicTagConstants.TAG_SITTING; +import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.*; /** * Data for visitors @@ -57,7 +59,7 @@ public VisitorData(final int id, final IColony colony, final IVisitorType visito { super(id, colony); this.visitorType = visitorType; - this.extraData = visitorType.getExtraDataKeys(); + this.extraData = List.copyOf(visitorType.getExtraDataKeys()); } /** @@ -83,9 +85,26 @@ public EntityType getEntityType() } @Override + @SuppressWarnings("unchecked") public T getExtraDataValue(final IVisitorExtraData extraData) { - return this.extraData.stream().filter(f -> f.getKey().equals(extraData.getKey())).findFirst().orElse(extraData.getDefaultValue()); + return this.extraData.stream() + .filter(f -> f.equals(extraData)) + .map(m -> (T) m.getValue()) + .findFirst() + .orElseThrow(); + } + + @Override + @SuppressWarnings("unchecked") + public void setExtraDataValue(final IVisitorExtraData extraData, final T value) + { + final IVisitorExtraData foundExtraData = this.extraData.stream() + .filter(f -> f.equals(extraData)) + .map(m -> (IVisitorExtraData) m) + .findFirst() + .orElseThrow(); + foundExtraData.setValue(value); } @Override @@ -152,16 +171,16 @@ public void deserializeNBT(final CompoundTag nbtTagCompound) } } - // TODO: 1.20.2 Remove backwards compat for old visitor data + // TODO: Next major release: Remove backwards compat for old visitor data if (nbtTagCompound.contains(TAG_SITTING)) { - regularVisitorData.setSittingPosition(BlockPosUtil.read(nbtTagCompound, TAG_SITTING)); + 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)); - regularVisitorData.setRecruitCost(itemStack); + setExtraDataValue(EXTRA_DATA_RECRUIT_COST, itemStack); if (nbtTagCompound.contains(TAG_TEXTURE_UUID)) { - regularVisitorData.setTextureUUID(nbtTagCompound.getUUID(TAG_TEXTURE_UUID)); + setExtraDataValue(EXTRA_DATA_CUSTOM_TEXTURE, Optional.of(nbtTagCompound.getUUID(TAG_TEXTURE_UUID))); } } } diff --git a/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java b/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java index ee2aac5cbc0..83d3f8d3205 100644 --- a/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java +++ b/src/main/java/com/minecolonies/coremod/colony/VisitorDataView.java @@ -54,10 +54,12 @@ public void deserialize(@NotNull final FriendlyByteBuf buf) visitorType = IMinecoloniesAPI.getInstance().getVisitorTypeRegistry().getValue(visitorTypeKey); if (visitorType != null) { - extraData = visitorType.createExtraData(); - if (extraData != null) + extraData = visitorType.getExtraDataKeys(); + + final CompoundTag compound = compoundTag.getCompound(TAG_EXTRA_DATA); + for (final IVisitorExtraData extraDataKey : extraData) { - extraData.deserializeNBT(compoundTag.getCompound(TAG_EXTRA_DATA)); + extraDataKey.deserializeNBT(compound.getCompound(extraDataKey.getKey())); } } } @@ -70,8 +72,13 @@ public EntityType getEntityType() } @Override - public T getExtraDataValue(final IVisitorExtraData extraData) + @SuppressWarnings("unchecked") + public T getExtraDataValue(final IVisitorExtraData extraData) { - return null; + return this.extraData.stream() + .filter(f -> f.equals(extraData)) + .map(m -> (T) m.getValue()) + .findFirst() + .orElseThrow(); } } diff --git a/src/main/java/com/minecolonies/coremod/colony/buildings/modules/TavernBuildingModule.java b/src/main/java/com/minecolonies/coremod/colony/buildings/modules/TavernBuildingModule.java index c654aac4804..314cec33eec 100644 --- a/src/main/java/com/minecolonies/coremod/colony/buildings/modules/TavernBuildingModule.java +++ b/src/main/java/com/minecolonies/coremod/colony/buildings/modules/TavernBuildingModule.java @@ -39,6 +39,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.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; +import static com.minecolonies.coremod.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 +113,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 +148,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) { @@ -181,11 +184,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 +224,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)) { @@ -291,10 +289,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 +309,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/coremod/colony/interactionhandling/RecruitmentInteraction.java b/src/main/java/com/minecolonies/coremod/colony/interactionhandling/RecruitmentInteraction.java index a147dd048a6..b39ee1a4367 100644 --- a/src/main/java/com/minecolonies/coremod/colony/interactionhandling/RecruitmentInteraction.java +++ b/src/main/java/com/minecolonies/coremod/colony/interactionhandling/RecruitmentInteraction.java @@ -34,6 +34,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.coremod.client.gui.WindowInteraction.BUTTON_RESPONSE_ID; +import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; /** * Interaction for recruiting visitors @@ -92,11 +93,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) + if (recruitButton != null && dataView instanceof IVisitorViewData visitorViewData) { - final ItemStack recruitCost = ((IVisitorViewData) dataView).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() + ": ")) @@ -115,7 +115,7 @@ public void onWindowOpened(final BOWindow window, final ICitizenDataView dataVie icon.setID(RECRUITMENT_ICON); icon.setSize(15, 15); group.addChild(icon); - icon.setItem(((IVisitorViewData) dataView).getRecruitCost()); + icon.setItem(recruitCost); icon.setPosition(iconPosX, iconPosY); icon.setVisible(true); } @@ -127,10 +127,10 @@ public boolean onClientResponseTriggered(final int responseId, final Player play { final Component response = getPossibleResponses().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); } @@ -146,14 +146,13 @@ public boolean onClientResponseTriggered(final int responseId, final Player play 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) + if (response.equals(recruitAnswer.getA()) && data instanceof IVisitorData visitorViewData) { 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 = visitorViewData.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); diff --git a/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java b/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java index f5882e9566a..c57eabb4941 100644 --- a/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java +++ b/src/main/java/com/minecolonies/coremod/colony/managers/VisitorManager.java @@ -8,19 +8,20 @@ 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.IVisitorType; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; import com.minecolonies.api.util.WorldUtil; import com.minecolonies.coremod.Network; import com.minecolonies.coremod.colony.VisitorData; import com.minecolonies.coremod.entity.citizen.VisitorCitizen; import com.minecolonies.coremod.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.*; @@ -229,9 +230,20 @@ public IVisitorData spawnOrCreateCivilian(IVisitorData data, final Level world, @Override public IVisitorData createAndRegisterCivilianData() + { + return createAndRegisterVisitor(ModVisitorTypes.visitor.get()); + } + + /** + * Create visitor data for the given visitor type. + * + * @param visitorType the input visitor type. + * @return the generated visitor data. + */ + private IVisitorData createAndRegisterVisitor(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; diff --git a/src/main/java/com/minecolonies/coremod/datalistener/CustomVisitorListener.java b/src/main/java/com/minecolonies/coremod/datalistener/CustomVisitorListener.java index 98560ea7645..42d9de7776b 100644 --- a/src/main/java/com/minecolonies/coremod/datalistener/CustomVisitorListener.java +++ b/src/main/java/com/minecolonies/coremod/datalistener/CustomVisitorListener.java @@ -10,17 +10,21 @@ import com.minecolonies.api.util.ItemStackUtils; import com.minecolonies.api.util.Log; import com.minecolonies.coremod.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.Optional; import java.util.Random; import java.util.UUID; +import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_CUSTOM_TEXTURE; +import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; + /** * Loads and listens to custom visitor data added */ @@ -186,7 +190,7 @@ public void applyToVisitor(final IVisitorData visitorData) { if (texture != null) { - visitorData.setCustomTexture(texture); + visitorData.setExtraDataValue(EXTRA_DATA_CUSTOM_TEXTURE, Optional.of(texture)); } if (gender != null) @@ -206,7 +210,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/coremod/entity/ai/visitor/EntityAIVisitor.java b/src/main/java/com/minecolonies/coremod/entity/ai/visitor/EntityAIVisitor.java index c487905f9c7..9f90903c25a 100644 --- a/src/main/java/com/minecolonies/coremod/entity/ai/visitor/EntityAIVisitor.java +++ b/src/main/java/com/minecolonies/coremod/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.coremod.colony.VisitorData; import com.minecolonies.coremod.colony.buildings.DefaultBuildingInstance; import com.minecolonies.coremod.colony.buildings.modules.BuildingModules; import com.minecolonies.coremod.colony.buildings.modules.TavernBuildingModule; @@ -20,6 +19,8 @@ import net.minecraft.world.entity.Entity; import org.jetbrains.annotations.NotNull; +import static com.minecolonies.coremod.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 */ @@ -92,11 +93,11 @@ public EntityAIVisitor(@NotNull final AbstractEntityCitizen entity) */ private boolean reduceTime() { - citizen.getVisitorData().decreaseSaturation(0.02); - citizen.getVisitorData().markDirty(20 * 20); - if (citizen.getVisitorData().getSaturation() <= 0) + citizen.getCitizenData().decreaseSaturation(0.02); + citizen.getCitizenData().markDirty(20 * 20); + if (citizen.getCitizenData().getSaturation() <= 0) { - citizen.getCitizenColonyHandler().getColony().getVisitorManager().removeCivilian(citizen.getVisitorData()); + citizen.getCitizenColonyHandler().getColony().getVisitorManager().removeCivilian(citizen.getCitizenData()); if (tavern != null) { tavern.getFirstModuleOccurance(TavernBuildingModule.class).removeCitizen(citizen.getCivilianID()); @@ -174,7 +175,7 @@ private VisitorState decide() final BlockPos pos = tavern.getModule(BuildingModules.TAVERN_VISITOR).getFreeSitPosition(); if (pos != null) { - ((VisitorData) citizen.getVisitorData()).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.getVisitorData()).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.getVisitorData()).getSittingPosition(); + final BlockPos moveTo = citizen.getCitizenData().getExtraDataValue(EXTRA_DATA_SITTING_POSITION); if (citizen.isWorkerAtSiteWithMove(moveTo, 1)) { SittingEntity.sitDown(moveTo, citizen, actionTimeoutCounter); @@ -223,18 +224,18 @@ private boolean sit() */ private boolean isEntityLoaded() { - if (citizen.getCitizenColonyHandler().getColony() == null || citizen.getVisitorData() == null || citizen.getVisitorData().getHomeBuilding() == null) + if (citizen.getCitizenColonyHandler().getColony() == null || citizen.getCitizenData() == null || citizen.getCitizenData().getHomeBuilding() == null) { return false; } - IBuilding building = citizen.getVisitorData().getHomeBuilding(); + IBuilding building = citizen.getCitizenData().getHomeBuilding(); if (building.hasModule(BuildingModules.TAVERN_VISITOR)) { tavern = (DefaultBuildingInstance) building; } - ((VisitorData) citizen.getVisitorData()).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.getVisitorData()).setSittingPosition(BlockPos.ZERO); + citizen.getCitizenData().setExtraDataValue(EXTRA_DATA_SITTING_POSITION, BlockPos.ZERO); } /** diff --git a/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java b/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java index 9d7c2c499be..9a0cdf0bcf4 100644 --- a/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java +++ b/src/main/java/com/minecolonies/coremod/entity/citizen/VisitorCitizen.java @@ -12,7 +12,10 @@ 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.BlockPosUtil; +import com.minecolonies.api.util.CompatibilityUtils; +import com.minecolonies.api.util.ItemStackUtils; +import com.minecolonies.api.util.MessageUtils; import com.minecolonies.api.util.MessageUtils.MessagePriority; import com.minecolonies.api.util.constant.TypeConstants; import com.minecolonies.coremod.MineColonies; @@ -24,14 +27,12 @@ import com.minecolonies.coremod.entity.citizen.citizenhandlers.*; import com.minecolonies.coremod.entity.pathfinding.EntityCitizenWalkToProxy; import com.minecolonies.coremod.entity.pathfinding.MovementHandler; -import com.minecolonies.coremod.network.messages.client.ItemParticleEffectMessage; import com.minecolonies.coremod.network.messages.server.colony.OpenInventoryMessage; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; 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; @@ -52,13 +53,11 @@ 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.coremod.entity.ai.minimal.EntityAIInteractToggleAble.*; /** @@ -284,55 +283,6 @@ public AbstractContainerMenu createMenu(final int id, final Inventory playerInve return new ContainerCitizenInventory(id, playerInventory, citizenColonyHandler.getColonyId(), citizenId); } - /** - * 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)) - { - final ItemStack remainingItem = usedStack.finishUsingItem(level, this); - if (!remainingItem.isEmpty() && remainingItem.getItem() != usedStack.getItem()) - { - if (!player.getInventory().add(remainingItem)) - { - InventoryUtils.spawnItemStack( - player.level, - player.getX(), - player.getY(), - player.getZ(), - remainingItem - ); - } - } - - if (!level.isClientSide()) - { - getCitizenData().increaseSaturation(usedStack.getItem().getFoodProperties(usedStack, this).getNutrition()); - - playSound(SoundEvents.GENERIC_EAT, 1.5F, (float) SoundUtils.getRandomPitch(getRandom())); - // Position needs to be centered on citizen, Eat AI wrong too? - Network.getNetwork() - .sendToTrackingEntity(new ItemParticleEffectMessage(usedStack, - getX(), - getY(), - getZ(), - getXRot(), - getYRot(), - getEyeHeight()), this); - - citizenChatHandler.sendLocalizedChat(MESSAGE_INTERACTION_VISITOR_FOOD); - } - return InteractionResult.CONSUME; - } - return null; - } - @Override public ICitizenDataView getCitizenDataView() { @@ -675,8 +625,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; } diff --git a/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java b/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java index b9ae6824bf3..1fbbac87641 100644 --- a/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java +++ b/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java @@ -1,6 +1,5 @@ package com.minecolonies.coremod.entity.visitor; -import com.minecolonies.api.colony.IVisitorViewData; import com.minecolonies.api.entity.ModEntities; import com.minecolonies.api.entity.visitor.*; import com.minecolonies.api.util.BlockPosUtil; @@ -22,7 +21,8 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -import java.util.function.Consumer; +import java.util.Optional; +import java.util.UUID; import static com.minecolonies.api.util.ItemStackUtils.ISFOOD; import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INTERACTION_VISITOR_FOOD; @@ -32,7 +32,12 @@ */ 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(); + public static final CustomTextureData EXTRA_DATA_CUSTOM_TEXTURE = new CustomTextureData(); @Override public ResourceLocation getId() @@ -55,7 +60,7 @@ public void createStateMachine(final AbstractEntityVisitor visitor) @Override public List> getExtraDataKeys() { - return List.of(EXTRA_DATA_SITTING_POSITION); + return List.of(EXTRA_DATA_SITTING_POSITION, EXTRA_DATA_RECRUIT_COST, EXTRA_DATA_CUSTOM_TEXTURE); } @Override @@ -98,59 +103,78 @@ public List> getExtraDataKeys() return InteractionResult.PASS; } - @Override - public void getCustomTexture(final IVisitorViewData visitor, final ResourceLocation cachedTexture, final Consumer cacheSetter) + public static class SittingPositionData extends AbstractVisitorExtraData { - if (cachedTexture != null) + private static final String TAG_VALUE = "value"; + + public SittingPositionData() { - return; + super("sitting-pos", BlockPos.ZERO); } + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + BlockPosUtil.write(compound, TAG_VALUE, getValue()); + return compound; + } - // - //if (!(visitor.getExtraData() instanceof RegularVisitorType.RegularVisitorData regularVisitorData)) - //{ - // return; - //} - //if (regularVisitorData.textureUUID == null) - //{ - // return; - //} - // - //cacheSetter.accept(DefaultPlayerSkin.getDefaultSkin(regularVisitorData.textureUUID)); - //Util.backgroundExecutor().execute(() -> - //{ - // Minecraft minecraft = Minecraft.getInstance(); - // final GameProfile profile = new GameProfile(regularVisitorData.textureUUID, "mcoltexturequery"); - // minecraft.getMinecraftSessionService().fillProfileProperties(profile, true); - // Map map = minecraft.getSkinManager().getInsecureSkinInformation(profile); - // if (!map.isEmpty()) - // { - // cacheSetter.accept(minecraft.getSkinManager().registerTexture(map.get(MinecraftProfileTexture.Type.SKIN), MinecraftProfileTexture.Type.SKIN)); - // } - //}); + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + setValue(BlockPosUtil.read(compoundTag, TAG_VALUE)); + } } - public static class SittingPositionData extends AbstractVisitorExtraData + public static class RecruitCostData extends AbstractVisitorExtraData { + private static final String TAG_VALUE = "value"; + + public RecruitCostData() + { + super("recruit-cost", ItemStack.EMPTY); + } + @Override - public String getKey() + public CompoundTag serializeNBT() { - return "sitting-pos"; + 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))); + } + } + + public static class CustomTextureData extends AbstractVisitorExtraData> + { + private static final String TAG_VALUE = "value"; + + public CustomTextureData() + { + super("custom-texture", Optional.empty()); } @Override public CompoundTag serializeNBT() { final CompoundTag compound = new CompoundTag(); - BlockPosUtil.write(compound, "value", getValue()); + getValue().ifPresent(val -> compound.putUUID(TAG_VALUE, val)); return compound; } @Override public void deserializeNBT(final CompoundTag compoundTag) { - setValue(BlockPosUtil.read(compoundTag, "value")); + if (compoundTag.contains(TAG_VALUE)) + { + setValue(Optional.of(compoundTag.getUUID(TAG_VALUE))); + } } } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/coremod/event/EventHandler.java b/src/main/java/com/minecolonies/coremod/event/EventHandler.java index 63276f0b14f..aae66baec3d 100755 --- a/src/main/java/com/minecolonies/coremod/event/EventHandler.java +++ b/src/main/java/com/minecolonies/coremod/event/EventHandler.java @@ -65,7 +65,10 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BedPart; import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraftforge.event.*; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.EntityJoinLevelEvent; import net.minecraftforge.event.entity.EntityTravelToDimensionEvent; import net.minecraftforge.event.entity.living.LivingConversionEvent; @@ -79,11 +82,9 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import org.jetbrains.annotations.NotNull; -import java.awt.*; import java.time.LocalDateTime; import java.time.Month; import java.util.*; -import java.util.List; import static com.minecolonies.api.colony.IColony.CLOSE_COLONY_CAP; import static com.minecolonies.api.research.util.ResearchConstants.SOFT_SHOES; @@ -91,6 +92,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.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; import static net.minecraftforge.eventbus.api.EventPriority.HIGHEST; import static net.minecraftforge.eventbus.api.EventPriority.LOWEST; @@ -766,7 +768,7 @@ 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)); } diff --git a/src/main/java/com/minecolonies/coremod/network/messages/server/colony/InteractionClose.java b/src/main/java/com/minecolonies/coremod/network/messages/server/colony/InteractionClose.java index ba4952b2f60..09d422594ca 100755 --- a/src/main/java/com/minecolonies/coremod/network/messages/server/colony/InteractionClose.java +++ b/src/main/java/com/minecolonies/coremod/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/coremod/network/messages/server/colony/InteractionResponse.java b/src/main/java/com/minecolonies/coremod/network/messages/server/colony/InteractionResponse.java index 2ea0759ae37..1c880720ae3 100755 --- a/src/main/java/com/minecolonies/coremod/network/messages/server/colony/InteractionResponse.java +++ b/src/main/java/com/minecolonies/coremod/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) From 35c7eb2dc54c51042a39f09c4d8934412b110e1e Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Mon, 15 Jan 2024 16:38:03 +0100 Subject: [PATCH 03/94] Final --- src/main/java/com/minecolonies/coremod/colony/VisitorData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java index 3c2e9d769f9..5127a8330ab 100644 --- a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java @@ -46,7 +46,7 @@ public class VisitorData extends CitizenData implements IVisitorData /** * The extra data instances. */ - private List> extraData; + private final List> extraData; /** * Create a VisitorData given an ID. Used as a super-constructor or during loading. From 513380efd5c17178622c260ae0a8922d8e94f991 Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Mon, 15 Jan 2024 16:39:37 +0100 Subject: [PATCH 04/94] Fix --- src/main/java/com/minecolonies/coremod/colony/VisitorData.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java index 5127a8330ab..d241d2057e5 100644 --- a/src/main/java/com/minecolonies/coremod/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/coremod/colony/VisitorData.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Optional; +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.NbtTagConstants.TAG_TEXTURE_UUID; import static com.minecolonies.api.util.constant.SchematicTagConstants.TAG_SITTING; @@ -71,7 +72,7 @@ public VisitorData(final int id, final IColony colony, final IVisitorType visito */ public static IVisitorData loadVisitorFromNBT(final IColony colony, final CompoundTag nbt) { - final ResourceLocation visitorTypeKey = new ResourceLocation(nbt.contains(TAG_VISITOR_TYPE) ? nbt.getString(TAG_VISITOR_TYPE) : ""); + 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); From 5ca75e1bead954d6a5d0865b0e66347705602b8c Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Wed, 17 Jan 2024 17:07:24 +0100 Subject: [PATCH 05/94] Fixes --- .../apiimp/initializer/EntityInitializer.java | 1 + .../apiimp/initializer/ModVisitorTypesInitializer.java | 2 +- .../java/com/minecolonies/core/colony/VisitorData.java | 2 +- .../com/minecolonies/core/colony/VisitorDataView.java | 4 ++-- .../colony/buildings/modules/TavernBuildingModule.java | 4 ++-- .../minecolonies/core/colony/managers/VisitorManager.java | 2 +- .../core/datalistener/CustomVisitorListener.java | 7 +++++-- .../core/entity/ai/visitor/EntityAIVisitor.java | 3 +-- .../entity/visitor/RegularVisitorType.java | 8 ++++---- .../java/com/minecolonies/core/event/EventHandler.java | 2 +- 10 files changed, 19 insertions(+), 16 deletions(-) rename src/main/java/com/minecolonies/{coremod => core}/entity/visitor/RegularVisitorType.java (96%) diff --git a/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java index 84a69411918..499070a5c72 100644 --- a/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java @@ -22,6 +22,7 @@ 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.RegularVisitorType; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java index eafb11d9974..f654f11cdec 100644 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java @@ -3,7 +3,7 @@ import com.minecolonies.api.entity.visitor.IVisitorType; import com.minecolonies.api.entity.visitor.ModVisitorTypes; import com.minecolonies.api.util.constant.Constants; -import com.minecolonies.coremod.entity.visitor.RegularVisitorType; +import com.minecolonies.core.entity.visitor.RegularVisitorType; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.registries.DeferredRegister; diff --git a/src/main/java/com/minecolonies/core/colony/VisitorData.java b/src/main/java/com/minecolonies/core/colony/VisitorData.java index 4286ed86531..b5ef8e456c1 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorData.java @@ -24,7 +24,7 @@ import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_TEXTURE_UUID; import static com.minecolonies.api.util.constant.SchematicTagConstants.TAG_SITTING; -import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.*; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.*; /** * Data for visitors diff --git a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java index 4fe67307d16..8dc112bb2a6 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java @@ -14,8 +14,8 @@ import java.util.List; -import static com.minecolonies.coremod.colony.VisitorData.TAG_EXTRA_DATA; -import static com.minecolonies.coremod.colony.VisitorData.TAG_VISITOR_TYPE; +import static com.minecolonies.core.colony.VisitorData.TAG_EXTRA_DATA; +import static com.minecolonies.core.colony.VisitorData.TAG_VISITOR_TYPE; /** * View data for visitors 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 b72c986394e..f125beb034e 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 @@ -39,8 +39,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.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; -import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_SITTING_POSITION; +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 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 5e72fdc8db8..321b7167504 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java @@ -15,7 +15,7 @@ import com.minecolonies.core.colony.VisitorData; import com.minecolonies.core.entity.citizen.VisitorCitizen; 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.ListTag; import net.minecraft.nbt.Tag; diff --git a/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java b/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java index 59d93762ca0..19731a5eb22 100644 --- a/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java +++ b/src/main/java/com/minecolonies/core/datalistener/CustomVisitorListener.java @@ -10,6 +10,9 @@ import com.minecolonies.api.util.ItemStackUtils; import com.minecolonies.api.util.Log; 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.util.profiling.ProfilerFiller; import net.minecraft.world.item.ItemStack; @@ -19,8 +22,8 @@ import java.util.Random; import java.util.UUID; -import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_CUSTOM_TEXTURE; -import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_CUSTOM_TEXTURE; +import static com.minecolonies.core.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; /** * Loads and listens to custom visitor data added 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 3a4879bd58d..22732e43170 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,7 +19,7 @@ import net.minecraft.world.entity.Entity; import org.jetbrains.annotations.NotNull; -import static com.minecolonies.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_SITTING_POSITION; +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 diff --git a/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java similarity index 96% rename from src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java rename to src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java index 1fbbac87641..4d1cb72de45 100644 --- a/src/main/java/com/minecolonies/coremod/entity/visitor/RegularVisitorType.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java @@ -1,13 +1,13 @@ -package com.minecolonies.coremod.entity.visitor; +package com.minecolonies.core.entity.visitor; 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.SoundUtils; -import com.minecolonies.coremod.Network; -import com.minecolonies.coremod.entity.ai.visitor.EntityAIVisitor; -import com.minecolonies.coremod.network.messages.client.ItemParticleEffectMessage; +import com.minecolonies.core.Network; +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; diff --git a/src/main/java/com/minecolonies/core/event/EventHandler.java b/src/main/java/com/minecolonies/core/event/EventHandler.java index ffe538b89d5..7a5e884fdc8 100755 --- a/src/main/java/com/minecolonies/core/event/EventHandler.java +++ b/src/main/java/com/minecolonies/core/event/EventHandler.java @@ -90,7 +90,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.coremod.entity.visitor.RegularVisitorType.EXTRA_DATA_RECRUIT_COST; +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; From 871f59fefbcd19f7a8e0adc256bc003a32fb123e Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Sat, 20 Jan 2024 23:14:03 +0100 Subject: [PATCH 06/94] Port expeditions --- .../com/minecolonies/api/colony/IColony.java | 6 + .../minecolonies/api/colony/IVisitorData.java | 14 +- .../api/colony/IVisitorViewData.java | 10 + .../colony/expeditions/ExpeditionStatus.java | 24 ++ .../api/colony/expeditions/IExpedition.java | 106 +++++ .../colony/expeditions/IExpeditionMember.java | 54 +++ .../colony/expeditions/IExpeditionStage.java | 66 ++++ .../ModInteractionResponseHandlers.java | 2 + .../interfaces/IExpeditionManager.java | 26 ++ .../minecolonies/api/entity/ModEntities.java | 2 + .../api/entity/visitor/ModVisitorTypes.java | 4 +- .../util/constant/TranslationConstants.java | 7 + .../api/util/constant/WindowConstants.java | 5 + .../translation/GuiTranslationConstants.java | 6 + .../apiimp/initializer/EntityInitializer.java | 8 + .../ModInteractionsInitializer.java | 6 + .../ModVisitorTypesInitializer.java | 2 + .../MainWindowExpeditionary.java | 107 +++++ .../com/minecolonies/core/colony/Colony.java | 11 + .../minecolonies/core/colony/ColonyView.java | 6 + .../minecolonies/core/colony/VisitorData.java | 6 + .../core/colony/VisitorDataView.java | 19 +- .../core/colony/expeditions/Expedition.java | 289 ++++++++++++++ .../expeditions/ExpeditionCitizenMember.java | 97 +++++ .../colony/expeditions/ExpeditionStage.java | 56 +++ .../expeditions/ExpeditionVisitorMember.java | 97 +++++ .../colony/ColonyExpeditionType.java | 371 ++++++++++++++++++ .../colony/ColonyExpeditionTypeManager.java | 114 ++++++ .../ColonyExpeditionFoodRequirement.java | 32 ++ .../ColonyExpeditionItemRequirement.java | 39 ++ .../ColonyExpeditionToolRequirement.java | 40 ++ .../IColonyExpeditionRequirement.java | 17 + .../ExpeditionaryInteraction.java | 83 ++++ .../colony/managers/ExpeditionManager.java | 125 ++++++ .../ColonyExpeditionTypeListener.java | 65 +++ .../ai/visitor/EntityAIExpeditionary.java | 80 ++++ .../visitor/ExpeditionaryVisitorType.java | 75 ++++ .../core/event/ClientRegistryHandler.java | 1 + .../core/event/FMLEventHandler.java | 1 + 39 files changed, 2066 insertions(+), 13 deletions(-) create mode 100644 src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java create mode 100644 src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java create mode 100644 src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java create mode 100644 src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java create mode 100644 src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java create mode 100644 src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionTypeManager.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionFoodRequirement.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionItemRequirement.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java create mode 100644 src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java create mode 100644 src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java create mode 100644 src/main/java/com/minecolonies/core/datalistener/ColonyExpeditionTypeListener.java create mode 100644 src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIExpeditionary.java create mode 100644 src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java diff --git a/src/api/java/com/minecolonies/api/colony/IColony.java b/src/api/java/com/minecolonies/api/colony/IColony.java index 4212bcd6795..4e10706fdf0 100755 --- a/src/api/java/com/minecolonies/api/colony/IColony.java +++ b/src/api/java/com/minecolonies/api/colony/IColony.java @@ -484,6 +484,12 @@ default List getWayPoints(@NotNull BlockPos position, @NotNull BlockPo */ IQuestManager getQuestManager(); + /** + * Get the expedition manager of the colony. + * @return the expedition manager. + */ + IExpeditionManager getExpeditionManager(); + /** * Get citizen from colony. * @param id the id of the cit. diff --git a/src/api/java/com/minecolonies/api/colony/IVisitorData.java b/src/api/java/com/minecolonies/api/colony/IVisitorData.java index bec009f783c..e99b7ff362f 100644 --- a/src/api/java/com/minecolonies/api/colony/IVisitorData.java +++ b/src/api/java/com/minecolonies/api/colony/IVisitorData.java @@ -2,11 +2,9 @@ import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import com.minecolonies.api.entity.visitor.IVisitorType; import net.minecraft.world.entity.EntityType; -import net.minecraft.core.BlockPos; -import net.minecraft.world.item.ItemStack; - -import java.util.UUID; +import org.jetbrains.annotations.NotNull; /** * Data for colony visitors, based on citizen data @@ -20,6 +18,14 @@ public interface IVisitorData extends ICitizenData */ EntityType getEntityType(); + /** + * Get the type of the visitor. + * + * @return the visitor type. + */ + @NotNull + IVisitorType getVisitorType(); + /** * Get any bit of additional information for this visitor. * diff --git a/src/api/java/com/minecolonies/api/colony/IVisitorViewData.java b/src/api/java/com/minecolonies/api/colony/IVisitorViewData.java index 76ed84344d9..c86aa3ce9f6 100644 --- a/src/api/java/com/minecolonies/api/colony/IVisitorViewData.java +++ b/src/api/java/com/minecolonies/api/colony/IVisitorViewData.java @@ -2,7 +2,9 @@ import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; import com.minecolonies.api.entity.visitor.IVisitorExtraData; +import com.minecolonies.api.entity.visitor.IVisitorType; import net.minecraft.world.entity.EntityType; +import org.jetbrains.annotations.NotNull; /** * View data for visitors @@ -16,6 +18,14 @@ public interface IVisitorViewData extends ICitizenDataView */ EntityType getEntityType(); + /** + * Get the type of the visitor. + * + * @return the visitor type. + */ + @NotNull + IVisitorType getVisitorType(); + /** * Get any bit of additional information for this visitor. * diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java b/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java new file mode 100644 index 00000000000..4239ab2b375 --- /dev/null +++ b/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java @@ -0,0 +1,24 @@ +package com.minecolonies.api.colony.expeditions; + +/** + * Enum describing the different statuses of an expedition. + */ +public enum ExpeditionStatus +{ + /** + * Initial expedition state, expedition exists but has not been started yet. + */ + CREATED, + /** + * The expedition embarked on their journey and is currently in progress. + */ + EMBARKED, + /** + * The expedition has returned safely to the colony. + */ + RETURNED, + /** + * The expedition has not returned in time, they either got lost or have been killed. + */ + MISSING; +} \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java b/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java new file mode 100644 index 00000000000..54c423ca8f8 --- /dev/null +++ b/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java @@ -0,0 +1,106 @@ +package com.minecolonies.api.colony.expeditions; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Main interface for an expedition instance. + */ +public interface IExpedition +{ + /** + * The dimension this expedition is going to. + * + * @return the dimension key. + */ + @NotNull + ResourceKey getTargetDimension(); + + /** + * Get the status of the expedition. + * + * @return the current status. + */ + ExpeditionStatus getStatus(); + + /** + * Set the status of the expedition. + * + * @param status the new status. + */ + void setStatus(final ExpeditionStatus status); + + /** + * Get all the members of the expedition. + * + * @return the expedition members. + */ + @NotNull + List getMembers(); + + /** + * Get the currently active members of the expedition. + * + * @return the expedition members. + */ + @NotNull + List getActiveMembers(); + + /** + * The equipment that has been given to this expedition to use. + * + * @return the list of equipment. + */ + @NotNull + List getEquipment(); + + /** + * The results of this expedition. + * Yields null as long as the stage of the expedition is not {@link ExpeditionStatus#RETURNED}. + * + * @return a list of stages containing results per expedition stage. + */ + @Nullable + List getResults(); + + /** + * Advances the current stage of the expedition. + * Adding a new stage on top of the current one. + */ + void advanceStage(); + + /** + * 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 type the entity type that got killed. + */ + void mobKilled(final EntityType type); + + /** + * 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/api/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java b/src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java new file mode 100644 index 00000000000..01ea2c0f51e --- /dev/null +++ b/src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java @@ -0,0 +1,54 @@ +package com.minecolonies.api.colony.expeditions; + +import com.minecolonies.api.colony.ICivilianData; +import com.minecolonies.api.colony.IColony; +import net.minecraft.nbt.CompoundTag; +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 whether this expedition member died during the expedition. + * + * @return true if so. + */ + boolean isDead(); + + /** + * Mark this expedition member as dead. + */ + void died(); + + /** + * Attempt to resolve the civilian data for this expedition member. + * May return null for multiple reasons. + * + * @return the civilian data, or null. + */ + @Nullable + ICivilianData resolveCivilianData(final IColony colony); + + /** + * Write this member to compound data. + * + * @param compound the compound tag. + */ + void write(final CompoundTag compound); +} \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java b/src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java new file mode 100644 index 00000000000..57d23b06ad7 --- /dev/null +++ b/src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java @@ -0,0 +1,66 @@ +package com.minecolonies.api.colony.expeditions; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +/** + * Interface for an expedition stage, an expedition can contain multiple stages, each with its own rewards/unlocks. + */ +public interface IExpeditionStage +{ + /** + * 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. + */ + List getRewards(); + + /** + * Adds a reward to this stage. + * + * @param itemStack the item to add to the stage. + */ + void addReward(final ItemStack itemStack); + + /** + * Get a map of mobs killed during this expedition stage, entries contain the mob type and their amount killed. + * + * @return the map of kills. + */ + Map, Integer> getKills(); + + /** + * Adds a kill to this stage. + * + * @param type the entity type that got killed. + */ + void rewardFound(final EntityType type); + + /** + * Get a members instance of what members were lost during this stage. + * + * @return which members were lost during this part of the expedition. + */ + @Nullable + List getMembersLost(); + + /** + * Adds a member that got lost during this stage. + * + * @param member the member that were lost. + */ + void memberLost(final IExpeditionMember member); + + /** + * Write this stage to compound data. + * + * @param compound the compound tag. + */ + void write(final CompoundTag compound); +} \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java b/src/api/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java index 6ec6eb410e3..a9cc65f4b2c 100644 --- a/src/api/java/com/minecolonies/api/colony/interactionhandling/ModInteractionResponseHandlers.java +++ b/src/api/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 EXPEDITIONARY = new ResourceLocation(Constants.MOD_ID, "expeditionary"); /** * List of entries. @@ -31,6 +32,7 @@ public final class ModInteractionResponseHandlers public static RegistryObject recruitment; public static RegistryObject quest; public static RegistryObject questAction; + public static RegistryObject expeditionary; private ModInteractionResponseHandlers() { diff --git a/src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java new file mode 100644 index 00000000000..d1c22f3e6fa --- /dev/null +++ b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java @@ -0,0 +1,26 @@ +package com.minecolonies.api.colony.managers.interfaces; + +import com.minecolonies.api.colony.expeditions.IExpedition; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; + +/** + * Interface for the colony expedition manager. From here all outgoing expeditions to external places are managed. + */ +public interface IExpeditionManager +{ + /** + * 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); + + /** + * Register a new expedition to the manager. + * + * @param expedition the expedition instance. + */ + void registerExpedition(final IExpedition expedition); +} \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/entity/ModEntities.java b/src/api/java/com/minecolonies/api/entity/ModEntities.java index 3c4b056545d..cdd1bc5e28b 100755 --- a/src/api/java/com/minecolonies/api/entity/ModEntities.java +++ b/src/api/java/com/minecolonies/api/entity/ModEntities.java @@ -21,6 +21,8 @@ public class ModEntities public static EntityType VISITOR; + public static EntityType EXPEDITIONARY; + public static EntityType FISHHOOK; public static EntityType MERCENARY; diff --git a/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java b/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java index b31ee505072..d82be82d53f 100644 --- a/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java +++ b/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java @@ -12,10 +12,12 @@ public class ModVisitorTypes /** * Resource ids. */ - public static final ResourceLocation VISITOR_TYPE_ID = new ResourceLocation(Constants.MOD_ID, "visitor"); + 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/api/java/com/minecolonies/api/util/constant/TranslationConstants.java b/src/api/java/com/minecolonies/api/util/constant/TranslationConstants.java index 5196aca6de8..1ffebc2eb61 100755 --- a/src/api/java/com/minecolonies/api/util/constant/TranslationConstants.java +++ b/src/api/java/com/minecolonies/api/util/constant/TranslationConstants.java @@ -772,6 +772,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/api/java/com/minecolonies/api/util/constant/WindowConstants.java b/src/api/java/com/minecolonies/api/util/constant/WindowConstants.java index f94bd6ee51c..60f185c6f21 100755 --- a/src/api/java/com/minecolonies/api/util/constant/WindowConstants.java +++ b/src/api/java/com/minecolonies/api/util/constant/WindowConstants.java @@ -617,6 +617,11 @@ public final class WindowConstants */ 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/api/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java b/src/api/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java index 95d9f96be19..211dc67bba7 100644 --- a/src/api/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java +++ b/src/api/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java @@ -49,4 +49,10 @@ public class GuiTranslationConstants public static final String QUEST_LOG_COMPLETED_MULTIPLE_TEXT = "com.minecolonies.coremod.item.questlog.gui.completed_multiple"; @NonNls public static final String QUEST_LOG_TRACK_CITIZEN_TEXT = "com.minecolonies.coremod.item.questlog.gui.track_citizen"; + + // Expeditionary window + @NonNls + public static final String EXPEDITIONARY_DIFFICULTY = "com.minecolonies.gui.expedition.difficulty"; + @NonNls + public static final String EXPEDITIONARY_DIFFICULTY_PREFIX = "com.minecolonies.gui.expedition.difficulty."; } diff --git a/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java index 499070a5c72..6aa0ebe9124 100644 --- a/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/EntityInitializer.java @@ -22,6 +22,7 @@ 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 net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; @@ -69,6 +70,13 @@ public static void setupEntities(RegisterEvent event) .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) .setTrackingRange(ENTITY_TRACKING_RANGE) diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModInteractionsInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModInteractionsInitializer.java index bfbe9af63a5..1d682b927c9 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.expeditionary = DEFERRED_REGISTER.register(ModInteractionResponseHandlers.EXPEDITIONARY.getPath(), + () -> new InteractionResponseHandlerEntry.Builder() + .setResponseHandlerProducer(ExpeditionaryInteraction::new) + .setRegistryName(ModInteractionResponseHandlers.EXPEDITIONARY) + .createEntry()); } } diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java index f654f11cdec..1609338145b 100644 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModVisitorTypesInitializer.java @@ -3,6 +3,7 @@ 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; @@ -17,6 +18,7 @@ public class ModVisitorTypesInitializer 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() { diff --git a/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java new file mode 100644 index 00000000000..0b3711f77cd --- /dev/null +++ b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java @@ -0,0 +1,107 @@ +package com.minecolonies.core.client.gui.visitor.expeditionary; + +import com.ldtteam.blockui.PaneBuilders; +import com.ldtteam.blockui.controls.Image; +import com.ldtteam.blockui.controls.Text; +import com.ldtteam.blockui.views.View; +import com.minecolonies.api.colony.IVisitorViewData; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.Network; +import com.minecolonies.core.client.gui.AbstractWindowSkeleton; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType.Difficulty; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType; +import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +import static com.minecolonies.api.util.constant.WindowConstants.EXPEDITIONARY_MAIN_RESOURCE_SUFFIX; +import static com.minecolonies.api.util.constant.translation.GuiTranslationConstants.EXPEDITIONARY_DIFFICULTY; +import static com.minecolonies.api.util.constant.translation.GuiTranslationConstants.EXPEDITIONARY_DIFFICULTY_PREFIX; + +/** + * Main window for the expeditionary their GUI. + */ +public class MainWindowExpeditionary extends AbstractWindowSkeleton +{ + /** + * Window constants. + */ + private static final String LABEL_EXPEDITION_NAME = "expedition_name"; + private static final String VIEW_EXPEDITION_DIFFICULTY = "expedition_difficulty"; + + /** + * The visitor data. + */ + @NotNull + private final IVisitorViewData visitorData; + + /** + * The current expedition type. + */ + private final ColonyExpeditionType expeditionType; + + /** + * Default constructor. + */ + public MainWindowExpeditionary(final @NotNull IVisitorViewData visitorData) + { + super(Constants.MOD_ID + EXPEDITIONARY_MAIN_RESOURCE_SUFFIX); + this.visitorData = visitorData; + this.expeditionType = visitorData.getExtraDataValue(ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION_TYPE).orElseThrow(); + + findPaneOfTypeByID(LABEL_EXPEDITION_NAME, Text.class).setText(expeditionType.getName()); + + updateDifficulty(); + + registerButton(LABEL_EXPEDITION_NAME, this::startExpedition); + } + + @Override + public void onUpdate() + { + super.onUpdate(); + } + + /** + * Triggers starting the expedition. + */ + private void startExpedition() + { + Network.getNetwork().sendToServer(new OpenInventoryMessage(visitorData.getColony(), visitorData.getName(), visitorData.getId())); + } + + /** + * Update the difficulty icons. + */ + private void updateDifficulty() + { + final int maxDifficulty = Arrays.stream(Difficulty.values()) + .filter(m -> m.equals(expeditionType.getDifficulty()) || !m.isHidden()) + .mapToInt(Difficulty::getLevel) + .max() + .orElse(0); + final Difficulty currentDifficulty = expeditionType.getDifficulty(); + + 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(VIEW_EXPEDITION_DIFFICULTY, View.class)) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/Colony.java b/src/main/java/com/minecolonies/core/colony/Colony.java index 1100f18161e..464790c23f8 100644 --- a/src/main/java/com/minecolonies/core/colony/Colony.java +++ b/src/main/java/com/minecolonies/core/colony/Colony.java @@ -191,6 +191,11 @@ public class Colony implements IColony */ private IQuestManager questManager; + /** + * Expedition manager for this colony + */ + private final IExpeditionManager expeditionManager = new ExpeditionManager(this); + /** * The Positions which players can freely interact. */ @@ -1941,6 +1946,12 @@ public IQuestManager getQuestManager() return questManager; } + @Override + public IExpeditionManager getExpeditionManager() + { + return expeditionManager; + } + @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 79e02702911..a529792c3db 100644 --- a/src/main/java/com/minecolonies/core/colony/ColonyView.java +++ b/src/main/java/com/minecolonies/core/colony/ColonyView.java @@ -1582,4 +1582,10 @@ public IQuestManager getQuestManager() { return this.questManager; } + + @Override + public IExpeditionManager getExpeditionManager() + { + return null; + } } \ 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 b5ef8e456c1..b6ff85fa61e 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorData.java @@ -85,6 +85,12 @@ public EntityType getEntityType() return visitorType.getEntityType(); } + @Override + public @NotNull IVisitorType getVisitorType() + { + return visitorType; + } + @Override @SuppressWarnings("unchecked") public T getExtraDataValue(final IVisitorExtraData extraData) diff --git a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java index 8dc112bb2a6..155d16b983f 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java @@ -52,15 +52,12 @@ public void deserialize(@NotNull final FriendlyByteBuf buf) { final ResourceLocation visitorTypeKey = new ResourceLocation(compoundTag.getString(TAG_VISITOR_TYPE)); visitorType = IMinecoloniesAPI.getInstance().getVisitorTypeRegistry().getValue(visitorTypeKey); - if (visitorType != null) - { - extraData = visitorType.getExtraDataKeys(); + extraData = visitorType.getExtraDataKeys(); - final CompoundTag compound = compoundTag.getCompound(TAG_EXTRA_DATA); - for (final IVisitorExtraData extraDataKey : extraData) - { - extraDataKey.deserializeNBT(compound.getCompound(extraDataKey.getKey())); - } + final CompoundTag compound = compoundTag.getCompound(TAG_EXTRA_DATA); + for (final IVisitorExtraData extraDataKey : extraData) + { + extraDataKey.deserializeNBT(compound.getCompound(extraDataKey.getKey())); } } } @@ -71,6 +68,12 @@ public EntityType getEntityType() return visitorType.getEntityType(); } + @Override + public @NotNull IVisitorType getVisitorType() + { + return visitorType; + } + @Override @SuppressWarnings("unchecked") public T getExtraDataValue(final IVisitorExtraData extraData) diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java b/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java new file mode 100644 index 00000000000..14027e8f99f --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java @@ -0,0 +1,289 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; +import com.minecolonies.api.colony.expeditions.IExpedition; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import com.minecolonies.api.colony.expeditions.IExpeditionStage; +import net.minecraft.core.registries.Registries; +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.entity.EntityType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_DIMENSION; + +/** + * Class for an expedition instance. + */ +public class Expedition implements IExpedition +{ + /** + * Nbt tag constants. + */ + private static final String TAG_MEMBERS = "members"; + private static final String TAG_MEMBER_TYPE = "memberType"; + private static final String TAG_EQUIPMENT = "equipment"; + + /** + * The dimension to send the expedition to. + */ + private final ResourceKey dimensionId; + + /** + * The results of this expedition. + */ + private final List results = new ArrayList<>(List.of(new ExpeditionStage())); + + /** + * The members of the expedition. + */ + @NotNull + private final List members; + + /** + * The list of items to give to the expedition members. + */ + @NotNull + private final List equipment; + + /** + * The stage of the expedition. + */ + private ExpeditionStatus stage = ExpeditionStatus.CREATED; + + /** + * Internal constructor. + */ + private Expedition(final ResourceKey dimensionId, final @NotNull List members, final @NotNull List equipment) + { + this.dimensionId = dimensionId; + this.members = members; + this.equipment = equipment; + } + + /** + * Create an expedition instance from compound data. + * + * @param compound the compound data. + * @return the expedition instance. + */ + public static Expedition of(final CompoundTag compound) + { + return Serializer.read(compound); + } + + @Override + public @NotNull ResourceKey getTargetDimension() + { + return this.dimensionId; + } + + @Override + public ExpeditionStatus getStatus() + { + return this.stage; + } + + @Override + public void setStatus(final ExpeditionStatus stage) + { + this.stage = stage; + } + + @Override + @NotNull + public List getMembers() + { + return Collections.unmodifiableList(this.members); + } + + @Override + public @NotNull List getActiveMembers() + { + return this.members.stream().filter(f -> !f.isDead()).toList(); + } + + @Override + @NotNull + public List getEquipment() + { + return this.equipment; + } + + @Override + @Nullable + public List getResults() + { + return Collections.unmodifiableList(this.results); + } + + @Override + public void advanceStage() + { + this.results.add(new ExpeditionStage()); + } + + @Override + public void rewardFound(final ItemStack itemStack) + { + this.results.get(this.results.size() - 1).addReward(itemStack); + } + + @Override + public void mobKilled(final EntityType type) + { + this.results.get(this.results.size() - 1).rewardFound(type); + } + + @Override + public void memberLost(final IExpeditionMember member) + { + this.results.get(this.results.size() - 1).memberLost(member); + member.died(); + } + + @Override + public void write(final CompoundTag compound) + { + Serializer.write(this, compound); + } + + /** + * Builder for an expedition. + */ + public static class Builder + { + /** + * The level to send the expedition to. + */ + private final ResourceKey dimensionId; + + /** + * The list of items to give to the expedition members. + */ + private final List equipment = new ArrayList<>(); + + /** + * The members of the expedition. + */ + private final List members = new ArrayList<>(); + + /** + * Default constructor. + */ + public Builder(final ResourceKey dimensionId) + { + this.dimensionId = dimensionId; + } + + /** + * Add additional members to this expedition. + * + * @param member the new member. + * @return the builder for chaining. + */ + public Builder addMembers(final IExpeditionMember member) + { + this.members.add(member); + return this; + } + + /** + * Finalizes creation of the expedition instance. + * + * @return the created expedition. + */ + public Expedition build() + { + return new Expedition(dimensionId, members, equipment); + } + } + + /** + * Serializer for the expeditions. + */ + private static class Serializer + { + /** + * Read a new expedition from NBT. + * + * @param compound the NBT data. + * @return the expedition instance. + */ + public static Expedition read(final CompoundTag compound) + { + final ResourceKey dimensionId = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(compound.getString(TAG_DIMENSION))); + + final List members = new ArrayList<>(); + final ListTag membersList = compound.getList(TAG_MEMBERS, 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)); + } + } + + 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))); + } + + return new Expedition(dimensionId, members, equipment); + } + + /** + * Write an expedition to NBT data. + * + * @param expedition the expedition instance. + * @param compound the NBT to write the expedition to. + */ + public static void write(final Expedition expedition, final CompoundTag compound) + { + compound.putString(TAG_DIMENSION, expedition.dimensionId.location().toString()); + + final ListTag memberTag = new ListTag(); + for (final IExpeditionMember member : expedition.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); + memberTag.add(memberCompound); + } + compound.put(TAG_MEMBERS, memberTag); + + final ListTag equipmentTag = new ListTag(); + for (final ItemStack itemStack : expedition.equipment) + { + equipmentTag.add(itemStack.serializeNBT()); + } + compound.put(TAG_EQUIPMENT, equipmentTag); + } + } +} \ 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..866fbb7f329 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java @@ -0,0 +1,97 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.ICitizenData; +import com.minecolonies.api.colony.ICivilianData; +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import net.minecraft.nbt.CompoundTag; +import org.jetbrains.annotations.Nullable; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_NAME; + +/** + * Citizen expedition members. + */ +public class ExpeditionCitizenMember implements IExpeditionMember +{ + /** + * Nbt tag constants. + */ + private static final String TAG_DIED = "died"; + + /** + * The id of the citizen. + */ + private final int id; + + /** + * The name of the citizen. + */ + private final String name; + + /** + * Whether this citizen dead or not. + */ + private boolean died; + + /** + * Default constructor for deserialization. + */ + public ExpeditionCitizenMember(final CompoundTag compound) + { + this.id = compound.getInt(TAG_ID); + this.name = compound.getString(TAG_NAME); + this.died = compound.getBoolean(TAG_DIED); + } + + /** + * 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.died = false; + } + + @Override + public int getId() + { + return this.id; + } + + @Override + public String getName() + { + return this.name; + } + + @Override + public boolean isDead() + { + return this.died; + } + + @Override + public void died() + { + this.died = true; + } + + @Override + public @Nullable ICivilianData 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.putBoolean(TAG_DIED, this.died); + } +} \ 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..9b9bb7ceee1 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java @@ -0,0 +1,56 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import com.minecolonies.api.colony.expeditions.IExpeditionStage; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class ExpeditionStage implements IExpeditionStage +{ + @Override + public List getRewards() + { + return null; + } + + @Override + public void addReward(final ItemStack itemStack) + { + + } + + @Override + public Map, Integer> getKills() + { + return null; + } + + @Override + public void rewardFound(final EntityType type) + { + + } + + @Override + public @Nullable List getMembersLost() + { + return null; + } + + @Override + public void memberLost(final IExpeditionMember member) + { + + } + + @Override + public void write(final CompoundTag compound) + { + + } +} \ 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..9299f04f62a --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java @@ -0,0 +1,97 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.ICivilianData; +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 org.jetbrains.annotations.Nullable; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_ID; +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_NAME; + +/** + * Visitor expedition members. + */ +public class ExpeditionVisitorMember implements IExpeditionMember +{ + /** + * Nbt tag constants. + */ + private static final String TAG_DIED = "died"; + + /** + * The id of the visitor. + */ + private final int id; + + /** + * The name of the visitor. + */ + private final String name; + + /** + * Whether this visitor dead or not. + */ + private boolean died; + + /** + * Default constructor for deserialization. + */ + public ExpeditionVisitorMember(final CompoundTag compound) + { + this.id = compound.getInt(TAG_ID); + this.name = compound.getString(TAG_NAME); + this.died = compound.getBoolean(TAG_DIED); + } + + /** + * 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.died = false; + } + + @Override + public int getId() + { + return this.id; + } + + @Override + public String getName() + { + return this.name; + } + + @Override + public boolean isDead() + { + return this.died; + } + + @Override + public void died() + { + this.died = true; + } + + @Override + public @Nullable ICivilianData 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.putBoolean(TAG_DIED, this.died); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java new file mode 100644 index 00000000000..2a704ee31d0 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java @@ -0,0 +1,371 @@ +package com.minecolonies.core.colony.expeditions.colony; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.minecolonies.api.util.constant.IToolType; +import com.minecolonies.api.util.constant.ToolType; +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.ColonyExpeditionToolRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.IColonyExpeditionRequirement; +import net.minecraft.ChatFormatting; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * JSON based class for defining colony expedition types. + */ +public class ColonyExpeditionType +{ + /** + * 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_TOOL_KEY = "tool"; + private static final String PROP_REQUIREMENT_ITEM_KEY = "item"; + private static final String PROP_GUARDS = "guards"; + + /** + * Requirement types + */ + private static final String REQUIREMENT_TYPE_TOOL = "tool"; + private static final String REQUIREMENT_TYPE_FOOD = "food"; + private static final String REQUIREMENT_TYPE_ITEM = "item"; + + /** + * The id of the expedition. + */ + private final ResourceLocation id; + + /** + * The name of the expedition, may be a translation string or a fixed text. + */ + private final Component 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 final Component toText; + + /** + * The difficulty of the expedition. + */ + private final Difficulty difficulty; + + /** + * 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; + + /** + * The minimum amount of guards needed for this expedition. + */ + private final int guards; + + /** + * Default constructor. + */ + public ColonyExpeditionType( + final ResourceLocation id, + final Component name, + final Component toText, + final @NotNull Difficulty difficulty, + final ResourceKey dimension, + final ResourceLocation lootTable, + final List requirements, + final int guards) + { + this.id = id; + this.name = name; + this.toText = toText; + this.difficulty = difficulty; + this.dimension = dimension; + this.lootTable = lootTable; + this.requirements = Collections.unmodifiableList(requirements); + this.guards = guards; + } + + /** + * 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 Difficulty difficulty = Difficulty.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 List requirements = new ArrayList<>(); + 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; + } + + requirements.add(parseRequirement(jsonRequirement.getAsJsonObject())); + } + } + + 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(Difficulty.values()).map(m -> m.key).collect(Collectors.joining(", ")))); + } + + return new ColonyExpeditionType(id, name, toText, difficulty, dimension, lootTable, requirements, guards); + } + + /** + * Parse an individual requirement from a json object. + * + * @param requirement the input json object. + * @return a requirement instance or null. + */ + @Nullable + private static IColonyExpeditionRequirement parseRequirement(final JsonObject requirement) + { + final int amount = requirement.has(PROP_REQUIREMENT_AMOUNT) ? requirement.getAsJsonPrimitive(PROP_REQUIREMENT_AMOUNT).getAsInt() : 1; + return switch (requirement.get(PROP_REQUIREMENT_TYPE).getAsString()) + { + case REQUIREMENT_TYPE_TOOL -> + { + final IToolType toolType = ToolType.getToolType(requirement.getAsJsonPrimitive(PROP_REQUIREMENT_TOOL_KEY).getAsString()); + yield new ColonyExpeditionToolRequirement(toolType, amount); + } + case REQUIREMENT_TYPE_FOOD -> new ColonyExpeditionFoodRequirement(amount); + case REQUIREMENT_TYPE_ITEM -> + { + final ResourceLocation itemId = new ResourceLocation(requirement.getAsJsonPrimitive(PROP_REQUIREMENT_ITEM_KEY).getAsString()); + yield new ColonyExpeditionItemRequirement(ForgeRegistries.ITEMS.getValue(itemId), amount); + } + default -> null; + }; + } + + /** + * Get the id of the expedition. + * + * @return the id. + */ + public ResourceLocation getId() + { + return id; + } + + /** + * Get the name of the expedition. + * + * @return the component. + */ + public Component getName() + { + return name; + } + + /** + * Get the difficulty of the expedition. + * + * @return the difficulty enum. + */ + public Difficulty getDifficulty() + { + return difficulty; + } + + /** + * Get the "to text" of the expedition, used inside the interaction. + * + * @return the component. + */ + public Object getToText() + { + return toText; + } + + /** + * Get the target dimension this expedition would go to. + * + * @return the level resource key. + */ + public ResourceKey getDimension() + { + return dimension; + } + + /** + * Get the loot table to use for rewards generation. + * + * @return the resloc for the loot table. + */ + public ResourceLocation getLootTable() + { + return lootTable; + } + + /** + * The expedition difficulty. + */ + public enum Difficulty + { + EASY("easy", 1, 0, Items.IRON_SWORD, false, Style.EMPTY), + MEDIUM("medium", 2, 1, Items.IRON_SWORD, false, Style.EMPTY), + HARD("hard", 3, 3, Items.IRON_SWORD, false, Style.EMPTY), + NIGHTMARE("nightmare", 4, 5, Items.NETHERITE_SWORD, true, Style.EMPTY.withColor(ChatFormatting.DARK_RED).withItalic(true)); + + /** + * The key of the difficulty, used in the json files. + */ + private final String key; + + /** + * The level of the difficulty. + */ + private final int level; + + /** + * The luck level an expedition of the given difficulty will have. + */ + private final int luckLevel; + + /** + * 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; + + Difficulty(final String key, final int level, final int luckLevel, final Item icon, final boolean hidden, final Style style) + { + this.key = key; + this.level = level; + this.luckLevel = luckLevel; + this.icon = icon; + this.hidden = hidden; + this.style = style; + } + + /** + * 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 Difficulty fromKey(final String key) + { + for (final Difficulty item : Difficulty.values()) + { + if (item.key.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; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionTypeManager.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionTypeManager.java new file mode 100644 index 00000000000..d1206d52045 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionTypeManager.java @@ -0,0 +1,114 @@ +package com.minecolonies.core.colony.expeditions.colony; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.managers.interfaces.IExpeditionManager; +import com.minecolonies.core.datalistener.ColonyExpeditionTypeListener; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Manager class for all the possible {@link ColonyExpeditionType} instances. + */ +public class ColonyExpeditionTypeManager +{ + /** + * The singleton instance. + */ + private static ColonyExpeditionTypeManager instance; + + /** + * The map of all possible expedition types. + */ + private final Map possibleTypes = new HashMap<>(); + + /** + * Randomizer instance. + */ + private final Random random = new Random(); + + /** + * Internal constructor. + */ + private ColonyExpeditionTypeManager() + { + } + + /** + * Get the singleton instance for the colony expedition type manager. + * + * @return the singleton instance. + */ + public static ColonyExpeditionTypeManager getInstance() + { + if (instance == null) + { + instance = new ColonyExpeditionTypeManager(); + } + return instance; + } + + /** + * Reload all types, initiated by {@link ColonyExpeditionTypeListener}. + * + * @param newTypes the new map of types. + */ + public void reloadTypes(final Map newTypes) + { + this.possibleTypes.clear(); + this.possibleTypes.putAll(newTypes); + } + + /** + * @param colony the colony reference to get the expedition manager for. + * @return the expedition type. + */ + public boolean canStartExpedition(final IColony colony) + { + return colony.hasTownHall() && colony.getBuildingManager().getTownHall().getBuildingLevel() > 0 + && random.nextInt(100) < 50; + } + + /** + * Get the provided expedition type from its id. + * + * @param id the id. + * @return the expedition type instance. + */ + public ColonyExpeditionType getExpeditionType(final ResourceLocation id) + { + return this.possibleTypes.get(id); + } + + /** + * Obtain a random expedition type from the map of possible expedition types. + * The target dimension must be reachable according to {@link IExpeditionManager#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 ColonyExpeditionType getRandomExpeditionType(final IColony colony) + { + final IExpeditionManager expeditionManager = colony.getExpeditionManager(); + final List expeditionTypes = new ArrayList<>(possibleTypes.values()); + + ColonyExpeditionType chosenExpeditionType = null; + while (!expeditionTypes.isEmpty() && chosenExpeditionType == null) + { + final ColonyExpeditionType colonyExpeditionType = expeditionTypes.get(random.nextInt(expeditionTypes.size())); + if (!expeditionManager.canGoToDimension(colonyExpeditionType.getDimension())) + { + expeditionTypes.removeIf(type -> type.getDimension().equals(colonyExpeditionType.getDimension())); + continue; + } + + chosenExpeditionType = colonyExpeditionType; + } + + return chosenExpeditionType; + } +} \ 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..9cc8beed82a --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionFoodRequirement.java @@ -0,0 +1,32 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.ItemStackUtils; +import net.minecraftforge.items.IItemHandler; + +/** + * Colony expedition requirements for providing any kind of food, with a minimum amount. + */ +public class ColonyExpeditionFoodRequirement implements IColonyExpeditionRequirement +{ + /** + * The minimum amount to fulfill this requirement. + */ + private final int amount; + + /** + * Default constructor. + * + * @param amount the minimum amount. + */ + public ColonyExpeditionFoodRequirement(final int amount) + { + this.amount = amount; + } + + @Override + public boolean isFulFilled(final IItemHandler itemHandler) + { + return InventoryUtils.getItemCountInItemHandler(itemHandler, ItemStackUtils.ISFOOD) >= this.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..945a49b9963 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionItemRequirement.java @@ -0,0 +1,39 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.util.InventoryUtils; +import net.minecraft.world.item.Item; +import net.minecraftforge.items.IItemHandler; + +/** + * Colony expedition requirements for providing any kind of item, with a minimum amount. + */ +public class ColonyExpeditionItemRequirement implements IColonyExpeditionRequirement +{ + /** + * The item to request. + */ + private final Item item; + + /** + * The minimum amount to fulfill this requirement. + */ + private final int amount; + + /** + * Default constructor. + * + * @param item the item to request. + * @param amount the minimum amount. + */ + public ColonyExpeditionItemRequirement(final Item item, final int amount) + { + this.item = item; + this.amount = amount; + } + + @Override + public boolean isFulFilled(final IItemHandler itemHandler) + { + return InventoryUtils.getItemCountInItemHandler(itemHandler, this.item) >= this.amount; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java new file mode 100644 index 00000000000..d5ae5d096e6 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java @@ -0,0 +1,40 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.ItemStackUtils; +import com.minecolonies.api.util.constant.IToolType; +import net.minecraftforge.items.IItemHandler; + +/** + * Colony expedition requirements for providing any kind of {@link com.minecolonies.api.util.constant.ToolType}. + */ +public class ColonyExpeditionToolRequirement implements IColonyExpeditionRequirement +{ + /** + * The required tool type. + */ + private final IToolType toolType; + + /** + * The minimum amount to fulfill this requirement. + */ + private final int amount; + + /** + * Default constructor. + * + * @param toolType the required tool type. + * @param amount the minimum amount. + */ + public ColonyExpeditionToolRequirement(final IToolType toolType, final int amount) + { + this.toolType = toolType; + this.amount = amount; + } + + @Override + public boolean isFulFilled(final IItemHandler itemHandler) + { + return InventoryUtils.getItemCountInItemHandler(itemHandler, stack -> ItemStackUtils.hasToolLevel(stack, this.toolType, 0, 5)) >= this.amount; + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java new file mode 100644 index 00000000000..8ce9bbd9fbe --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java @@ -0,0 +1,17 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import net.minecraftforge.items.IItemHandler; + +/** + * Interface for defining different types of colony expedition requirements. + */ +public interface IColonyExpeditionRequirement +{ + /** + * Whether the requirement is present in the provided inventory. + * + * @param itemHandler the inventory. + * @return whether the requirement is present or not. + */ + boolean isFulFilled(final IItemHandler itemHandler); +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java new file mode 100644 index 00000000000..7078232e7d6 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java @@ -0,0 +1,83 @@ +package com.minecolonies.core.colony.interactionhandling; + +import com.ldtteam.blockui.views.BOWindow; +import com.minecolonies.api.colony.ICitizen; +import com.minecolonies.api.colony.ICitizenDataView; +import com.minecolonies.api.colony.IVisitorViewData; +import com.minecolonies.api.colony.interactionhandling.IChatPriority; +import com.minecolonies.api.colony.interactionhandling.IInteractionResponseHandler; +import com.minecolonies.api.colony.interactionhandling.ModInteractionResponseHandlers; +import com.minecolonies.api.util.Tuple; +import com.minecolonies.core.client.gui.visitor.expeditionary.MainWindowExpeditionary; +import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; + +import java.util.Collections; +import java.util.List; + +/** + * Interaction for expeditionary visitors. + */ +public class ExpeditionaryInteraction extends ServerCitizenInteraction +{ + /** + * The view GUI answer. + */ + private static final Tuple viewAnswer = new Tuple<>(Component.translatable("com.minecolonies.coremod.gui.chat.recruit"), null); + + /** + * The return answer. + */ + private static final Tuple returnAnswer = new Tuple<>(Component.translatable("com.minecolonies.coremod.gui.chat.notnow"), null); + + /** + * Default constructor. + * + * @param inquiry the inquiry text. + * @param priority the chat priority. + */ + public ExpeditionaryInteraction( + final Component inquiry, + final IChatPriority priority) + { + super(inquiry, true, priority, d -> true, null, viewAnswer, returnAnswer); + } + + /** + * Initializer constructor. + * + * @param data the input citizen data. + */ + public ExpeditionaryInteraction(final ICitizen data) + { + super(data); + } + + @Override + public List genChildInteractions() + { + return Collections.emptyList(); + } + + @Override + public String getType() + { + return ModInteractionResponseHandlers.EXPEDITIONARY.getPath(); + } + + @Override + public boolean onClientResponseTriggered(final int responseId, final Player player, final ICitizenDataView data, final BOWindow window) + { + final Component response = getPossibleResponses().get(responseId); + if (response.equals(viewAnswer.getA()) && data instanceof IVisitorViewData visitorData && visitorData.getVisitorType() instanceof ExpeditionaryVisitorType) + { + final MainWindowExpeditionary windowExpeditionary = new MainWindowExpeditionary(visitorData); + windowExpeditionary.open(); + + return false; + } + + return !response.equals(returnAnswer); + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java b/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java new file mode 100644 index 00000000000..80d58937f98 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java @@ -0,0 +1,125 @@ +package com.minecolonies.core.colony.managers; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.buildings.ModBuildings; +import com.minecolonies.api.colony.expeditions.IExpedition; +import com.minecolonies.api.colony.managers.interfaces.IExpeditionManager; +import net.minecraft.resources.ResourceKey; +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; + +/** + * Implementation for the colony expedition manager. From here all outgoing expeditions to external places are managed. + */ +public class ExpeditionManager implements IExpeditionManager +{ + /** + * The colony this manager is for. + */ + private final IColony colony; + + /** + * 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; + + /** + * Default constructor. + */ + public ExpeditionManager(final IColony colony) + { + this.colony = colony; + } + + @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 void registerExpedition(final IExpedition expedition) + { + if (!canGoToDimension(expedition.getTargetDimension())) + { + return; + } + } + + + //@Override + //public IExpedition createExpedition(final Level level, final Consumer expeditionBuilder) + //{ + // //final LootContextParamSet lootContextParams = new LootContextParamSet.Builder().build(); + // // + // //final Builder lootContextBuilder = new Builder((ServerLevel) colony.getWorld()); + // //lootContextBuilder.withLuck(1); + // //final LootContext lootContext = lootContextBuilder.create(lootContextParams); + // // + // //colony.getWorld().getServer().getLootTables().get(new ResourceLocation("")).getRandomItems(lootContext); + // return null; + //} + + /** + * This event is fired by {@link ExpeditionManager#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/datalistener/ColonyExpeditionTypeListener.java b/src/main/java/com/minecolonies/core/datalistener/ColonyExpeditionTypeListener.java new file mode 100644 index 00000000000..23ceb5e2d0a --- /dev/null +++ b/src/main/java/com/minecolonies/core/datalistener/ColonyExpeditionTypeListener.java @@ -0,0 +1,65 @@ +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.colony.expeditions.colony.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; +import net.minecraft.resources.ResourceLocation; +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.HashMap; +import java.util.Map; + +/** + * Loader for json based expedition types. + */ +public class ColonyExpeditionTypeListener extends SimpleJsonResourceReloadListener +{ + /** + * The gson instance. + */ + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + + /** + * Set up the core loading, with the directory in the datapack that contains this data + * Directory is: (namespace)/expedition_types/(path) + */ + public ColonyExpeditionTypeListener() + { + super(GSON, "expedition_types"); + } + + @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 = ColonyExpeditionType.parse(key, entry.getValue().getAsJsonObject()); + newTypes.put(key, parsed); + } + catch (final JsonParseException | NullPointerException e) + { + Log.getLogger().error(new FormattedMessage("Error parsing expedition type {}", new Object[] {key}, e)); + } + } + + final ColonyExpeditionTypeManager manager = ColonyExpeditionTypeManager.getInstance(); + manager.reloadTypes(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..0e31b59f922 --- /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; + +/** + * 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.IDLE, 50)); + stateMachine.addTransition(new TickingTransition<>(VisitorState.IDLE, () -> true, this::decide, 50)); + } + + /** + * Whether the entity is in a ticked chunk + * + * @return true if loaded + */ + private boolean isEntityLoaded() + { + if (visitor.getCitizenColonyHandler().getColony() == null || visitor.getCitizenData() == null || visitor.getCitizenData().getHomeBuilding() == 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 decide() + { + visitor.isWorkerAtSiteWithMove(townhall.getPosition(), 20); + return VisitorState.IDLE; + } + + /** + * States of the expeditionary AI. + */ + public enum VisitorState implements IState + { + IDLE, + SLEEPING, + SITTING, + WANDERING + } +} \ No newline at end of file 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..793e9d048d2 --- /dev/null +++ b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java @@ -0,0 +1,75 @@ +package com.minecolonies.core.entity.visitor; + +import com.minecolonies.api.entity.ModEntities; +import com.minecolonies.api.entity.visitor.*; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; +import com.minecolonies.core.entity.ai.visitor.EntityAIExpeditionary; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; + +import java.util.List; +import java.util.Optional; + +/** + * Visitor type for expeditionary visitors in the town hall. + */ +public class ExpeditionaryVisitorType implements IVisitorType +{ + /** + * Extra data fields. + */ + public static final ColonyExpeditionTypeData EXTRA_DATA_EXPEDITION_TYPE = new ColonyExpeditionTypeData(); + + @Override + public ResourceLocation getId() + { + return ModVisitorTypes.EXPEDITIONARY_VISITOR_TYPE_ID; + } + + @Override + public EntityType getEntityType() + { + return ModEntities.EXPEDITIONARY; + } + + @Override + public void createStateMachine(final AbstractEntityVisitor visitor) + { + new EntityAIExpeditionary(visitor); + } + + @Override + public List> getExtraDataKeys() + { + return List.of(EXTRA_DATA_EXPEDITION_TYPE); + } + + public static class ColonyExpeditionTypeData extends AbstractVisitorExtraData> + { + private static final String TAG_VALUE = "value"; + + public ColonyExpeditionTypeData() + { + super("expedition-type", Optional.empty()); + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + getValue().ifPresent(val -> compound.putString(TAG_VALUE, val.getId().toString())); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + if (compoundTag.contains(TAG_VALUE)) + { + setValue(Optional.ofNullable(ColonyExpeditionTypeManager.getInstance().getExpeditionType(new ResourceLocation(compoundTag.getString(TAG_VALUE))))); + } + } + } +} diff --git a/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java b/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java index d49f1a6589b..2f37ef61022 100644 --- a/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java +++ b/src/main/java/com/minecolonies/core/event/ClientRegistryHandler.java @@ -269,6 +269,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/FMLEventHandler.java b/src/main/java/com/minecolonies/core/event/FMLEventHandler.java index 66716892a8d..9909eae7a5c 100755 --- a/src/main/java/com/minecolonies/core/event/FMLEventHandler.java +++ b/src/main/java/com/minecolonies/core/event/FMLEventHandler.java @@ -53,6 +53,7 @@ public static void onAddReloadListenerEvent(@NotNull final AddReloadListenerEven event.addListener(new CitizenNameListener()); event.addListener(new QuestJsonListener()); event.addListener(new ItemNbtListener()); + event.addListener(new ColonyExpeditionTypeListener()); } @SubscribeEvent From c0b2b71a4053893ef97ce5008f5089dbe840770f Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Thu, 25 Jan 2024 10:51:08 +0100 Subject: [PATCH 07/94] Lot of fixes --- .../colonyEvents/IColonyEntitySpawnEvent.java | 2 +- .../api/colony/colonyEvents/IColonyEvent.java | 9 - .../colony/colonyEvents/IColonyRaidEvent.java | 2 +- .../colonyEvents/IColonySpawnEvent.java | 2 +- .../IColonyStructureSpawnEvent.java | 2 +- .../colony/expeditions/ExpeditionStatus.java | 43 +++- .../api/colony/expeditions/IExpedition.java | 21 +- .../interfaces/IExpeditionManager.java | 15 +- .../ModColonyEventTypeInitializer.java | 2 + .../MainWindowExpeditionary.java | 20 +- .../raidEvents/AbstractShipRaidEvent.java | 6 - .../raidEvents/HordeRaidEvent.java | 12 +- .../expeditions/AbstractExpeditionEvent.java | 105 ++++++++ .../core/colony/expeditions/Expedition.java | 110 +++------ .../expeditions/ExpeditionCitizenMember.java | 2 +- .../colony/expeditions/ExpeditionStage.java | 2 +- .../expeditions/ExpeditionVisitorMember.java | 2 +- .../colony/ColonyExpeditionEvent.java | 227 ++++++++++++++++++ .../colony/ColonyExpeditionType.java | 13 + .../colony/managers/ExpeditionManager.java | 30 ++- src/main/resources/META-INF/mods.toml | 6 +- .../gui/layouthuts/layoutexpeditionlog.xml | 2 +- 22 files changed, 480 insertions(+), 155 deletions(-) create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/AbstractExpeditionEvent.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java diff --git a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEntitySpawnEvent.java b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEntitySpawnEvent.java index 9df68f84d85..40a42603dee 100755 --- a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEntitySpawnEvent.java +++ b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEntitySpawnEvent.java @@ -9,7 +9,7 @@ /** * A colony event which spawns and uses entities */ -public interface IColonyEntitySpawnEvent extends IColonySpawnEvent +public interface IColonyEntitySpawnEvent { /** * The list of entities related to this event diff --git a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEvent.java b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEvent.java index 1a51f0062cf..7610a8d8873 100755 --- a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEvent.java +++ b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyEvent.java @@ -1,11 +1,9 @@ package com.minecolonies.api.colony.colonyEvents; -import com.minecolonies.api.colony.IColony; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.common.util.INBTSerializable; -import org.jetbrains.annotations.NotNull; /** * Interface for all colony event types. @@ -40,13 +38,6 @@ public interface IColonyEvent extends INBTSerializable */ ResourceLocation getEventTypeID(); - /** - * Sets the colony - * - * @param colony the colony to set. - */ - void setColony(@NotNull final IColony colony); - /* * * Event triggers diff --git a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyRaidEvent.java b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyRaidEvent.java index 511bc895343..0b68fb809ec 100755 --- a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyRaidEvent.java +++ b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyRaidEvent.java @@ -8,7 +8,7 @@ /** * Interface type for raid events */ -public interface IColonyRaidEvent extends IColonyEntitySpawnEvent +public interface IColonyRaidEvent extends IColonyEvent, IColonySpawnEvent, IColonyEntitySpawnEvent { /** * Get the normal raider type. diff --git a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonySpawnEvent.java b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonySpawnEvent.java index e90e51c1b22..5ac838fcc0a 100755 --- a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonySpawnEvent.java +++ b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonySpawnEvent.java @@ -5,7 +5,7 @@ /** * An colony event which spawns at a certain position */ -public interface IColonySpawnEvent extends IColonyEvent +public interface IColonySpawnEvent { /** * Sets the spawn point diff --git a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyStructureSpawnEvent.java b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyStructureSpawnEvent.java index add6493c2c8..7352e7631ae 100755 --- a/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyStructureSpawnEvent.java +++ b/src/api/java/com/minecolonies/api/colony/colonyEvents/IColonyStructureSpawnEvent.java @@ -8,7 +8,7 @@ /** * Used by events which do spawn a structure in the world */ -public interface IColonyStructureSpawnEvent extends IColonyEvent +public interface IColonyStructureSpawnEvent { /** * Returns the list of used schematics and their positions. The string should be the full path to the schematic file. diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java b/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java index 4239ab2b375..011fa9fce62 100644 --- a/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java +++ b/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java @@ -1,5 +1,7 @@ package com.minecolonies.api.colony.expeditions; +import com.minecolonies.api.colony.colonyEvents.EventStatus; + /** * Enum describing the different statuses of an expedition. */ @@ -8,17 +10,48 @@ public enum ExpeditionStatus /** * Initial expedition state, expedition exists but has not been started yet. */ - CREATED, + CREATED(EventStatus.WAITING), + /** + * The expedition is ready and is about to leave. + */ + READY(EventStatus.STARTING), /** * The expedition embarked on their journey and is currently in progress. */ - EMBARKED, + EMBARKED(EventStatus.PROGRESSING), /** * The expedition has returned safely to the colony. */ - RETURNED, + RETURNED(EventStatus.DONE), + /** + * The expedition has been killed off. + */ + KILLED(EventStatus.DONE), + /** + * The expedition has gotten lost. + */ + LOST(EventStatus.DONE); + + /** + * The underlying status for the expedition event. + */ + private final EventStatus eventStatus; + + /** + * Internal constructor. + */ + ExpeditionStatus(final EventStatus eventStatus) + { + this.eventStatus = eventStatus; + } + /** - * The expedition has not returned in time, they either got lost or have been killed. + * Get the underlying status for the expedition event. + * + * @return the event status. */ - MISSING; + public EventStatus getEventStatus() + { + return eventStatus; + } } \ No newline at end of file diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java b/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java index 54c423ca8f8..bfe887d0c8d 100644 --- a/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java +++ b/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java @@ -1,13 +1,14 @@ package com.minecolonies.api.colony.expeditions; +import com.minecolonies.api.colony.colonyEvents.EventStatus; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.List; /** @@ -46,29 +47,27 @@ public interface IExpedition List getMembers(); /** - * Get the currently active members of the expedition. + * Get the equipment given to the expedition at start time. * - * @return the expedition members. + * @return the equipment list. */ - @NotNull - List getActiveMembers(); + List getEquipment(); /** - * The equipment that has been given to this expedition to use. + * Get the currently active members of the expedition. * - * @return the list of equipment. + * @return the expedition members. */ @NotNull - List getEquipment(); + List getActiveMembers(); /** * The results of this expedition. - * Yields null as long as the stage of the expedition is not {@link ExpeditionStatus#RETURNED}. + * 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. */ - @Nullable - List getResults(); + Collection getResults(); /** * Advances the current stage of the expedition. diff --git a/src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java index d1c22f3e6fa..ffdbfa2f663 100644 --- a/src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java +++ b/src/api/java/com/minecolonies/api/colony/managers/interfaces/IExpeditionManager.java @@ -10,17 +10,18 @@ public interface IExpeditionManager { /** - * Check that determines if expeditions to a given dimension may be sent or not. + * Register a finished expedition to the manager. * - * @param dimension the target dimension. - * @return whether the target dimension is allowed or not. + * @param expedition the expedition instance. + * @param owner the owning class of the expedition. */ - boolean canGoToDimension(final ResourceKey dimension); + void addExpedition(final IExpedition expedition, final Class owner); /** - * Register a new expedition to the manager. + * Check that determines if expeditions to a given dimension may be sent or not. * - * @param expedition the expedition instance. + * @param dimension the target dimension. + * @return whether the target dimension is allowed or not. */ - void registerExpedition(final IExpedition expedition); + boolean canGoToDimension(final ResourceKey dimension); } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java b/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java index 9e7c9bb0e95..a78210831e1 100755 --- a/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java +++ b/src/main/java/com/minecolonies/apiimp/initializer/ModColonyEventTypeInitializer.java @@ -9,6 +9,7 @@ import com.minecolonies.core.colony.colonyEvents.raidEvents.norsemenevent.NorsemenShipRaidEvent; import com.minecolonies.core.colony.colonyEvents.raidEvents.pirateEvent.PirateGroundRaidEvent; import com.minecolonies.core.colony.colonyEvents.raidEvents.pirateEvent.PirateRaidEvent; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionEvent; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.registries.DeferredRegister; @@ -33,5 +34,6 @@ private ModColonyEventTypeInitializer() DEFERRED_REGISTER.register(NorsemenRaidEvent.NORSEMEN_RAID_EVENT_TYPE_ID.getPath(), () -> new ColonyEventTypeRegistryEntry(NorsemenRaidEvent::loadFromNBT, NorsemenRaidEvent.NORSEMEN_RAID_EVENT_TYPE_ID)); 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(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/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java index 0b3711f77cd..eca866f2ce6 100644 --- a/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java +++ b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java @@ -5,15 +5,20 @@ import com.ldtteam.blockui.controls.Text; import com.ldtteam.blockui.views.View; import com.minecolonies.api.colony.IVisitorViewData; +import com.minecolonies.api.colony.expeditions.IExpedition; +import com.minecolonies.api.util.InventoryUtils; import com.minecolonies.api.util.constant.Constants; import com.minecolonies.core.Network; import com.minecolonies.core.client.gui.AbstractWindowSkeleton; +import com.minecolonies.core.colony.expeditions.Expedition; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType.Difficulty; import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType; import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.items.IItemHandler; import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -57,7 +62,7 @@ public MainWindowExpeditionary(final @NotNull IVisitorViewData visitorData) updateDifficulty(); - registerButton(LABEL_EXPEDITION_NAME, this::startExpedition); + registerButton(LABEL_EXPEDITION_NAME, this::openVisitorInventory); } @Override @@ -67,9 +72,9 @@ public void onUpdate() } /** - * Triggers starting the expedition. + * Open visitor inventory for providing tools. */ - private void startExpedition() + private void openVisitorInventory() { Network.getNetwork().sendToServer(new OpenInventoryMessage(visitorData.getColony(), visitorData.getName(), visitorData.getId())); } @@ -104,4 +109,13 @@ private void updateDifficulty() .hoverPane(findPaneOfTypeByID(VIEW_EXPEDITION_DIFFICULTY, View.class)) .build(); } + + /** + * Triggers starting the expedition. + */ + private void startExpedition() + { + visitorData.getInventory().isEmpty(); + final IExpedition expedition = new Expedition(); + } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/AbstractShipRaidEvent.java b/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/AbstractShipRaidEvent.java index 62f453a14bb..4d90dcf3dee 100755 --- a/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/AbstractShipRaidEvent.java +++ b/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/AbstractShipRaidEvent.java @@ -416,12 +416,6 @@ public void setShipRotation(final int shipRotation) this.shipRotation = shipRotation; } - @Override - public void setColony(@NotNull final IColony colony) - { - this.colony = colony; - } - @Override public void setSpawnPoint(final BlockPos spawnPoint) { diff --git a/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/HordeRaidEvent.java b/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/HordeRaidEvent.java index cd0f323a2da..8dc6fb8b01a 100755 --- a/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/HordeRaidEvent.java +++ b/src/main/java/com/minecolonies/core/colony/colonyEvents/raidEvents/HordeRaidEvent.java @@ -2,10 +2,7 @@ import com.minecolonies.api.colony.ColonyState; import com.minecolonies.api.colony.IColony; -import com.minecolonies.api.colony.colonyEvents.EventStatus; -import com.minecolonies.api.colony.colonyEvents.IColonyCampFireRaidEvent; -import com.minecolonies.api.colony.colonyEvents.IColonyEvent; -import com.minecolonies.api.colony.colonyEvents.IColonyRaidEvent; +import com.minecolonies.api.colony.colonyEvents.*; import com.minecolonies.api.entity.citizen.happiness.ExpirationBasedHappinessModifier; import com.minecolonies.api.entity.citizen.happiness.StaticHappinessSupplier; import com.minecolonies.api.entity.mobs.AbstractEntityRaiderMob; @@ -36,7 +33,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.pathfinder.Path; -import org.jetbrains.annotations.NotNull; import java.util.*; @@ -190,12 +186,6 @@ public int getID() return id; } - @Override - public void setColony(@NotNull final IColony colony) - { - this.colony = colony; - } - /** * Called when an entity is removed * diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/AbstractExpeditionEvent.java b/src/main/java/com/minecolonies/core/colony/expeditions/AbstractExpeditionEvent.java new file mode 100644 index 00000000000..ff609ecc8aa --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/AbstractExpeditionEvent.java @@ -0,0 +1,105 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.colonyEvents.EventStatus; +import com.minecolonies.api.colony.colonyEvents.IColonyEvent; +import com.minecolonies.api.colony.expeditions.IExpedition; +import net.minecraft.nbt.CompoundTag; +import org.apache.commons.lang3.function.TriFunction; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_EVENT_ID; + +/** + * Abstract implementation for expedition related events. + */ +public abstract class AbstractExpeditionEvent implements IColonyEvent +{ + /** + * NBT tags. + */ + private static final String TAG_EXPEDITION = "expedition"; + + /** + * The event ID. + */ + private final int id; + + /** + * The colony this event is for. + */ + private final IColony colony; + + /** + * The expedition instance. + */ + private final IExpedition expedition; + + /** + * Internal constructor. + */ + protected AbstractExpeditionEvent(final int id, final IColony colony, final IExpedition expedition) + { + this.id = id; + this.colony = colony; + this.expedition = expedition; + } + + /** + * Construct an abstract colony from NBT data, meant to be called from implementations passing specific constructors along. + * + * @param colony the target colony. + * @param compound the input compound data. + * @param eventLoader a function for creating the event instance. + * @param the generic type for the event class. + * @return the created event instance. + */ + public static T loadFromNBT( + final IColony colony, + final CompoundTag compound, + final TriFunction eventLoader) + { + final int id = compound.getInt(TAG_EVENT_ID); + final IExpedition expedition = Expedition.loadFromNBT(compound.getCompound(TAG_EXPEDITION)); + final T event = eventLoader.apply(id, colony, expedition); + event.deserializeNBT(compound); + return event; + } + + @Override + public final EventStatus getStatus() + { + return expedition.getStatus().getEventStatus(); + } + + @Override + public final 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 final int getID() + { + return id; + } + + /** + * Get the colony for this event. + * + * @return the colony instance. + */ + public final IColony getColony() + { + return colony; + } + + /** + * Get the expedition instance belonging to this event. + * + * @return the expedition instance. + */ + public IExpedition getExpedition() + { + return expedition; + } +} diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java b/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java index 14027e8f99f..c3ad3ccd989 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java @@ -14,36 +14,34 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_DIMENSION; /** * Class for an expedition instance. */ -public class Expedition implements IExpedition +public final class Expedition 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_EQUIPMENT = "equipment"; /** * The dimension to send the expedition to. */ + @NotNull private final ResourceKey dimensionId; /** - * The results of this expedition. + * The equipment given to the expedition prior to starting. */ - private final List results = new ArrayList<>(List.of(new ExpeditionStage())); + @NotNull + private final List equipment; /** * The members of the expedition. @@ -52,24 +50,23 @@ public class Expedition implements IExpedition private final List members; /** - * The list of items to give to the expedition members. + * The results of this expedition. */ - @NotNull - private final List equipment; + private final Deque results = new ArrayDeque<>(List.of(new ExpeditionStage())); /** * The stage of the expedition. */ - private ExpeditionStatus stage = ExpeditionStatus.CREATED; + private ExpeditionStatus status = ExpeditionStatus.CREATED; /** * Internal constructor. */ - private Expedition(final ResourceKey dimensionId, final @NotNull List members, final @NotNull List equipment) + public Expedition(final @NotNull ResourceKey dimensionId, final @NotNull List equipment, final @NotNull List members) { this.dimensionId = dimensionId; - this.members = members; this.equipment = equipment; + this.members = members; } /** @@ -78,7 +75,7 @@ private Expedition(final ResourceKey dimensionId, final @NotNull List getMembers() } @Override - public @NotNull List getActiveMembers() + public List getEquipment() { - return this.members.stream().filter(f -> !f.isDead()).toList(); + return equipment; } @Override - @NotNull - public List getEquipment() + public @NotNull List getActiveMembers() { - return this.equipment; + return this.members.stream().filter(f -> !f.isDead()).toList(); } @Override - @Nullable - public List getResults() + public @NotNull Collection getResults() { - return Collections.unmodifiableList(this.results); + return Collections.unmodifiableCollection(this.results); } @Override @@ -137,19 +132,19 @@ public void advanceStage() @Override public void rewardFound(final ItemStack itemStack) { - this.results.get(this.results.size() - 1).addReward(itemStack); + this.results.getLast().addReward(itemStack); } @Override public void mobKilled(final EntityType type) { - this.results.get(this.results.size() - 1).rewardFound(type); + this.results.getLast().rewardFound(type); } @Override public void memberLost(final IExpeditionMember member) { - this.results.get(this.results.size() - 1).memberLost(member); + this.results.getLast().memberLost(member); member.died(); } @@ -159,57 +154,6 @@ public void write(final CompoundTag compound) Serializer.write(this, compound); } - /** - * Builder for an expedition. - */ - public static class Builder - { - /** - * The level to send the expedition to. - */ - private final ResourceKey dimensionId; - - /** - * The list of items to give to the expedition members. - */ - private final List equipment = new ArrayList<>(); - - /** - * The members of the expedition. - */ - private final List members = new ArrayList<>(); - - /** - * Default constructor. - */ - public Builder(final ResourceKey dimensionId) - { - this.dimensionId = dimensionId; - } - - /** - * Add additional members to this expedition. - * - * @param member the new member. - * @return the builder for chaining. - */ - public Builder addMembers(final IExpeditionMember member) - { - this.members.add(member); - return this; - } - - /** - * Finalizes creation of the expedition instance. - * - * @return the created expedition. - */ - public Expedition build() - { - return new Expedition(dimensionId, members, equipment); - } - } - /** * Serializer for the expeditions. */ @@ -248,7 +192,7 @@ else if (Objects.equals(memberType, "visitor")) equipment.add(ItemStack.of(equipmentList.getCompound(i))); } - return new Expedition(dimensionId, members, equipment); + return new Expedition(dimensionId, equipment, members); } /** @@ -259,10 +203,10 @@ else if (Objects.equals(memberType, "visitor")) */ public static void write(final Expedition expedition, final CompoundTag compound) { - compound.putString(TAG_DIMENSION, expedition.dimensionId.location().toString()); + compound.putString(TAG_DIMENSION, expedition.getTargetDimension().location().toString()); final ListTag memberTag = new ListTag(); - for (final IExpeditionMember member : expedition.members) + for (final IExpeditionMember member : expedition.getMembers()) { final CompoundTag memberCompound = new CompoundTag(); if (member instanceof ExpeditionCitizenMember) diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java index 866fbb7f329..6cf8be98969 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java @@ -13,7 +13,7 @@ /** * Citizen expedition members. */ -public class ExpeditionCitizenMember implements IExpeditionMember +public final class ExpeditionCitizenMember implements IExpeditionMember { /** * Nbt tag constants. diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java index 9b9bb7ceee1..67f2f167d7d 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionStage.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Map; -public class ExpeditionStage implements IExpeditionStage +public final class ExpeditionStage implements IExpeditionStage { @Override public List getRewards() diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java index 9299f04f62a..47a215b4f40 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java @@ -13,7 +13,7 @@ /** * Visitor expedition members. */ -public class ExpeditionVisitorMember implements IExpeditionMember +public final class ExpeditionVisitorMember implements IExpeditionMember { /** * Nbt tag constants. diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java new file mode 100644 index 00000000000..4946166026c --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java @@ -0,0 +1,227 @@ +package com.minecolonies.core.colony.expeditions.colony; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; +import com.minecolonies.api.colony.expeditions.IExpedition; +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.NBTUtils; +import com.minecolonies.api.util.constant.Constants; +import com.minecolonies.core.colony.expeditions.AbstractExpeditionEvent; +import com.minecolonies.core.items.ItemAdventureToken; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +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.minecraft.world.level.storage.loot.LootTable; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet; +import net.minecraftforge.common.extensions.IForgeItemStack; +import net.minecraftforge.items.ItemStackHandler; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Random; +import java.util.stream.Collectors; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_INVENTORY; + +public class ColonyExpeditionEvent extends AbstractExpeditionEvent +{ + /** + * 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_TYPE = "expeditionType"; + private static final String TAG_DAYS_IN_PROGRESS = "daysInProgress"; + private static final String TAG_REMAINING_ITEMS = "remainingItems"; + + /** + * The size of the expedition inventory. + */ + private static final int EXPEDITION_INVENTORY_SIZE = 27; + + /** + * The maximum amount of days an expedition can take. + * This is actually 3 days, but we take into consideration the potential partial first day of travelling. + */ + private static final int MAX_EXPEDITION_DAYS_LENGTH = 4; + + /** + * The inventory for the expedition. + */ + private final ItemStackHandler inventory = new ItemStackHandler(EXPEDITION_INVENTORY_SIZE); + + /** + * Random instance for calculating random values. + */ + private final Random random = new Random(); + + /** + * The expedition type for this colony expedition. + */ + private ColonyExpeditionType expeditionType; + + /** + * The amount of days that the expedition is currently in progress. + */ + private int daysInProgress = 0; + + /** + * Contains a set of items that still have yet to be found. + */ + @Nullable + private Deque remainingItems; + + /** + * Create a new colony expedition event. + * + * @param colony the colony instance. + * @param expedition the expedition instance. + */ + public ColonyExpeditionEvent(final IColony colony, final IExpedition expedition) + { + super(colony.getEventManager().getAndTakeNextEventID(), colony, expedition); + } + + /** + * Create a new colony expedition event. + * + * @param id the event ID. + * @param colony the colony instance. + * @param expedition the expedition instance. + */ + private ColonyExpeditionEvent(final int id, final IColony colony, final IExpedition expedition) + { + super(id, colony, expedition); + } + + /** + * 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) + { + return AbstractExpeditionEvent.loadFromNBT(colony, compound, ColonyExpeditionEvent::new); + } + + private void processAdventureToken(final ItemStack itemStack) + { + } + + @Override + public ResourceLocation getEventTypeID() + { + return COLONY_EXPEDITION_EVENT_TYPE_ID; + } + + @Override + public void onUpdate() + { + if (!getExpedition().getStatus().equals(ExpeditionStatus.EMBARKED) || remainingItems == null) + { + return; + } + + final ItemStack nextItem = remainingItems.getFirst(); + if (nextItem.getItem() instanceof ItemAdventureToken) + { + if (nextItem.hasTag()) + { + processAdventureToken(nextItem); + } + } + else + { + getExpedition().rewardFound(nextItem); + } + } + + @Override + public void onStart() + { + final Level world = getColony().getWorld(); + if (!world.isClientSide) + { + getExpedition().getEquipment().forEach(f -> InventoryUtils.addItemStackToItemHandler(inventory, f)); + + final LootParams lootParams = new Builder((ServerLevel) world) + .withLuck(expeditionType.getDifficulty().getLuckLevel()) + .create(LootContextParamSet.builder().build()); + + final LootTable lootTable = getColony().getWorld().getServer().getLootData().getLootTable(expeditionType.getLootTable()); + + // Copy the items, natively a Stack implementation, to a deque, so we can pop the first item off each colony tick. + remainingItems = new ArrayDeque<>(lootTable.getRandomItems(lootParams)); + } + } + + @Override + public void onFinish() + { + getColony().getExpeditionManager().addExpedition(getExpedition(), ColonyExpeditionEvent.class); + } + + @Override + public void onNightFall() + { + if (getExpedition().getStatus().equals(ExpeditionStatus.EMBARKED)) + { + daysInProgress++; + if (daysInProgress >= MAX_EXPEDITION_DAYS_LENGTH) + { + final int chance = random.nextInt(100); + if (chance <= 2) + { + getExpedition().setStatus(ExpeditionStatus.LOST); + } + else + { + getExpedition().setStatus(ExpeditionStatus.RETURNED); + } + } + } + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + compound.putString(TAG_EXPEDITION_TYPE, expeditionType.getId().toString()); + compound.putInt(TAG_DAYS_IN_PROGRESS, daysInProgress); + compound.put(TAG_INVENTORY, inventory.serializeNBT()); + + if (remainingItems != null) + { + compound.put(TAG_REMAINING_ITEMS, remainingItems.stream() + .map(IForgeItemStack::serializeNBT) + .collect(NBTUtils.toListNBT())); + } + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + expeditionType = ColonyExpeditionTypeManager.getInstance().getExpeditionType(new ResourceLocation(compoundTag.getString(TAG_EXPEDITION_TYPE))); + daysInProgress = compoundTag.getInt(TAG_DAYS_IN_PROGRESS); + inventory.deserializeNBT(compoundTag.getCompound(TAG_INVENTORY)); + + if (compoundTag.contains(TAG_REMAINING_ITEMS)) + { + remainingItems = NBTUtils.streamCompound(compoundTag.getList(TAG_REMAINING_ITEMS, Tag.TAG_COMPOUND)) + .map(ItemStack::of) + .collect(Collectors.toCollection(ArrayDeque::new)); + } + } +} diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java index 2a704ee31d0..899b00e3651 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java @@ -289,6 +289,9 @@ public enum Difficulty */ private final Style style; + /** + * Internal constructor. + */ Difficulty(final String key, final int level, final int luckLevel, final Item icon, final boolean hidden, final Style style) { this.key = key; @@ -367,5 +370,15 @@ public Style getStyle() { return style; } + + /** + * Get the luck level an expedition of the given difficulty will have. + * + * @return the luck level. + */ + public int getLuckLevel() + { + return luckLevel; + } } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java b/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java index 80d58937f98..c2030b0ea6e 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java @@ -1,5 +1,6 @@ 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.IExpedition; @@ -11,16 +12,28 @@ import net.minecraftforge.eventbus.api.Event.HasResult; import net.minecraftforge.eventbus.api.Event.Result; +import java.util.HashMap; + /** * Implementation for the colony expedition manager. From here all outgoing expeditions to external places are managed. */ public class ExpeditionManager implements IExpeditionManager { + /** + * The maximum amount of expeditions kept in the history, separated per expedition owner type. + */ + private static final int MAX_EXPEDITION_HISTORY = 5; + /** * The colony this manager is for. */ private final IColony colony; + /** + * The currently registered expeditions. + */ + private final HashMap, EvictingQueue> expeditions; + /** * Whether a ruined portal has been discovered by an expedition. */ @@ -37,6 +50,14 @@ public class ExpeditionManager implements IExpeditionManager public ExpeditionManager(final IColony colony) { this.colony = colony; + this.expeditions = new HashMap<>(); + } + + @Override + public void addExpedition(final IExpedition expedition, final Class owner) + { + this.expeditions.computeIfAbsent(owner, clazz -> EvictingQueue.create(MAX_EXPEDITION_HISTORY)); + this.expeditions.get(owner).add(expedition); } @Override @@ -65,15 +86,6 @@ else if (dimension.equals(Level.END)) return false; } - @Override - public void registerExpedition(final IExpedition expedition) - { - if (!canGoToDimension(expedition.getTargetDimension())) - { - return; - } - } - //@Override //public IExpedition createExpedition(final Level level, final Consumer expeditionBuilder) diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index f2a16ac91fb..7251db5322b 100755 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -3,16 +3,16 @@ # The overall format is standard TOML format, v0.5.0. # Note that there are a couple of TOML lists in this file. # Find more information on toml format here: https://github.com/toml-lang/toml -# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +# The name of the mod eventLoader type to load - for regular FML @Mod mods it should be javafml modLoader="javafml" #mandatory -# A version range to match for said mod loader - for regular FML @Mod it will be the forge version +# A version range to match for said mod eventLoader - for regular FML @Mod it will be the forge version loaderVersion="${fml_range}" #mandatory (27 is current forge version) # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. license="GPL 3.0" # A URL to refer people to when problems occur with this mod issueTrackerURL="https://github.com/ldtteam/minecolonies/issues/new/choose" #optional -# A list of mods - how many allowed here is determined by the individual mod loader +# A list of mods - how many allowed here is determined by the individual mod eventLoader [[mods]] #mandatory # The modid of the mod modId="minecolonies" #mandatory diff --git a/src/main/resources/assets/minecolonies/gui/layouthuts/layoutexpeditionlog.xml b/src/main/resources/assets/minecolonies/gui/layouthuts/layoutexpeditionlog.xml index 4aa31dd654f..b49d4599d5a 100644 --- a/src/main/resources/assets/minecolonies/gui/layouthuts/layoutexpeditionlog.xml +++ b/src/main/resources/assets/minecolonies/gui/layouthuts/layoutexpeditionlog.xml @@ -19,5 +19,5 @@ + tooltip="$(com.minecolonies.gui.workerhuts.abstractExpedition.killed.tip)" visible="false" /> From 3fefe0f1f63dbccb0301315bd418a36af2798e45 Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Sun, 28 Jan 2024 15:01:36 +0100 Subject: [PATCH 08/94] Temp --- .../colony/expeditions/ExpeditionStatus.java | 8 +- .../api/entity/visitor/IVisitorExtraData.java | 5 +- .../MainWindowExpeditionary.java | 34 +++++-- .../expeditions/ExpeditionVisitorMember.java | 4 +- .../colony/ColonyExpeditionEvent.java | 98 ++++++++++++------- .../colony/managers/ExpeditionManager.java | 18 +--- .../core/colony/managers/VisitorManager.java | 20 ++++ .../visitor/ExpeditionaryVisitorType.java | 41 +++++++- .../expeditionary/StartExpeditionMessage.java | 53 ++++++++++ 9 files changed, 211 insertions(+), 70 deletions(-) create mode 100644 src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/StartExpeditionMessage.java diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java b/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java index 011fa9fce62..9e344fee918 100644 --- a/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java +++ b/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java @@ -8,13 +8,9 @@ public enum ExpeditionStatus { /** - * Initial expedition state, expedition exists but has not been started yet. + * Default state, the moment this expedition is registered as an event to the colony they will embark. */ - CREATED(EventStatus.WAITING), - /** - * The expedition is ready and is about to leave. - */ - READY(EventStatus.STARTING), + CREATED(EventStatus.STARTING), /** * The expedition embarked on their journey and is currently in progress. */ diff --git a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java index b1a9ea4ccb2..a9b535a6bc6 100644 --- a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java +++ b/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java @@ -3,6 +3,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraftforge.common.util.INBTSerializable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Interface for extra visitor data. @@ -22,7 +23,7 @@ public interface IVisitorExtraData extends INBTSerializable * * @return the value. */ - @NotNull + @Nullable S getValue(); /** @@ -37,6 +38,6 @@ public interface IVisitorExtraData extends INBTSerializable * * @return the value. */ - @NotNull + @Nullable S getDefaultValue(); } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java index eca866f2ce6..e7b10b95968 100644 --- a/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java +++ b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java @@ -5,27 +5,33 @@ import com.ldtteam.blockui.controls.Text; import com.ldtteam.blockui.views.View; import com.minecolonies.api.colony.IVisitorViewData; +import com.minecolonies.api.colony.expeditions.ExpeditionStatus; import com.minecolonies.api.colony.expeditions.IExpedition; +import com.minecolonies.api.colony.expeditions.IExpeditionMember; import com.minecolonies.api.util.InventoryUtils; import com.minecolonies.api.util.constant.Constants; import com.minecolonies.core.Network; import com.minecolonies.core.client.gui.AbstractWindowSkeleton; import com.minecolonies.core.colony.expeditions.Expedition; +import com.minecolonies.core.colony.expeditions.ExpeditionVisitorMember; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType.Difficulty; -import com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType; import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; +import com.minecolonies.core.network.messages.server.colony.visitor.expeditionary.StartExpeditionMessage; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.player.Player; -import net.minecraftforge.items.IItemHandler; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import static com.minecolonies.api.util.constant.WindowConstants.EXPEDITIONARY_MAIN_RESOURCE_SUFFIX; import static com.minecolonies.api.util.constant.translation.GuiTranslationConstants.EXPEDITIONARY_DIFFICULTY; import static com.minecolonies.api.util.constant.translation.GuiTranslationConstants.EXPEDITIONARY_DIFFICULTY_PREFIX; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION_TYPE; /** * Main window for the expeditionary their GUI. @@ -56,7 +62,7 @@ public MainWindowExpeditionary(final @NotNull IVisitorViewData visitorData) { super(Constants.MOD_ID + EXPEDITIONARY_MAIN_RESOURCE_SUFFIX); this.visitorData = visitorData; - this.expeditionType = visitorData.getExtraDataValue(ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION_TYPE).orElseThrow(); + this.expeditionType = visitorData.getExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE).orElseThrow(); findPaneOfTypeByID(LABEL_EXPEDITION_NAME, Text.class).setText(expeditionType.getName()); @@ -115,7 +121,23 @@ private void updateDifficulty() */ private void startExpedition() { - visitorData.getInventory().isEmpty(); - final IExpedition expedition = new Expedition(); + // Gather the inventory and the armor slots from the inventory. + final List equipment = InventoryUtils.getItemHandlerAsList(visitorData.getInventory()); + for (final EquipmentSlot equipmentSlot : EquipmentSlot.values()) + { + final ItemStack armorItem = visitorData.getInventory().getArmorInSlot(equipmentSlot); + if (armorItem != ItemStack.EMPTY) + { + equipment.add(armorItem); + } + } + + final List members = new ArrayList<>(); + members.add(new ExpeditionVisitorMember(visitorData)); + // TODO: Iterate assigned guards + + final IExpedition expedition = new Expedition(expeditionType.getDimension(), equipment, members); + expedition.setStatus(ExpeditionStatus.EMBARKED); + Network.getNetwork().sendToServer(new StartExpeditionMessage(visitorData.getColony(), expedition)); } } \ 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 index 47a215b4f40..c175ffa2fd0 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java @@ -2,7 +2,7 @@ import com.minecolonies.api.colony.ICivilianData; import com.minecolonies.api.colony.IColony; -import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.colony.IVisitorViewData; import com.minecolonies.api.colony.expeditions.IExpeditionMember; import net.minecraft.nbt.CompoundTag; import org.jetbrains.annotations.Nullable; @@ -50,7 +50,7 @@ public ExpeditionVisitorMember(final CompoundTag compound) * * @param visitorData the visitor to create the expedition member for. */ - public ExpeditionVisitorMember(final IVisitorData visitorData) + public ExpeditionVisitorMember(final IVisitorViewData visitorData) { this.id = visitorData.getId(); this.name = visitorData.getName(); diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java index 4946166026c..81586f4f0bd 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java @@ -27,6 +27,7 @@ import java.util.Random; import java.util.stream.Collectors; +import static com.minecolonies.api.util.constant.Constants.TICKS_HOUR; import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_INVENTORY; public class ColonyExpeditionEvent extends AbstractExpeditionEvent @@ -39,21 +40,14 @@ public class ColonyExpeditionEvent extends AbstractExpeditionEvent /** * NBT tags. */ - private static final String TAG_EXPEDITION_TYPE = "expeditionType"; - private static final String TAG_DAYS_IN_PROGRESS = "daysInProgress"; - private static final String TAG_REMAINING_ITEMS = "remainingItems"; + private static final String TAG_EXPEDITION_TYPE = "expeditionType"; + private static final String TAG_REMAINING_ITEMS = "remainingItems"; /** * The size of the expedition inventory. */ private static final int EXPEDITION_INVENTORY_SIZE = 27; - /** - * The maximum amount of days an expedition can take. - * This is actually 3 days, but we take into consideration the potential partial first day of travelling. - */ - private static final int MAX_EXPEDITION_DAYS_LENGTH = 4; - /** * The inventory for the expedition. */ @@ -69,17 +63,27 @@ public class ColonyExpeditionEvent extends AbstractExpeditionEvent */ private ColonyExpeditionType expeditionType; - /** - * The amount of days that the expedition is currently in progress. - */ - private int daysInProgress = 0; - /** * Contains a set of items that still have yet to be found. */ @Nullable private Deque remainingItems; + /** + * The minimum time that the expedition is able to end at. + */ + private long endTime = -1; + + /** + * Whether the timeout has passed. + */ + private boolean isMinimumTimeElapsed = false; + + /** + * + */ + private boolean isRemainingItemsEmpty = false; + /** * Create a new colony expedition event. * @@ -128,12 +132,53 @@ public ResourceLocation getEventTypeID() @Override public void onUpdate() { + // Skip the update entirely if we're not embarked just yet if (!getExpedition().getStatus().equals(ExpeditionStatus.EMBARKED) || remainingItems == null) { return; } + // We have to continuously check if the minimum end time of the expedition has already passed + if (!isMinimumTimeElapsed && endTime < getColony().getWorld().getGameTime()) + { + isMinimumTimeElapsed = true; + } + + // If the minimum time has passed and the loot table is empty, we can finish the expedition + if (isMinimumTimeElapsed && isRemainingItemsEmpty) + { + if (getExpedition().getActiveMembers().isEmpty()) + { + getExpedition().setStatus(ExpeditionStatus.KILLED); + return; + } + + final int chance = random.nextInt(100); + if (chance <= 2) + { + getExpedition().setStatus(ExpeditionStatus.LOST); + } + else + { + getExpedition().setStatus(ExpeditionStatus.RETURNED); + } + return; + } + + // If the deque is empty, we can set the flag for loot table empty to be done. + if (remainingItems.isEmpty()) + { + isRemainingItemsEmpty = true; + return; + } + + // Process the next item in the loot table deque. final ItemStack nextItem = remainingItems.getFirst(); + if (nextItem.equals(ItemStack.EMPTY)) + { + return; + } + if (nextItem.getItem() instanceof ItemAdventureToken) { if (nextItem.hasTag()) @@ -153,6 +198,8 @@ public void onStart() final Level world = getColony().getWorld(); if (!world.isClientSide) { + endTime = world.getGameTime() + TICKS_HOUR; + getExpedition().getEquipment().forEach(f -> InventoryUtils.addItemStackToItemHandler(inventory, f)); final LootParams lootParams = new Builder((ServerLevel) world) @@ -172,33 +219,11 @@ public void onFinish() getColony().getExpeditionManager().addExpedition(getExpedition(), ColonyExpeditionEvent.class); } - @Override - public void onNightFall() - { - if (getExpedition().getStatus().equals(ExpeditionStatus.EMBARKED)) - { - daysInProgress++; - if (daysInProgress >= MAX_EXPEDITION_DAYS_LENGTH) - { - final int chance = random.nextInt(100); - if (chance <= 2) - { - getExpedition().setStatus(ExpeditionStatus.LOST); - } - else - { - getExpedition().setStatus(ExpeditionStatus.RETURNED); - } - } - } - } - @Override public CompoundTag serializeNBT() { final CompoundTag compound = new CompoundTag(); compound.putString(TAG_EXPEDITION_TYPE, expeditionType.getId().toString()); - compound.putInt(TAG_DAYS_IN_PROGRESS, daysInProgress); compound.put(TAG_INVENTORY, inventory.serializeNBT()); if (remainingItems != null) @@ -214,7 +239,6 @@ public CompoundTag serializeNBT() public void deserializeNBT(final CompoundTag compoundTag) { expeditionType = ColonyExpeditionTypeManager.getInstance().getExpeditionType(new ResourceLocation(compoundTag.getString(TAG_EXPEDITION_TYPE))); - daysInProgress = compoundTag.getInt(TAG_DAYS_IN_PROGRESS); inventory.deserializeNBT(compoundTag.getCompound(TAG_INVENTORY)); if (compoundTag.contains(TAG_REMAINING_ITEMS)) diff --git a/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java b/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java index c2030b0ea6e..1b0abd54b3d 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/ExpeditionManager.java @@ -13,6 +13,7 @@ import net.minecraftforge.eventbus.api.Event.Result; import java.util.HashMap; +import java.util.Map; /** * Implementation for the colony expedition manager. From here all outgoing expeditions to external places are managed. @@ -32,7 +33,7 @@ public class ExpeditionManager implements IExpeditionManager /** * The currently registered expeditions. */ - private final HashMap, EvictingQueue> expeditions; + private final Map, EvictingQueue> expeditions = new HashMap<>(); /** * Whether a ruined portal has been discovered by an expedition. @@ -50,7 +51,6 @@ public class ExpeditionManager implements IExpeditionManager public ExpeditionManager(final IColony colony) { this.colony = colony; - this.expeditions = new HashMap<>(); } @Override @@ -86,20 +86,6 @@ else if (dimension.equals(Level.END)) return false; } - - //@Override - //public IExpedition createExpedition(final Level level, final Consumer expeditionBuilder) - //{ - // //final LootContextParamSet lootContextParams = new LootContextParamSet.Builder().build(); - // // - // //final Builder lootContextBuilder = new Builder((ServerLevel) colony.getWorld()); - // //lootContextBuilder.withLuck(1); - // //final LootContext lootContext = lootContextBuilder.create(lootContextParams); - // // - // //colony.getWorld().getServer().getLootTables().get(new ResourceLocation("")).getRandomItems(lootContext); - // return null; - //} - /** * This event is fired by {@link ExpeditionManager#canGoToDimension(ResourceKey)}. * This allows other mods to control whether a dimension is allowed to send expedition to from the colony. 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 321b7167504..3efe76877b9 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java @@ -13,6 +13,8 @@ import com.minecolonies.api.util.WorldUtil; import com.minecolonies.core.Network; import com.minecolonies.core.colony.VisitorData; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; import com.minecolonies.core.entity.citizen.VisitorCitizen; import com.minecolonies.core.network.messages.client.colony.ColonyVisitorViewDataMessage; import net.minecraft.core.BlockPos; @@ -28,6 +30,7 @@ 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.EXTRA_DATA_EXPEDITION_TYPE; /** * Manages all visiting entities to the colony @@ -234,6 +237,14 @@ public IVisitorData createAndRegisterCivilianData() return createAndRegisterVisitor(ModVisitorTypes.visitor.get()); } + public IVisitorData createAndRegisterExpeditionary(@NotNull final ColonyExpeditionType expeditionType) + { + final IVisitorData expeditionary = createAndRegisterVisitor(ModVisitorTypes.expeditionary.get()); + expeditionary.setExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE, Optional.of(expeditionType)); + + return expeditionary; + } + /** * Create visitor data for the given visitor type. * @@ -280,6 +291,15 @@ public void onColonyTick(final IColony colony) { data.updateEntityIfNecessary(); } + + if (visitorMap.values().stream().noneMatch(f -> f.getVisitorType().equals(ModVisitorTypes.expeditionary.get()))) + { + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeManager.getInstance().getRandomExpeditionType(colony); + if (expeditionType != null) + { + createAndRegisterExpeditionary(expeditionType); + } + } } } } diff --git a/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java index 793e9d048d2..bb539553ee5 100644 --- a/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java @@ -2,6 +2,7 @@ import com.minecolonies.api.entity.ModEntities; import com.minecolonies.api.entity.visitor.*; +import com.minecolonies.core.colony.expeditions.Expedition; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; import com.minecolonies.core.entity.ai.visitor.EntityAIExpeditionary; @@ -21,6 +22,7 @@ public class ExpeditionaryVisitorType implements IVisitorType * Extra data fields. */ public static final ColonyExpeditionTypeData EXTRA_DATA_EXPEDITION_TYPE = new ColonyExpeditionTypeData(); + public static final ExpeditionData EXTRA_DATA_EXPEDTIION = new ExpeditionData(); @Override public ResourceLocation getId() @@ -43,9 +45,12 @@ public void createStateMachine(final AbstractEntityVisitor visitor) @Override public List> getExtraDataKeys() { - return List.of(EXTRA_DATA_EXPEDITION_TYPE); + return List.of(EXTRA_DATA_EXPEDITION_TYPE, EXTRA_DATA_EXPEDTIION); } + /** + * Extra data for storing the expedition type instance. + */ public static class ColonyExpeditionTypeData extends AbstractVisitorExtraData> { private static final String TAG_VALUE = "value"; @@ -72,4 +77,38 @@ public void deserializeNBT(final CompoundTag compoundTag) } } } + + /** + * Extra data for storing the expedition instance. + */ + public static class ExpeditionData extends AbstractVisitorExtraData> + { + private static final String TAG_VALUE = "value"; + + public ExpeditionData() + { + super("expedition", Optional.empty()); + } + + @Override + public CompoundTag serializeNBT() + { + final CompoundTag compound = new CompoundTag(); + getValue().ifPresent(val -> { + final CompoundTag valueCompound = new CompoundTag(); + val.write(valueCompound); + compound.put(TAG_VALUE, valueCompound); + }); + return compound; + } + + @Override + public void deserializeNBT(final CompoundTag compoundTag) + { + if (compoundTag.contains(TAG_VALUE)) + { + setValue(Optional.of(Expedition.loadFromNBT(compoundTag.getCompound(TAG_VALUE)))); + } + } + } } diff --git a/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/StartExpeditionMessage.java b/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/StartExpeditionMessage.java new file mode 100644 index 00000000000..f7b0451c45e --- /dev/null +++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/StartExpeditionMessage.java @@ -0,0 +1,53 @@ +package com.minecolonies.core.network.messages.server.colony.visitor.expeditionary; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.expeditions.IExpedition; +import com.minecolonies.core.colony.expeditions.Expedition; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionEvent; +import com.minecolonies.core.network.messages.server.AbstractColonyServerMessage; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent.Context; + +/** + * Network message for triggering a colony expedition start. + */ +public class StartExpeditionMessage extends AbstractColonyServerMessage +{ + /** + * The provided expedition instance. + */ + private IExpedition expedition; + + /** + * Default constructor. + * + * @param colony the colony this expedition will occur in. + * @param expedition the input expedition instance. + */ + public StartExpeditionMessage(final IColony colony, final IExpedition expedition) + { + super(colony); + this.expedition = expedition; + } + + @Override + protected void onExecute(final Context ctxIn, final boolean isLogicalServer, final IColony colony) + { + colony.getEventManager().addEvent(new ColonyExpeditionEvent(colony, expedition)); + } + + @Override + protected void toBytesOverride(final FriendlyByteBuf buf) + { + final CompoundTag compound = new CompoundTag(); + expedition.write(compound); + buf.writeNbt(compound); + } + + @Override + protected void fromBytesOverride(final FriendlyByteBuf buf) + { + expedition = Expedition.loadFromNBT(buf.readNbt()); + } +} From 74a655a1741357000fe7fac5d06f5595d3c0e89f Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Sun, 28 Jan 2024 15:21:24 +0100 Subject: [PATCH 09/94] Move --- .../api/entity/visitor/AbstractEntityVisitor.java | 0 .../api/entity/visitor/AbstractVisitorExtraData.java | 0 .../minecolonies/api/entity/visitor/IVisitorExtraData.java | 0 .../com/minecolonies/api/entity/visitor/IVisitorType.java | 0 .../com/minecolonies/api/entity/visitor/ModVisitorTypes.java | 0 .../com/minecolonies/core/entity/visitor/VisitorCitizen.java | 5 ----- 6 files changed, 5 deletions(-) rename src/{api => main}/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java (100%) rename src/{api => main}/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java (100%) rename src/{api => main}/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java (100%) rename src/{api => main}/java/com/minecolonies/api/entity/visitor/IVisitorType.java (100%) rename src/{api => main}/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java (100%) diff --git a/src/api/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java b/src/main/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java similarity index 100% rename from src/api/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java rename to src/main/java/com/minecolonies/api/entity/visitor/AbstractEntityVisitor.java diff --git a/src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java b/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java similarity index 100% rename from src/api/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java rename to src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java diff --git a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java similarity index 100% rename from src/api/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java rename to src/main/java/com/minecolonies/api/entity/visitor/IVisitorExtraData.java diff --git a/src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java similarity index 100% rename from src/api/java/com/minecolonies/api/entity/visitor/IVisitorType.java rename to src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java diff --git a/src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java b/src/main/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java similarity index 100% rename from src/api/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java rename to src/main/java/com/minecolonies/api/entity/visitor/ModVisitorTypes.java 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 35ab3d1d721..ae9ab5b9874 100644 --- a/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java @@ -7,8 +7,6 @@ import com.minecolonies.api.colony.requestsystem.location.ILocation; import com.minecolonies.api.entity.CustomGoalSelector; import com.minecolonies.api.entity.pathfinding.proxy.IWalkToProxy; -import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; -import com.minecolonies.api.entity.ai.pathfinding.IWalkToProxy; import com.minecolonies.api.entity.citizen.citizenhandlers.*; import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; import com.minecolonies.api.entity.visitor.IVisitorType; @@ -27,12 +25,10 @@ import com.minecolonies.core.colony.buildings.modules.TavernBuildingModule; import com.minecolonies.core.entity.ai.minimal.EntityAIInteractToggleAble; import com.minecolonies.core.entity.ai.minimal.LookAtEntityGoal; -import com.minecolonies.core.entity.ai.visitor.EntityAIVisitor; import com.minecolonies.core.entity.citizen.citizenhandlers.*; import com.minecolonies.core.entity.citizen.EntityCitizen; import com.minecolonies.core.entity.pathfinding.EntityCitizenWalkToProxy; import com.minecolonies.core.entity.pathfinding.MovementHandler; -import com.minecolonies.core.network.messages.client.ItemParticleEffectMessage; import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; @@ -64,7 +60,6 @@ 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.*; /** From 06bb8cb57afeec05f50b1e6e8d6f36126c13cc4b Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Sun, 28 Jan 2024 15:23:19 +0100 Subject: [PATCH 10/94] Move --- .../com/minecolonies/api/colony/expeditions/ExpeditionStatus.java | 0 .../java/com/minecolonies/api/colony/expeditions/IExpedition.java | 0 .../minecolonies/api/colony/expeditions/IExpeditionMember.java | 0 .../com/minecolonies/api/colony/expeditions/IExpeditionStage.java | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/{api => main}/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java (100%) rename src/{api => main}/java/com/minecolonies/api/colony/expeditions/IExpedition.java (100%) rename src/{api => main}/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java (100%) rename src/{api => main}/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java (100%) diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java b/src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java similarity index 100% rename from src/api/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java rename to src/main/java/com/minecolonies/api/colony/expeditions/ExpeditionStatus.java diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java b/src/main/java/com/minecolonies/api/colony/expeditions/IExpedition.java similarity index 100% rename from src/api/java/com/minecolonies/api/colony/expeditions/IExpedition.java rename to src/main/java/com/minecolonies/api/colony/expeditions/IExpedition.java diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java b/src/main/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java similarity index 100% rename from src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java rename to src/main/java/com/minecolonies/api/colony/expeditions/IExpeditionMember.java diff --git a/src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java b/src/main/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java similarity index 100% rename from src/api/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java rename to src/main/java/com/minecolonies/api/colony/expeditions/IExpeditionStage.java From 63e435e88ad00d28eee2844337fdba0c591c434c Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Sun, 4 Feb 2024 15:26:12 +0100 Subject: [PATCH 11/94] Fixes --- .../minecolonies/api/colony/IVisitorData.java | 9 ---- .../api/colony/IVisitorViewData.java | 9 ---- .../managers/interfaces/ICitizenManager.java | 24 +++++++--- .../managers/interfaces/IEntityManager.java | 20 --------- .../managers/interfaces/IVisitorManager.java | 21 +++++++++ .../visitor/AbstractVisitorExtraData.java | 11 ----- .../api/entity/visitor/IVisitorExtraData.java | 8 ---- .../api/entity/visitor/IVisitorType.java | 8 ++-- .../util/constant/ExpeditionConstants.java | 16 +++++++ .../translation/GuiTranslationConstants.java | 6 --- .../apiimp/CommonMinecoloniesAPIImpl.java | 1 - .../com/minecolonies/core/MineColonies.java | 1 + .../MainWindowExpeditionary.java | 18 ++++---- .../minecolonies/core/colony/CitizenData.java | 4 +- .../minecolonies/core/colony/VisitorData.java | 17 +++---- .../core/colony/VisitorDataView.java | 8 ---- .../modules/TavernBuildingModule.java | 17 +++---- .../colony/ColonyExpeditionEvent.java | 24 ++++++---- .../ExpeditionaryInteraction.java | 4 +- .../RecruitmentInteraction.java | 2 +- .../core/colony/managers/CitizenManager.java | 10 ++--- .../colony/managers/ReproductionManager.java | 3 +- .../core/colony/managers/VisitorManager.java | 42 ++++++------------ .../CommandCitizenSpawnNew.java | 2 +- .../visitor/ExpeditionaryVisitorType.java | 24 +++++----- .../entity/visitor/RegularVisitorType.java | 6 +-- .../minecolonies/core/event/EventHandler.java | 5 ++- .../core/items/ItemScrollGuardHelp.java | 2 +- .../gui/visitor/expeditionary/main.xml | 13 ++++++ .../minecolonies/lang/manual_en_us.json | 9 +++- .../visitor/expeditionary/sword_backdrop.png | Bin 0 -> 217 bytes .../expedition_types/end_regular.json | 42 ++++++++++++++++++ .../expedition_types/nether_regular.json | 42 ++++++++++++++++++ .../expedition_types/overworld_regular.json | 42 ++++++++++++++++++ 34 files changed, 288 insertions(+), 182 deletions(-) create mode 100644 src/main/java/com/minecolonies/api/util/constant/ExpeditionConstants.java create mode 100644 src/main/resources/assets/minecolonies/gui/visitor/expeditionary/main.xml create mode 100644 src/main/resources/assets/minecolonies/textures/gui/visitor/expeditionary/sword_backdrop.png create mode 100644 src/main/resources/data/minecolonies/expedition_types/end_regular.json create mode 100644 src/main/resources/data/minecolonies/expedition_types/nether_regular.json create mode 100644 src/main/resources/data/minecolonies/expedition_types/overworld_regular.json diff --git a/src/main/java/com/minecolonies/api/colony/IVisitorData.java b/src/main/java/com/minecolonies/api/colony/IVisitorData.java index e99b7ff362f..325c5c02e97 100644 --- a/src/main/java/com/minecolonies/api/colony/IVisitorData.java +++ b/src/main/java/com/minecolonies/api/colony/IVisitorData.java @@ -1,9 +1,7 @@ package com.minecolonies.api.colony; -import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; import com.minecolonies.api.entity.visitor.IVisitorExtraData; import com.minecolonies.api.entity.visitor.IVisitorType; -import net.minecraft.world.entity.EntityType; import org.jetbrains.annotations.NotNull; /** @@ -11,13 +9,6 @@ */ public interface IVisitorData extends ICitizenData { - /** - * Get the entity type for this visitor. - * - * @return the entity type. - */ - EntityType getEntityType(); - /** * Get the type of the visitor. * diff --git a/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java b/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java index c86aa3ce9f6..038b3ef2698 100644 --- a/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java +++ b/src/main/java/com/minecolonies/api/colony/IVisitorViewData.java @@ -1,9 +1,7 @@ package com.minecolonies.api.colony; -import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; import com.minecolonies.api.entity.visitor.IVisitorExtraData; import com.minecolonies.api.entity.visitor.IVisitorType; -import net.minecraft.world.entity.EntityType; import org.jetbrains.annotations.NotNull; /** @@ -11,13 +9,6 @@ */ public interface IVisitorViewData extends ICitizenDataView { - /** - * Get the entity type for this visitor. - * - * @return the entity type. - */ - EntityType getEntityType(); - /** * Get the type of the visitor. * 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 86dc2d762d3..88c4ed6eb32 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 @@ -15,6 +15,23 @@ */ 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 a5b34d312bd..3dd12aa0e9e 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; @@ -71,24 +69,6 @@ void sendPackets( */ 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(); - /** * Removes a civilian from the colony. * 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 a53b6f3c9e3..82c693eddf6 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,10 +1,31 @@ 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 { + /** + * 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 visitorType the visitor type. + * @return new IVisitorData + */ + IVisitorData createAndRegisterVisitorData(final IVisitorType visitorType); } diff --git a/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java b/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java index 5e26ed541b2..586adeed2fa 100644 --- a/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java +++ b/src/main/java/com/minecolonies/api/entity/visitor/AbstractVisitorExtraData.java @@ -7,16 +7,12 @@ public abstract class AbstractVisitorExtraData implements IVisitorExtraData extends INBTSerializable * @param value the new value. */ void setValue(S value); - - /** - * Get the default value in case no value was explicitly set yet. - * - * @return the value. - */ - @Nullable - S getDefaultValue(); } \ 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 index 9d4bc49b643..0e364f7577a 100644 --- a/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java +++ b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java @@ -3,12 +3,12 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.EntityType; 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; /** * Specific handler actions for interacting with different types of visitors. @@ -23,11 +23,11 @@ public interface IVisitorType ResourceLocation getId(); /** - * Get the entity type for this visitor type. + * Get the generator function for creating the entity for this given visitor. * - * @return the entity type. + * @return the entity creator. */ - EntityType getEntityType(); + Function getEntityCreator(); /** * Creates the state machine for this specific visitor. 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..bc8f549299b --- /dev/null +++ b/src/main/java/com/minecolonies/api/util/constant/ExpeditionConstants.java @@ -0,0 +1,16 @@ +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_INTERACTION_INQUIRY = "com.minecolonies.core.expedition.interaction"; + @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."; +} diff --git a/src/main/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java b/src/main/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java index 211dc67bba7..95d9f96be19 100644 --- a/src/main/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/translation/GuiTranslationConstants.java @@ -49,10 +49,4 @@ public class GuiTranslationConstants public static final String QUEST_LOG_COMPLETED_MULTIPLE_TEXT = "com.minecolonies.coremod.item.questlog.gui.completed_multiple"; @NonNls public static final String QUEST_LOG_TRACK_CITIZEN_TEXT = "com.minecolonies.coremod.item.questlog.gui.track_citizen"; - - // Expeditionary window - @NonNls - public static final String EXPEDITIONARY_DIFFICULTY = "com.minecolonies.gui.expedition.difficulty"; - @NonNls - public static final String EXPEDITIONARY_DIFFICULTY_PREFIX = "com.minecolonies.gui.expedition.difficulty."; } diff --git a/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java b/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java index 7e76ced9731..25e1a5e5819 100755 --- a/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java +++ b/src/main/java/com/minecolonies/apiimp/CommonMinecoloniesAPIImpl.java @@ -80,7 +80,6 @@ public class CommonMinecoloniesAPIImpl implements IMinecoloniesAPI private IForgeRegistry happinessFactorTypeRegistry; private IForgeRegistry happinessFunctionRegistry; private IForgeRegistry visitorTypeRegistry; - private IForgeRegistry visitorExtraDataRegistry; @Override @NotNull diff --git a/src/main/java/com/minecolonies/core/MineColonies.java b/src/main/java/com/minecolonies/core/MineColonies.java index ad00d4233e5..4c6fe61cf96 100755 --- a/src/main/java/com/minecolonies/core/MineColonies.java +++ b/src/main/java/com/minecolonies/core/MineColonies.java @@ -173,6 +173,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/visitor/expeditionary/MainWindowExpeditionary.java b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java index e7b10b95968..f733db81ecd 100644 --- a/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java +++ b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java @@ -16,6 +16,7 @@ import com.minecolonies.core.colony.expeditions.ExpeditionVisitorMember; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType.Difficulty; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; import com.minecolonies.core.network.messages.server.colony.visitor.expeditionary.StartExpeditionMessage; import net.minecraft.network.chat.Component; @@ -28,9 +29,9 @@ import java.util.Arrays; import java.util.List; +import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITIONARY_DIFFICULTY; +import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITIONARY_DIFFICULTY_PREFIX; import static com.minecolonies.api.util.constant.WindowConstants.EXPEDITIONARY_MAIN_RESOURCE_SUFFIX; -import static com.minecolonies.api.util.constant.translation.GuiTranslationConstants.EXPEDITIONARY_DIFFICULTY; -import static com.minecolonies.api.util.constant.translation.GuiTranslationConstants.EXPEDITIONARY_DIFFICULTY_PREFIX; import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION_TYPE; /** @@ -62,7 +63,12 @@ public MainWindowExpeditionary(final @NotNull IVisitorViewData visitorData) { super(Constants.MOD_ID + EXPEDITIONARY_MAIN_RESOURCE_SUFFIX); this.visitorData = visitorData; - this.expeditionType = visitorData.getExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE).orElseThrow(); + final ResourceLocation expeditionTypeId = visitorData.getExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE); + this.expeditionType = ColonyExpeditionTypeManager.getInstance().getExpeditionType(expeditionTypeId); + if (this.expeditionType == null) + { + throw new IllegalStateException(String.format("Expedition with id '%s' does not exist.", expeditionTypeId.toString())); + } findPaneOfTypeByID(LABEL_EXPEDITION_NAME, Text.class).setText(expeditionType.getName()); @@ -71,12 +77,6 @@ public MainWindowExpeditionary(final @NotNull IVisitorViewData visitorData) registerButton(LABEL_EXPEDITION_NAME, this::openVisitorInventory); } - @Override - public void onUpdate() - { - super.onUpdate(); - } - /** * Open visitor inventory for providing tools. */ diff --git a/src/main/java/com/minecolonies/core/colony/CitizenData.java b/src/main/java/com/minecolonies/core/colony/CitizenData.java index ef2f42c82f9..dd0d215b022 100755 --- a/src/main/java/com/minecolonies/core/colony/CitizenData.java +++ b/src/main/java/com/minecolonies/core/colony/CitizenData.java @@ -873,12 +873,12 @@ public void updateEntityIfNecessary() if (nextRespawnPos != null) { - colony.getCitizenManager().spawnOrCreateCivilian(this, colony.getWorld(), nextRespawnPos, true); + colony.getCitizenManager().spawnOrCreateCitizen(this, colony.getWorld(), nextRespawnPos, true); nextRespawnPos = null; } else { - colony.getCitizenManager().spawnOrCreateCivilian(this, colony.getWorld(), lastPosition, true); + colony.getCitizenManager().spawnOrCreateCitizen(this, colony.getWorld(), lastPosition, true); } } diff --git a/src/main/java/com/minecolonies/core/colony/VisitorData.java b/src/main/java/com/minecolonies/core/colony/VisitorData.java index b6ff85fa61e..12e17c845ef 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorData.java @@ -3,7 +3,6 @@ import com.minecolonies.api.IMinecoloniesAPI; import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.IVisitorData; -import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; import com.minecolonies.api.entity.visitor.IVisitorExtraData; import com.minecolonies.api.entity.visitor.IVisitorType; import com.minecolonies.api.util.BlockPosUtil; @@ -13,7 +12,6 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; @@ -79,12 +77,6 @@ public static IVisitorData loadVisitorFromNBT(final IColony colony, final Compou return data; } - @Override - public EntityType getEntityType() - { - return visitorType.getEntityType(); - } - @Override public @NotNull IVisitorType getVisitorType() { @@ -129,7 +121,7 @@ public void updateEntityIfNecessary() if (getLastPosition() != BlockPos.ZERO && (getLastPosition().getX() != 0 && getLastPosition().getZ() != 0) && WorldUtil.isEntityBlockLoaded(getColony().getWorld(), getLastPosition())) { - getColony().getVisitorManager().spawnOrCreateCivilian(this, getColony().getWorld(), getLastPosition(), true); + getColony().getVisitorManager().spawnOrCreateVisitor(visitorType, this, getColony().getWorld(), getLastPosition()); } else if (getHomeBuilding() != null) { @@ -138,7 +130,7 @@ else if (getHomeBuilding() != null) final BlockPos spawnPos = BlockPosUtil.findSpawnPosAround(getColony().getWorld(), getHomeBuilding().getID()); if (spawnPos != null) { - getColony().getVisitorManager().spawnOrCreateCivilian(this, getColony().getWorld(), spawnPos, true); + getColony().getVisitorManager().spawnOrCreateVisitor(visitorType, this, getColony().getWorld(), spawnPos); } } } @@ -170,11 +162,12 @@ public CompoundTag serializeNBT() public void deserializeNBT(final CompoundTag nbtTagCompound) { super.deserializeNBT(nbtTagCompound); + final CompoundTag extraDataCompound = nbtTagCompound.getCompound(TAG_EXTRA_DATA); for (final IVisitorExtraData extraDataKey : extraData) { - if (nbtTagCompound.contains(extraDataKey.getKey())) + if (extraDataCompound.contains(extraDataKey.getKey())) { - extraDataKey.deserializeNBT(nbtTagCompound.getCompound(extraDataKey.getKey())); + extraDataKey.deserializeNBT(extraDataCompound.getCompound(extraDataKey.getKey())); } } diff --git a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java index 155d16b983f..c4dccf2730a 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorDataView.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorDataView.java @@ -3,13 +3,11 @@ import com.minecolonies.api.IMinecoloniesAPI; import com.minecolonies.api.colony.IColonyView; import com.minecolonies.api.colony.IVisitorViewData; -import com.minecolonies.api.entity.visitor.AbstractEntityVisitor; 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.entity.EntityType; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -62,12 +60,6 @@ public void deserialize(@NotNull final FriendlyByteBuf buf) } } - @Override - public EntityType getEntityType() - { - return visitorType.getEntityType(); - } - @Override public @NotNull IVisitorType getVisitorType() { 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 f125beb034e..1d0957eaf47 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; @@ -175,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()); @@ -233,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); diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java index 81586f4f0bd..66ad3828e48 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionEvent.java @@ -40,8 +40,11 @@ public class ColonyExpeditionEvent extends AbstractExpeditionEvent /** * NBT tags. */ - private static final String TAG_EXPEDITION_TYPE = "expeditionType"; - private static final String TAG_REMAINING_ITEMS = "remainingItems"; + private static final String TAG_EXPEDITION_TYPE = "expeditionType"; + private static final String TAG_REMAINING_ITEMS = "remainingItems"; + private static final String TAG_END_TIME = "endTime"; + private static final String TAG_FLAG_MINIMUM_TIME_ELAPSED = "flagMinTimeElapsed"; + private static final String TAG_FLAG_REMAINING_ITEMS_EMPTY = "flagRemainingItemsEmpty"; /** * The size of the expedition inventory. @@ -53,11 +56,6 @@ public class ColonyExpeditionEvent extends AbstractExpeditionEvent */ private final ItemStackHandler inventory = new ItemStackHandler(EXPEDITION_INVENTORY_SIZE); - /** - * Random instance for calculating random values. - */ - private final Random random = new Random(); - /** * The expedition type for this colony expedition. */ @@ -80,7 +78,7 @@ public class ColonyExpeditionEvent extends AbstractExpeditionEvent private boolean isMinimumTimeElapsed = false; /** - * + * Whether the remaining items list was emptied out. */ private boolean isRemainingItemsEmpty = false; @@ -153,7 +151,7 @@ public void onUpdate() return; } - final int chance = random.nextInt(100); + final int chance = new Random().nextInt(100); if (chance <= 2) { getExpedition().setStatus(ExpeditionStatus.LOST); @@ -232,6 +230,10 @@ public CompoundTag serializeNBT() .map(IForgeItemStack::serializeNBT) .collect(NBTUtils.toListNBT())); } + + compound.putLong(TAG_END_TIME, endTime); + compound.putBoolean(TAG_FLAG_MINIMUM_TIME_ELAPSED, isMinimumTimeElapsed); + compound.putBoolean(TAG_FLAG_REMAINING_ITEMS_EMPTY, isRemainingItemsEmpty); return compound; } @@ -247,5 +249,9 @@ public void deserializeNBT(final CompoundTag compoundTag) .map(ItemStack::of) .collect(Collectors.toCollection(ArrayDeque::new)); } + + endTime = compoundTag.getLong(TAG_END_TIME); + isMinimumTimeElapsed = compoundTag.getBoolean(TAG_FLAG_MINIMUM_TIME_ELAPSED); + isRemainingItemsEmpty = compoundTag.getBoolean(TAG_FLAG_REMAINING_ITEMS_EMPTY); } } diff --git a/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java b/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java index 7078232e7d6..762103b0ae4 100644 --- a/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/ExpeditionaryInteraction.java @@ -37,9 +37,7 @@ public class ExpeditionaryInteraction extends ServerCitizenInteraction * @param inquiry the inquiry text. * @param priority the chat priority. */ - public ExpeditionaryInteraction( - final Component inquiry, - final IChatPriority priority) + public ExpeditionaryInteraction(final Component inquiry, final IChatPriority priority) { super(inquiry, true, priority, d -> true, null, viewAnswer, returnAnswer); } 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 a22a865192b..b29192c414e 100644 --- a/src/main/java/com/minecolonies/core/colony/interactionhandling/RecruitmentInteraction.java +++ b/src/main/java/com/minecolonies/core/colony/interactionhandling/RecruitmentInteraction.java @@ -169,7 +169,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("", ""); 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 1056dc96146..1c8b04edfa6 100755 --- a/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java @@ -232,7 +232,7 @@ public void sendPackets( } @Override - public ICitizenData spawnOrCreateCivilian(@Nullable final ICitizenData 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)) { @@ -280,7 +280,7 @@ private ICitizenData spawnCitizenOnPosition( ICitizenData citizenData = data; if (citizenData == null) { - citizenData = createAndRegisterCivilianData(); + citizenData = createAndRegisterCitizenData(); if (getMaxCitizens() >= getCurrentCitizenCount() && !force) { @@ -307,7 +307,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. @@ -586,7 +586,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)))); @@ -608,7 +608,7 @@ else if (femaleCount < (getCitizens().size() - 1) / 2.0) newCitizen.setGenderAndGenerateName(false); } - spawnOrCreateCivilian(newCitizen, colony.getWorld(), null, true); + spawnOrCreateCitizen(newCitizen, colony.getWorld(), null, true); MinecraftForge.EVENT_BUS.post(new CitizenAddedEvent(newCitizen, CitizenAddedEvent.Source.INITIAL)); 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 75165fbb7a3..8e4ab08b466 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/ReproductionManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/ReproductionManager.java @@ -14,7 +14,6 @@ import com.minecolonies.core.colony.buildings.modules.LivingBuildingModule; import com.minecolonies.core.colony.colonyEvents.citizenEvents.CitizenBornEvent; import com.minecolonies.core.util.AdvancementUtils; -import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import org.jetbrains.annotations.NotNull; @@ -116,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/VisitorManager.java b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java index 8aee45a24f4..542b585d46d 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java @@ -4,10 +4,11 @@ import com.minecolonies.api.colony.ICivilianData; import com.minecolonies.api.colony.IColony; import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.colony.interactionhandling.ChatPriority; 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.WorldUtil; @@ -15,13 +16,13 @@ import com.minecolonies.core.colony.VisitorData; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; -import com.minecolonies.core.entity.citizen.VisitorCitizen; -import com.minecolonies.core.entity.visitor.VisitorCitizen; +import com.minecolonies.core.colony.interactionhandling.ExpeditionaryInteraction; import com.minecolonies.core.network.messages.client.colony.ColonyVisitorViewDataMessage; 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.Entity; import net.minecraft.world.level.Level; @@ -30,6 +31,7 @@ import java.util.*; import static com.minecolonies.api.util.constant.Constants.SLIGHTLY_UP; +import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITION_INTERACTION_INQUIRY; import static com.minecolonies.api.util.constant.PathingConstants.HALF_A_BLOCK; import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION_TYPE; @@ -205,7 +207,7 @@ public IVisitorData getCivilian(final int citizenId) } @Override - public IVisitorData spawnOrCreateCivilian(IVisitorData 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)) { @@ -214,11 +216,10 @@ public IVisitorData spawnOrCreateCivilian(IVisitorData data, final Level world, 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 data; @@ -233,26 +234,7 @@ public IVisitorData spawnOrCreateCivilian(IVisitorData data, final Level world, } @Override - public IVisitorData createAndRegisterCivilianData() - { - return createAndRegisterVisitor(ModVisitorTypes.visitor.get()); - } - - public IVisitorData createAndRegisterExpeditionary(@NotNull final ColonyExpeditionType expeditionType) - { - final IVisitorData expeditionary = createAndRegisterVisitor(ModVisitorTypes.expeditionary.get()); - expeditionary.setExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE, Optional.of(expeditionType)); - - return expeditionary; - } - - /** - * Create visitor data for the given visitor type. - * - * @param visitorType the input visitor type. - * @return the generated visitor data. - */ - private IVisitorData createAndRegisterVisitor(final IVisitorType visitorType) + public IVisitorData createAndRegisterVisitorData(final IVisitorType visitorType) { markDirty(); final IVisitorData data = new VisitorData(nextVisitorID--, colony, visitorType); @@ -298,7 +280,11 @@ public void onColonyTick(final IColony colony) final ColonyExpeditionType expeditionType = ColonyExpeditionTypeManager.getInstance().getRandomExpeditionType(colony); if (expeditionType != null) { - createAndRegisterExpeditionary(expeditionType); + final IVisitorData newVisitor = createAndRegisterVisitorData(ModVisitorTypes.expeditionary.get()); + newVisitor.setExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE, expeditionType.getId()); + newVisitor.triggerInteraction(new ExpeditionaryInteraction(Component.translatable(EXPEDITION_INTERACTION_INQUIRY), ChatPriority.IMPORTANT)); + + spawnOrCreateVisitor(ModVisitorTypes.expeditionary.get(), newVisitor, 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 9b731091cdb..3e1e086eece 100755 --- a/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenSpawnNew.java +++ b/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenSpawnNew.java @@ -39,7 +39,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); MinecraftForge.EVENT_BUS.post(new CitizenAddedEvent(newCitizen, CitizenAddedEvent.Source.COMMANDS)); diff --git a/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java index bb539553ee5..7e074233a5c 100644 --- a/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java @@ -3,15 +3,14 @@ import com.minecolonies.api.entity.ModEntities; import com.minecolonies.api.entity.visitor.*; import com.minecolonies.core.colony.expeditions.Expedition; -import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; -import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; import com.minecolonies.core.entity.ai.visitor.EntityAIExpeditionary; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.Level; import java.util.List; import java.util.Optional; +import java.util.function.Function; /** * Visitor type for expeditionary visitors in the town hall. @@ -22,7 +21,7 @@ public class ExpeditionaryVisitorType implements IVisitorType * Extra data fields. */ public static final ColonyExpeditionTypeData EXTRA_DATA_EXPEDITION_TYPE = new ColonyExpeditionTypeData(); - public static final ExpeditionData EXTRA_DATA_EXPEDTIION = new ExpeditionData(); + public static final ExpeditionData EXTRA_DATA_EXPEDITION = new ExpeditionData(); @Override public ResourceLocation getId() @@ -31,9 +30,9 @@ public ResourceLocation getId() } @Override - public EntityType getEntityType() + public Function getEntityCreator() { - return ModEntities.EXPEDITIONARY; + return ModEntities.EXPEDITIONARY::create; } @Override @@ -45,36 +44,33 @@ public void createStateMachine(final AbstractEntityVisitor visitor) @Override public List> getExtraDataKeys() { - return List.of(EXTRA_DATA_EXPEDITION_TYPE, EXTRA_DATA_EXPEDTIION); + return List.of(EXTRA_DATA_EXPEDITION_TYPE, EXTRA_DATA_EXPEDITION); } /** * Extra data for storing the expedition type instance. */ - public static class ColonyExpeditionTypeData extends AbstractVisitorExtraData> + public static class ColonyExpeditionTypeData extends AbstractVisitorExtraData { private static final String TAG_VALUE = "value"; public ColonyExpeditionTypeData() { - super("expedition-type", Optional.empty()); + super("expedition-type", new ResourceLocation("")); } @Override public CompoundTag serializeNBT() { final CompoundTag compound = new CompoundTag(); - getValue().ifPresent(val -> compound.putString(TAG_VALUE, val.getId().toString())); + compound.putString(TAG_VALUE, getValue().toString()); return compound; } @Override public void deserializeNBT(final CompoundTag compoundTag) { - if (compoundTag.contains(TAG_VALUE)) - { - setValue(Optional.ofNullable(ColonyExpeditionTypeManager.getInstance().getExpeditionType(new ResourceLocation(compoundTag.getString(TAG_VALUE))))); - } + setValue(new ResourceLocation(compoundTag.getString(TAG_VALUE))); } } diff --git a/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java index 4d1cb72de45..6e64b0d3c84 100644 --- a/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java @@ -14,7 +14,6 @@ import net.minecraft.sounds.SoundEvents; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; @@ -23,6 +22,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.function.Function; import static com.minecolonies.api.util.ItemStackUtils.ISFOOD; import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INTERACTION_VISITOR_FOOD; @@ -46,9 +46,9 @@ public ResourceLocation getId() } @Override - public EntityType getEntityType() + public Function getEntityCreator() { - return ModEntities.VISITOR; + return ModEntities.VISITOR::create; } @Override diff --git a/src/main/java/com/minecolonies/core/event/EventHandler.java b/src/main/java/com/minecolonies/core/event/EventHandler.java index 7a5e884fdc8..3de1ba4df68 100755 --- a/src/main/java/com/minecolonies/core/event/EventHandler.java +++ b/src/main/java/com/minecolonies/core/event/EventHandler.java @@ -10,6 +10,7 @@ import com.minecolonies.api.entity.ModEntities; import com.minecolonies.api.entity.ai.statemachine.tickratestatemachine.TickRateStateMachine; import com.minecolonies.api.entity.citizen.AbstractEntityCitizen; +import com.minecolonies.api.entity.visitor.ModVisitorTypes; import com.minecolonies.api.items.ModTags; import com.minecolonies.api.util.*; import com.minecolonies.api.util.constant.Constants; @@ -722,7 +723,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); @@ -734,7 +735,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()) diff --git a/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java b/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java index 7b903fff964..2678a3582e7 100644 --- a/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java +++ b/src/main/java/com/minecolonies/core/items/ItemScrollGuardHelp.java @@ -109,7 +109,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); ((AbstractBuildingGuards) building).getSetting(AbstractBuildingGuards.GUARD_TASK).set(GuardTaskSetting.FOLLOW); diff --git a/src/main/resources/assets/minecolonies/gui/visitor/expeditionary/main.xml b/src/main/resources/assets/minecolonies/gui/visitor/expeditionary/main.xml new file mode 100644 index 00000000000..9272dd577b5 --- /dev/null +++ b/src/main/resources/assets/minecolonies/gui/visitor/expeditionary/main.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/assets/minecolonies/lang/manual_en_us.json b/src/main/resources/assets/minecolonies/lang/manual_en_us.json index cfa0e307874..2fcbb1c559d 100644 --- a/src/main/resources/assets/minecolonies/lang/manual_en_us.json +++ b/src/main/resources/assets/minecolonies/lang/manual_en_us.json @@ -2532,5 +2532,12 @@ "com.minecolonies.coremod.progress.colony_founded": "Word is out about a newly founded colony. Soon the first settlers will join your colony, if there is enough space around the Town Hall.", "com.minecolonies.coremod.progress.supplies_placed": "§6 Welcome to MineColonies. Now that you have your supplies, how about searching a nice location to place your Town Hall to create your colony?", "com.minecolonies.coremod.progress.childgrow": "%s grew up and wants a job now.", - "com.minecolonies.coremod.progress.newchild": "A new child, %s, is now happily living in %s!" + "com.minecolonies.coremod.progress.newchild": "A new child, %s, is now happily living in %s!", + + "com.minecolonies.core.expedition.interaction": "Hello! I am going on an expedition to %s, but I need some people and supplies. Could you help me out?", + "com.minecolonies.core.expedition.gui.difficulty": "Difficulty: %s", + "com.minecolonies.core.expedition.gui.difficulty.easy": "Easy", + "com.minecolonies.core.expedition.gui.difficulty.medium": "Medium", + "com.minecolonies.core.expedition.gui.difficulty.hard": "Hard", + "com.minecolonies.core.expedition.gui.difficulty.nightmare": "Nightmare" } diff --git a/src/main/resources/assets/minecolonies/textures/gui/visitor/expeditionary/sword_backdrop.png b/src/main/resources/assets/minecolonies/textures/gui/visitor/expeditionary/sword_backdrop.png new file mode 100644 index 0000000000000000000000000000000000000000..f5867f5f50de993576c4f5c8230d10c54fdd2a45 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}c0G|+7kXj85Ao*u?Z$FS?E(!7r{{Nrh`YB$ZS_aMnkH}&M2EM}}%y>M1 zMG8>R%hSa%L?bx1-%*G`frIJle|sxe&Ti++c@L)??MqGTp7C3Gk~||{XIke&p%oJq zS9!Xro_#9#NN!f!np3+P7jE71U|RmQi%)(R9AP{cz*xUfVbVgNsSKX3elF{r5}E+o C_e3lJ literal 0 HcmV?d00001 diff --git a/src/main/resources/data/minecolonies/expedition_types/end_regular.json b/src/main/resources/data/minecolonies/expedition_types/end_regular.json new file mode 100644 index 00000000000..2ec8214c4d7 --- /dev/null +++ b/src/main/resources/data/minecolonies/expedition_types/end_regular.json @@ -0,0 +1,42 @@ +{ + "name": "Expedition in the End", + "to-text": "the End", + "difficulty": "hard", + "dimension": "minecraft:the_end", + "loot-table": "minecolonies:expedition_end", + "requirements": [ + { + "type": "tool", + "tool": "weapon" + }, + { + "type": "tool", + "tool": "pickaxe" + }, + { + "type": "tool", + "tool": "shovel" + }, + { + "type": "tool", + "tool": "helmet" + }, + { + "type": "tool", + "tool": "chestplate" + }, + { + "type": "tool", + "tool": "leggings" + }, + { + "type": "tool", + "tool": "boots" + }, + { + "type": "food", + "amount": 32 + } + ], + "guards": 1 +} \ No newline at end of file diff --git a/src/main/resources/data/minecolonies/expedition_types/nether_regular.json b/src/main/resources/data/minecolonies/expedition_types/nether_regular.json new file mode 100644 index 00000000000..286de8523a8 --- /dev/null +++ b/src/main/resources/data/minecolonies/expedition_types/nether_regular.json @@ -0,0 +1,42 @@ +{ + "name": "Expedition in the Nether", + "to-text": "the Nether", + "difficulty": "medium", + "dimension": "minecraft:the_nether", + "loot-table": "minecolonies:expedition_nether", + "requirements": [ + { + "type": "tool", + "tool": "weapon" + }, + { + "type": "tool", + "tool": "pickaxe" + }, + { + "type": "tool", + "tool": "shovel" + }, + { + "type": "tool", + "tool": "helmet" + }, + { + "type": "tool", + "tool": "chestplate" + }, + { + "type": "tool", + "tool": "leggings" + }, + { + "type": "tool", + "tool": "boots" + }, + { + "type": "food", + "amount": 32 + } + ], + "guards": 1 +} \ No newline at end of file diff --git a/src/main/resources/data/minecolonies/expedition_types/overworld_regular.json b/src/main/resources/data/minecolonies/expedition_types/overworld_regular.json new file mode 100644 index 00000000000..cfc0aab0195 --- /dev/null +++ b/src/main/resources/data/minecolonies/expedition_types/overworld_regular.json @@ -0,0 +1,42 @@ +{ + "name": "Expedition in the Overworld", + "to-text": "the Overworld", + "difficulty": "easy", + "dimension": "minecraft:overworld", + "loot-table": "minecolonies:expedition_overworld", + "requirements": [ + { + "type": "tool", + "tool": "weapon" + }, + { + "type": "tool", + "tool": "pickaxe" + }, + { + "type": "tool", + "tool": "shovel" + }, + { + "type": "tool", + "tool": "helmet" + }, + { + "type": "tool", + "tool": "chestplate" + }, + { + "type": "tool", + "tool": "leggings" + }, + { + "type": "tool", + "tool": "boots" + }, + { + "type": "food", + "amount": 32 + } + ], + "guards": 1 +} \ No newline at end of file From ab04bb8f649803a884d1ed07f799e065e0109f78 Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Sun, 4 Feb 2024 15:26:52 +0100 Subject: [PATCH 12/94] Fix --- src/main/java/com/minecolonies/core/colony/VisitorData.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/minecolonies/core/colony/VisitorData.java b/src/main/java/com/minecolonies/core/colony/VisitorData.java index b5ef8e456c1..f0af511a3cf 100644 --- a/src/main/java/com/minecolonies/core/colony/VisitorData.java +++ b/src/main/java/com/minecolonies/core/colony/VisitorData.java @@ -164,11 +164,12 @@ public CompoundTag serializeNBT() public void deserializeNBT(final CompoundTag nbtTagCompound) { super.deserializeNBT(nbtTagCompound); + final CompoundTag extraDataCompound = nbtTagCompound.getCompound(TAG_EXTRA_DATA); for (final IVisitorExtraData extraDataKey : extraData) { - if (nbtTagCompound.contains(extraDataKey.getKey())) + if (extraDataCompound.contains(extraDataKey.getKey())) { - extraDataKey.deserializeNBT(nbtTagCompound.getCompound(extraDataKey.getKey())); + extraDataKey.deserializeNBT(extraDataCompound.getCompound(extraDataKey.getKey())); } } From 90771e431fa1647d7e28d32ed80d9e604452da52 Mon Sep 17 00:00:00 2001 From: Thom van den Akker Date: Wed, 14 Feb 2024 14:52:28 +0100 Subject: [PATCH 13/94] Temp --- .../minecolonies/api/colony/jobs/IJob.java | 12 +- .../api/colony/jobs/IJobView.java | 10 + .../api/entity/visitor/IVisitorType.java | 7 +- .../minecolonies/api/util/BlockPosUtil.java | 49 ++++ .../minecolonies/api/util/InventoryUtils.java | 26 +- .../core/client/gui/WindowHireWorker.java | 2 +- .../core/client/gui/WindowResourceList.java | 16 +- .../client/gui/blockui/RotatingItemIcon.java | 7 +- .../gui/citizen/CitizenWindowUtils.java | 5 +- .../core/client/gui/generic/ResourceItem.java | 253 ++++++++++++++++++ .../modules/WarehouseOptionsModuleWindow.java | 180 ++++++------- .../gui/modules/WindowBuilderResModule.java | 94 +------ .../MainWindowExpeditionary.java | 133 +++++++-- .../modules/BuildingResourcesModule.java | 9 +- .../utils/BuildingBuilderResource.java | 117 ++------ .../core/colony/expeditions/Expedition.java | 19 +- .../colony/expeditions/ExpeditionBuilder.java | 219 +++++++++++++++ .../expeditions/ExpeditionCitizenMember.java | 23 ++ .../expeditions/ExpeditionVisitorMember.java | 23 ++ .../colony/ColonyExpeditionType.java | 37 ++- .../ColonyExpeditionFoodRequirement.java | 80 +++++- .../ColonyExpeditionItemRequirement.java | 70 ++++- .../ColonyExpeditionRequirement.java | 107 ++++++++ .../ColonyExpeditionToolRequirement.java | 81 +++++- .../IColonyExpeditionRequirement.java | 17 -- .../core/colony/jobs/AbstractJob.java | 2 + .../core/colony/jobs/AbstractJobGuard.java | 6 + .../core/colony/jobs/JobDruid.java | 6 + .../colony/jobs/views/DefaultJobView.java | 24 ++ .../core/colony/managers/VisitorManager.java | 31 ++- .../ai/visitor/EntityAIExpeditionary.java | 19 +- .../visitor/ExpeditionaryVisitorType.java | 41 +-- .../entity/visitor/RegularVisitorType.java | 10 +- .../core/network/NetworkChannel.java | 2 + .../expeditionary/TransferItemsMessage.java | 94 +++++++ .../minecolonies/gui/generic/resource.xml | 11 + .../gui/layouthuts/layoutbuilderres.xml | 15 +- .../gui/layouthuts/layoutwarehouseoptions.xml | 12 +- .../gui/visitor/expeditionary/main.xml | 9 +- .../minecolonies/lang/manual_en_us.json | 8 +- 40 files changed, 1453 insertions(+), 433 deletions(-) create mode 100644 src/main/java/com/minecolonies/core/client/gui/generic/ResourceItem.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionBuilder.java create mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionRequirement.java delete mode 100644 src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java create mode 100644 src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/TransferItemsMessage.java create mode 100644 src/main/resources/assets/minecolonies/gui/generic/resource.xml 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 5c6741bd534..ba6724f2dfc 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/entity/visitor/IVisitorType.java b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java index 0e364f7577a..f0f130afeac 100644 --- a/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java +++ b/src/main/java/com/minecolonies/api/entity/visitor/IVisitorType.java @@ -10,6 +10,8 @@ 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. */ @@ -57,7 +59,10 @@ default List> getExtraDataKeys() @NotNull default InteractionResult onPlayerInteraction(final AbstractEntityVisitor visitor, final Player player, final Level level, final InteractionHand hand) { - // Nothing should happen by default. + // 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; } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/api/util/BlockPosUtil.java b/src/main/java/com/minecolonies/api/util/BlockPosUtil.java index e7b33347d65..26d6815ab58 100755 --- a/src/main/java/com/minecolonies/api/util/BlockPosUtil.java +++ b/src/main/java/com/minecolonies/api/util/BlockPosUtil.java @@ -1058,4 +1058,53 @@ public static boolean isInArea(final BlockPos cornerA, final BlockPos cornerB, f && location.getY() >= y1 - 1 && location.getY() <= y2 + 1 && location.getZ() >= z1 - 1 && location.getZ() <= z2 + 1; } + + /** + * Find the first viable empty position around a given block, given a max search radius. + * Searches horizontally first. In case no free position can be found at all within the entire area + * + * @param world the level. + * @param pos the input position. + * @param distance the maximum distance to look around the input position (all axis). + * @param minHeight the height that should be empty in order for the location to be valid. + * @return the output position. + */ + public static BlockPos getSurroundingEmptyBlock(final Level world, final BlockPos pos, final int distance, final int minHeight) + { + if (world.getBlockState(pos).isAir()) + { + return pos; + } + + for (int y = 0; y < distance; y++) + { + for (int x = -distance; x < distance; x++) + { + for (int z = -distance; z < distance; z++) + { + final BlockPos currentPos = pos.offset(x, y, z); + if (!world.getBlockState(currentPos).isAir()) + { + continue; + } + + boolean isFullyFree = true; + for (int height = 1; height < minHeight; height++) + { + if (!world.getBlockState(currentPos.above(height)).isAir()) + { + isFullyFree = false; + } + } + + if (isFullyFree) + { + return currentPos; + } + } + } + } + + return pos; + } } diff --git a/src/main/java/com/minecolonies/api/util/InventoryUtils.java b/src/main/java/com/minecolonies/api/util/InventoryUtils.java index 7721c99c595..5f110132165 100755 --- a/src/main/java/com/minecolonies/api/util/InventoryUtils.java +++ b/src/main/java/com/minecolonies/api/util/InventoryUtils.java @@ -2853,9 +2853,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; } @@ -2864,7 +2884,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/core/client/gui/WindowHireWorker.java b/src/main/java/com/minecolonies/core/client/gui/WindowHireWorker.java index fb604799996..596d0b2ee09 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/WindowResourceList.java b/src/main/java/com/minecolonies/core/client/gui/WindowResourceList.java index 774d92cf152..8a0aadcb8bf 100755 --- a/src/main/java/com/minecolonies/core/client/gui/WindowResourceList.java +++ b/src/main/java/com/minecolonies/core/client/gui/WindowResourceList.java @@ -14,6 +14,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; @@ -30,8 +31,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. @@ -113,7 +113,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(); } @@ -125,7 +125,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()); } /** @@ -242,8 +242,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))); @@ -253,9 +253,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 98b9b766648..c8b87ca2735 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 @@ -17,6 +17,7 @@ import com.minecolonies.core.colony.buildings.moduleviews.WorkerBuildingModuleView; import com.minecolonies.core.colony.buildings.views.AbstractBuildingView; import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Tuple; import org.jetbrains.annotations.NotNull; @@ -28,13 +29,11 @@ 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; -import net.minecraft.network.chat.Component; - /** * BOWindow for the citizen. */ 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..927d493327e --- /dev/null +++ b/src/main/java/com/minecolonies/core/client/gui/generic/ResourceItem.java @@ -0,0 +1,253 @@ +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.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; + +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 item. + * + * @param index the item index. + * @param rowPane the parenting pane. + */ + public static void updateResourcePane(final T resource, 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); + + 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(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) + { + if (resource1.getAvailabilityStatus() == resource2.getAvailabilityStatus()) + { + return resource1.getName().toString().compareTo(resource2.getName().toString()); + } + + return resource1.getAvailabilityStatus().getOrder().compareTo(resource2.getAvailabilityStatus().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..87c5eb3ab2e 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, 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 1042dd4ae95..7a9d6515304 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,9 +1,7 @@ package com.minecolonies.core.client.gui.modules; -import com.ldtteam.blockui.Color; import com.ldtteam.blockui.Pane; 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; @@ -12,6 +10,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; @@ -34,14 +34,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. */ @@ -101,7 +93,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(); } @@ -111,7 +103,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 @@ -132,7 +124,7 @@ public int getElementCount() @Override public void updateElement(final int index, @NotNull final Pane rowPane) { - updateResourcePane(index, rowPane); + ResourceItem.updateResourcePane(resources.get(index), index, rowPane); } }); @@ -143,74 +135,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() { @@ -250,10 +174,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/visitor/expeditionary/MainWindowExpeditionary.java b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java index f733db81ecd..85c83b5a96d 100644 --- a/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java +++ b/src/main/java/com/minecolonies/core/client/gui/visitor/expeditionary/MainWindowExpeditionary.java @@ -1,37 +1,47 @@ package com.minecolonies.core.client.gui.visitor.expeditionary; +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.Text; +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.IVisitorViewData; import com.minecolonies.api.colony.expeditions.ExpeditionStatus; -import com.minecolonies.api.colony.expeditions.IExpedition; -import com.minecolonies.api.colony.expeditions.IExpeditionMember; 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.ResourceComparator; import com.minecolonies.core.colony.expeditions.Expedition; -import com.minecolonies.core.colony.expeditions.ExpeditionVisitorMember; +import com.minecolonies.core.colony.expeditions.ExpeditionBuilder; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType.Difficulty; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement.RequirementHandler; import com.minecolonies.core.network.messages.server.colony.OpenInventoryMessage; import com.minecolonies.core.network.messages.server.colony.visitor.expeditionary.StartExpeditionMessage; +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.entity.EquipmentSlot; import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.wrapper.InvWrapper; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITIONARY_DIFFICULTY; import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITIONARY_DIFFICULTY_PREFIX; -import static com.minecolonies.api.util.constant.WindowConstants.EXPEDITIONARY_MAIN_RESOURCE_SUFFIX; +import static com.minecolonies.api.util.constant.WindowConstants.*; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION; import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION_TYPE; /** @@ -42,8 +52,9 @@ public class MainWindowExpeditionary extends AbstractWindowSkeleton /** * Window constants. */ - private static final String LABEL_EXPEDITION_NAME = "expedition_name"; - private static final String VIEW_EXPEDITION_DIFFICULTY = "expedition_difficulty"; + private static final String ID_EXPEDITION_NAME = "expedition_name"; + private static final String ID_EXPEDITION_ITEMS = "expedition_items"; + private static final String ID_EXPEDITION_DIFFICULTY = "expedition_difficulty"; /** * The visitor data. @@ -56,6 +67,16 @@ public class MainWindowExpeditionary extends AbstractWindowSkeleton */ private final ColonyExpeditionType expeditionType; + /** + * The builder instance for the expedition. + */ + private final ExpeditionBuilder expeditionBuilder; + + /** + * The requirements for this expedition type. + */ + private final List requirements; + /** * Default constructor. */ @@ -63,18 +84,20 @@ public MainWindowExpeditionary(final @NotNull IVisitorViewData visitorData) { super(Constants.MOD_ID + EXPEDITIONARY_MAIN_RESOURCE_SUFFIX); this.visitorData = visitorData; + final ResourceLocation expeditionTypeId = visitorData.getExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE); - this.expeditionType = ColonyExpeditionTypeManager.getInstance().getExpeditionType(expeditionTypeId); - if (this.expeditionType == null) + expeditionType = ColonyExpeditionTypeManager.getInstance().getExpeditionType(expeditionTypeId); + if (expeditionType == null) { throw new IllegalStateException(String.format("Expedition with id '%s' does not exist.", expeditionTypeId.toString())); } - findPaneOfTypeByID(LABEL_EXPEDITION_NAME, Text.class).setText(expeditionType.getName()); + expeditionBuilder = visitorData.getExtraDataValue(EXTRA_DATA_EXPEDITION); - updateDifficulty(); + requirements = expeditionType.getRequirements().stream().map(m -> m.createHandler(visitorData::getInventory)).collect(Collectors.toList()); + requirements.sort(new ResourceComparator()); - registerButton(LABEL_EXPEDITION_NAME, this::openVisitorInventory); + registerButton(RESOURCE_ADD, this::transferItems); } /** @@ -86,9 +109,9 @@ private void openVisitorInventory() } /** - * Update the difficulty icons. + * Render the difficulty icons. */ - private void updateDifficulty() + private void renderDifficulty() { final int maxDifficulty = Arrays.stream(Difficulty.values()) .filter(m -> m.equals(expeditionType.getDifficulty()) || !m.isHidden()) @@ -109,13 +132,86 @@ private void updateDifficulty() iconPane.setImage(new ResourceLocation("textures/item/" + currentDifficulty.getIcon().toString() + ".png"), false); } + final List allGuards = visitorData.getColony().getCitizens().values().stream() + .filter(f -> f.getJobView() != null && f.getJobView().isGuard() && f.getJobView().isCombatGuard()) + .toList(); + PaneBuilders.tooltipBuilder() .append(Component.translatable(EXPEDITIONARY_DIFFICULTY, Component.translatable(EXPEDITIONARY_DIFFICULTY_PREFIX + currentDifficulty.getKey())) .withStyle(currentDifficulty.getStyle())) - .hoverPane(findPaneOfTypeByID(VIEW_EXPEDITION_DIFFICULTY, View.class)) + .hoverPane(findPaneOfTypeByID(ID_EXPEDITION_DIFFICULTY, View.class)) .build(); } + @Override + public void onOpened() + { + super.onOpened(); + + renderDifficulty(); + renderItemsList(); + + findPaneOfTypeByID(ID_EXPEDITION_NAME, Text.class).setText(expeditionType.getName()); + + registerButton(ID_EXPEDITION_NAME, this::openVisitorInventory); + } + + @Override + public void onUpdate() + { + super.onUpdate(); + requirements.sort(new ResourceComparator()); + } + + /** + * Render the item requirement list. + */ + private void renderItemsList() + { + final ScrollingList itemsList = findPaneOfTypeByID(ID_EXPEDITION_ITEMS, ScrollingList.class); + 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), index, rowPane); + } + }); + } + + /** + * 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("WindowHutBuilder.transferItems: Error - Could not find the resource."); + return; + } + + Network.getNetwork().sendToServer(new TransferItemsMessage(visitorData, expeditionType, requirement.getId())); + + final int needed = requirement.getAmount() - requirement.getAmountAvailable(); + InventoryUtils.transferItemStackIntoNextFreeSlotFromItemHandler(new InvWrapper(mc.player.getInventory()), + requirement.getItemPredicate(), + needed, + visitorData.getInventory()); + } + /** * Triggers starting the expedition. */ @@ -131,12 +227,9 @@ private void startExpedition() equipment.add(armorItem); } } + expeditionBuilder.addEquipment(equipment); - final List members = new ArrayList<>(); - members.add(new ExpeditionVisitorMember(visitorData)); - // TODO: Iterate assigned guards - - final IExpedition expedition = new Expedition(expeditionType.getDimension(), equipment, members); + final Expedition expedition = expeditionBuilder.build(); expedition.setStatus(ExpeditionStatus.EMBARKED); Network.getNetwork().sendToServer(new StartExpeditionMessage(visitorData.getColony(), expedition)); } 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 690e12f7d76..b84da6e8c98 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 @@ -19,11 +19,11 @@ import com.minecolonies.core.colony.buildings.utils.BuildingBuilderResource; import com.minecolonies.core.colony.jobs.AbstractJobStructure; import com.minecolonies.core.entity.ai.workers.util.BuildingStructureHandler; - -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,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. @@ -82,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/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/expeditions/Expedition.java b/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java index c3ad3ccd989..98b52b97b73 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/Expedition.java @@ -27,9 +27,11 @@ public final class Expedition 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"; + static final String TAG_EQUIPMENT = "equipment"; + static final String TAG_EQUIPMENT_ITEM = "equipmentItem"; + static final String TAG_EQUIPMENT_COUNT = "equipmentCount"; + static final String TAG_MEMBERS = "members"; + static final String TAG_MEMBER_TYPE = "memberType"; /** * The dimension to send the expedition to. @@ -75,6 +77,7 @@ public Expedition(final @NotNull ResourceKey dimensionId, final @NotNull * @param compound the compound data. * @return the expedition instance. */ + @NotNull public static Expedition loadFromNBT(final CompoundTag compound) { return Serializer.read(compound); @@ -148,6 +151,11 @@ public void memberLost(final IExpeditionMember member) member.died(); } + /** + * Write this expedition builder to compound data. + * + * @param compound the compound tag. + */ @Override public void write(final CompoundTag compound) { @@ -165,6 +173,7 @@ private static class Serializer * @param compound the NBT data. * @return the expedition instance. */ + @NotNull public static Expedition read(final CompoundTag compound) { final ResourceKey dimensionId = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(compound.getString(TAG_DIMENSION))); @@ -203,10 +212,10 @@ else if (Objects.equals(memberType, "visitor")) */ public static void write(final Expedition expedition, final CompoundTag compound) { - compound.putString(TAG_DIMENSION, expedition.getTargetDimension().location().toString()); + compound.putString(TAG_DIMENSION, expedition.dimensionId.location().toString()); final ListTag memberTag = new ListTag(); - for (final IExpeditionMember member : expedition.getMembers()) + for (final IExpeditionMember member : expedition.members) { final CompoundTag memberCompound = new CompoundTag(); if (member instanceof ExpeditionCitizenMember) diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionBuilder.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionBuilder.java new file mode 100644 index 00000000000..ad711f9e6da --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionBuilder.java @@ -0,0 +1,219 @@ +package com.minecolonies.core.colony.expeditions; + +import com.minecolonies.api.colony.expeditions.IExpeditionMember; +import net.minecraft.core.registries.Registries; +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.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.Map.Entry; + +import static com.minecolonies.api.util.constant.NbtTagConstants.TAG_DIMENSION; +import static com.minecolonies.core.colony.expeditions.Expedition.*; + +/** + * Builder instance for {@link Expedition} instances. + */ +public class ExpeditionBuilder +{ + /** + * The equipment given to the expedition prior to starting. + */ + @NotNull + private final Map equipment; + + /** + * The members of the expedition. + */ + @NotNull + private final List members; + + /** + * The dimension to send the expedition to. + */ + @NotNull + private ResourceKey dimensionId = Level.OVERWORLD; + + /** + * Default constructor. + */ + public ExpeditionBuilder() + { + this.equipment = new HashMap<>(); + this.members = new ArrayList<>(); + } + + /** + * Create an expedition builder instance from compound data. + * + * @param compound the compound data. + * @return the expedition builder instance. + */ + @NotNull + public static ExpeditionBuilder loadFromNBT(final CompoundTag compound) + { + return Serializer.read(compound); + } + + /** + * Set the target dimension for this expedition. + * + * @param dimensionId the dimension key. + */ + public void inDimension(final ResourceKey dimensionId) + { + this.dimensionId = dimensionId; + } + + /** + * Adds equipment for this expedition to use. + * + * @param stacks the items. + */ + public void addEquipment(final List stacks) + { + for (final ItemStack stack : stacks) + { + final ItemStack key = stack.copyWithCount(1); + this.equipment.putIfAbsent(key, 0); + this.equipment.put(key, this.equipment.get(key) + stack.getCount()); + } + } + + /** + * Adds a member to the list of members. + * + * @param members the new members. + */ + public void addMember(final List members) + { + this.members.addAll(members); + } + + /** + * Removes a member from the list of members. + * + * @param member the member to remove. + */ + public void removeMember(final IExpeditionMember member) + { + this.members.remove(member); + } + + /** + * Construct the final expedition instance. + * + * @return the expedition instance. + */ + public Expedition build() + { + final List fullEquipment = equipment.entrySet().stream() + .map(entry -> entry.getKey().copyWithCount(entry.getValue())) + .toList(); + return new Expedition(dimensionId, fullEquipment, members); + } + + /** + * Write this expedition builder to compound data. + * + * @param compound the compound tag. + */ + public void write(final CompoundTag compound) + { + Serializer.write(this, compound); + } + + /** + * Serializer for the expeditions. + */ + private static class Serializer + { + /** + * Read a new expedition from NBT. + * + * @param compound the NBT data. + * @return the expedition instance. + */ + @NotNull + public static ExpeditionBuilder read(final CompoundTag compound) + { + final ResourceKey dimensionId = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(compound.getString(TAG_DIMENSION))); + + final List members = new ArrayList<>(); + final ListTag membersList = compound.getList(TAG_MEMBERS, 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)); + } + } + + final Map equipment = new HashMap<>(); + final ListTag equipmentList = compound.getList(TAG_EQUIPMENT, Tag.TAG_COMPOUND); + for (int i = 0; i < equipmentList.size(); ++i) + { + final CompoundTag itemCompound = equipmentList.getCompound(i); + final ItemStack item = ItemStack.of(itemCompound.getCompound(TAG_EQUIPMENT_ITEM)).copyWithCount(1); + final int count = itemCompound.getInt(TAG_EQUIPMENT_COUNT); + equipment.put(item, count); + } + + final ExpeditionBuilder builder = new ExpeditionBuilder(); + builder.dimensionId = dimensionId; + builder.equipment.putAll(equipment); + builder.members.addAll(members); + return builder; + } + + /** + * Write an expedition to NBT data. + * + * @param expeditionBuilder the expedition builder instance. + * @param compound the NBT to write the expedition to. + */ + public static void write(final ExpeditionBuilder expeditionBuilder, final CompoundTag compound) + { + compound.putString(TAG_DIMENSION, expeditionBuilder.dimensionId.location().toString()); + + final ListTag memberTag = new ListTag(); + for (final IExpeditionMember member : expeditionBuilder.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); + memberTag.add(memberCompound); + } + compound.put(TAG_MEMBERS, memberTag); + + final ListTag equipmentTag = new ListTag(); + for (final Entry equipmentEntry : expeditionBuilder.equipment.entrySet()) + { + final CompoundTag itemCompound = new CompoundTag(); + itemCompound.put(TAG_EQUIPMENT_ITEM, equipmentEntry.getKey().serializeNBT()); + itemCompound.putInt(TAG_EQUIPMENT_COUNT, equipmentEntry.getValue()); + equipmentTag.add(itemCompound); + } + compound.put(TAG_EQUIPMENT, equipmentTag); + } + } +} diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java index 6cf8be98969..182531de321 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionCitizenMember.java @@ -94,4 +94,27 @@ public void write(final CompoundTag compound) compound.putString(TAG_NAME, this.name); compound.putBoolean(TAG_DIED, this.died); } + + @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; + } + + @Override + public int hashCode() + { + return id; + } } \ 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 index c175ffa2fd0..2e2c5f2af44 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/ExpeditionVisitorMember.java @@ -94,4 +94,27 @@ public void write(final CompoundTag compound) compound.putString(TAG_NAME, this.name); compound.putBoolean(TAG_DIED, this.died); } + + @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; + } + + @Override + public int hashCode() + { + return id; + } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java index 899b00e3651..fdd95f6b287 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/ColonyExpeditionType.java @@ -8,8 +8,8 @@ import com.minecolonies.api.util.constant.ToolType; 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 com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionToolRequirement; -import com.minecolonies.core.colony.expeditions.colony.requirements.IColonyExpeditionRequirement; import net.minecraft.ChatFormatting; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; @@ -23,10 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -89,7 +86,7 @@ public class ColonyExpeditionType /** * The list of requirements for this expedition type to be sent. */ - private final List requirements; + private final List requirements; /** * The minimum amount of guards needed for this expedition. @@ -106,7 +103,7 @@ public ColonyExpeditionType( final @NotNull Difficulty difficulty, final ResourceKey dimension, final ResourceLocation lootTable, - final List requirements, + final List requirements, final int guards) { this.id = id; @@ -136,7 +133,7 @@ public static ColonyExpeditionType parse(final ResourceLocation id, final JsonOb 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 List requirements = new ArrayList<>(); + final Set requirements = new HashSet<>(); if (object.has(PROP_REQUIREMENTS) && object.get(PROP_REQUIREMENTS).isJsonArray()) { final JsonArray jsonRequirements = object.getAsJsonArray(PROP_REQUIREMENTS); @@ -147,7 +144,11 @@ public static ColonyExpeditionType parse(final ResourceLocation id, final JsonOb continue; } - requirements.add(parseRequirement(jsonRequirement.getAsJsonObject())); + final ColonyExpeditionRequirement requirement = parseRequirement(jsonRequirement.getAsJsonObject()); + if (requirement != null) + { + requirements.add(requirement); + } } } @@ -159,7 +160,7 @@ public static ColonyExpeditionType parse(final ResourceLocation id, final JsonOb Arrays.stream(Difficulty.values()).map(m -> m.key).collect(Collectors.joining(", ")))); } - return new ColonyExpeditionType(id, name, toText, difficulty, dimension, lootTable, requirements, guards); + return new ColonyExpeditionType(id, name, toText, difficulty, dimension, lootTable, requirements.stream().toList(), guards); } /** @@ -169,9 +170,9 @@ public static ColonyExpeditionType parse(final ResourceLocation id, final JsonOb * @return a requirement instance or null. */ @Nullable - private static IColonyExpeditionRequirement parseRequirement(final JsonObject requirement) + private static ColonyExpeditionRequirement parseRequirement(final JsonObject requirement) { - final int amount = requirement.has(PROP_REQUIREMENT_AMOUNT) ? requirement.getAsJsonPrimitive(PROP_REQUIREMENT_AMOUNT).getAsInt() : 1; + 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_TOOL -> @@ -224,7 +225,7 @@ public Difficulty getDifficulty() * * @return the component. */ - public Object getToText() + public Component getToText() { return toText; } @@ -249,6 +250,16 @@ public ResourceLocation getLootTable() return lootTable; } + /** + * Get the list of requirements for this expedition type to be sent. + * + * @return the unmodifiable list. + */ + public List getRequirements() + { + return requirements; + } + /** * The expedition difficulty. */ 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 index 9cc8beed82a..40959e19030 100644 --- 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 @@ -1,19 +1,31 @@ package com.minecolonies.core.colony.expeditions.colony.requirements; -import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.colony.IColonyManager; import com.minecolonies.api.util.ItemStackUtils; -import net.minecraftforge.items.IItemHandler; +import com.minecolonies.api.util.constant.Constants; +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.List; +import java.util.function.Predicate; /** * Colony expedition requirements for providing any kind of food, with a minimum amount. */ -public class ColonyExpeditionFoodRequirement implements IColonyExpeditionRequirement +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. * @@ -25,8 +37,66 @@ public ColonyExpeditionFoodRequirement(final int amount) } @Override - public boolean isFulFilled(final IItemHandler itemHandler) + @NotNull + public ResourceLocation getId() + { + return new ResourceLocation(Constants.MOD_ID, "food"); + } + + @Override + public RequirementHandler createHandler(final InventorySupplier inventory) { - return InventoryUtils.getItemCountInItemHandler(itemHandler, ItemStackUtils.ISFOOD) >= this.amount; + return new FoodRequirementHandler(inventory); + } + + /** + * Food handler instance used for verifying if the given item handler contains any food items. + */ + public class FoodRequirementHandler extends RequirementHandler + { + /** + * Default constructor. + * + * @param inventory supplier for obtaining the inventory. + */ + private FoodRequirementHandler(final InventorySupplier inventory) + { + super(inventory); + } + + @Override + public ResourceLocation getId() + { + return ColonyExpeditionFoodRequirement.this.getId(); + } + + @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; + } + + @Override + public Predicate getItemPredicate() + { + return ItemStackUtils.ISFOOD; + } } } \ 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 index 945a49b9963..f78f0a0b445 100644 --- 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 @@ -1,13 +1,20 @@ package com.minecolonies.core.colony.expeditions.colony.requirements; -import com.minecolonies.api.util.InventoryUtils; +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.minecraftforge.items.IItemHandler; +import net.minecraft.world.item.ItemStack; +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 implements IColonyExpeditionRequirement +public class ColonyExpeditionItemRequirement extends ColonyExpeditionRequirement { /** * The item to request. @@ -32,8 +39,61 @@ public ColonyExpeditionItemRequirement(final Item item, final int amount) } @Override - public boolean isFulFilled(final IItemHandler itemHandler) + @NotNull + public ResourceLocation getId() + { + return new ResourceLocation(Constants.MOD_ID, "item/" + item.getDescriptionId()); + } + + @Override + public RequirementHandler createHandler(final InventorySupplier inventory) { - return InventoryUtils.getItemCountInItemHandler(itemHandler, this.item) >= this.amount; + return new ItemRequirementHandler(inventory); + } + + /** + * Item handler instance used for verifying if the given item handler contains the exact item. + */ + public class ItemRequirementHandler extends RequirementHandler + { + /** + * Default constructor. + * + * @param inventory supplier for obtaining the inventory. + */ + private ItemRequirementHandler(final InventorySupplier inventory) + { + super(inventory); + } + + @Override + public ResourceLocation getId() + { + return ColonyExpeditionItemRequirement.this.getId(); + } + + @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; + } + + @Override + public Predicate getItemPredicate() + { + return stack -> ItemStackUtils.compareItemStacksIgnoreStackSize(stack, item.getDefaultInstance()); + } } } \ 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..6f4c6c89df5 --- /dev/null +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionRequirement.java @@ -0,0 +1,107 @@ +package com.minecolonies.core.colony.expeditions.colony.requirements; + +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.core.client.gui.generic.ResourceItem.Resource; +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.Predicate; +import java.util.function.Supplier; + +/** + * Interface for defining different types of colony expedition requirements. + */ +public abstract class ColonyExpeditionRequirement +{ + /** + * Get a unique ID for this requirement. + * + * @return the resource location. + */ + @NotNull + public abstract ResourceLocation getId(); + + /** + * 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 InventorySupplier inventory); + + /** + * Supplier for providing the reference to the inventory to check this requirement for. + */ + @FunctionalInterface + public interface InventorySupplier extends Supplier + { + + } + + /** + * Handler instance used for verifying if the given item handler satisfies the requirements. + */ + public abstract static class RequirementHandler implements Resource + { + /** + * Supplier for obtaining the inventory. + */ + private final InventorySupplier inventory; + + /** + * Default constructor. + * + * @param inventory supplier for obtaining the inventory. + */ + protected RequirementHandler(final InventorySupplier inventory) + { + this.inventory = inventory; + } + + /** + * 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(); + + @Override + public final int getAmountAvailable() + { + return InventoryUtils.getItemCountInItemHandler(inventory.get(), 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()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java index d5ae5d096e6..910fbbe790e 100644 --- a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java +++ b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/ColonyExpeditionToolRequirement.java @@ -1,14 +1,21 @@ package com.minecolonies.core.colony.expeditions.colony.requirements; -import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.colony.IColonyManager; import com.minecolonies.api.util.ItemStackUtils; +import com.minecolonies.api.util.constant.Constants; import com.minecolonies.api.util.constant.IToolType; -import net.minecraftforge.items.IItemHandler; +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.List; +import java.util.function.Predicate; /** * Colony expedition requirements for providing any kind of {@link com.minecolonies.api.util.constant.ToolType}. */ -public class ColonyExpeditionToolRequirement implements IColonyExpeditionRequirement +public class ColonyExpeditionToolRequirement extends ColonyExpeditionRequirement { /** * The required tool type. @@ -20,6 +27,11 @@ public class ColonyExpeditionToolRequirement implements IColonyExpeditionRequire */ private final int amount; + /** + * Cached set of icons, because lookup is expensive. + */ + private List iconsCached; + /** * Default constructor. * @@ -33,8 +45,67 @@ public ColonyExpeditionToolRequirement(final IToolType toolType, final int amoun } @Override - public boolean isFulFilled(final IItemHandler itemHandler) + @NotNull + public ResourceLocation getId() + { + return new ResourceLocation(Constants.MOD_ID, "tool/" + toolType.getName()); + } + + @Override + public ToolRequirementHandler createHandler(final InventorySupplier inventory) { - return InventoryUtils.getItemCountInItemHandler(itemHandler, stack -> ItemStackUtils.hasToolLevel(stack, this.toolType, 0, 5)) >= this.amount; + return new ToolRequirementHandler(inventory); + } + + /** + * Tool handler instance used for verifying if the given item handler contains the required tool. + */ + public class ToolRequirementHandler extends RequirementHandler + { + /** + * Default constructor. + * + * @param inventory supplier for obtaining the inventory. + */ + private ToolRequirementHandler(final InventorySupplier inventory) + { + super(inventory); + } + + @Override + public ResourceLocation getId() + { + return ColonyExpeditionToolRequirement.this.getId(); + } + + @Override + public Component getName() + { + return Component.translatable("com.minecolonies.core.expedition.gui.items.requirement.type.tool", toolType.getDisplayName()); + } + + @Override + public List getIcon() + { + if (iconsCached == null) + { + iconsCached = + IColonyManager.getInstance().getCompatibilityManager().getListOfAllItems().stream().filter(stack -> ItemStackUtils.isTool(stack, toolType)).toList(); + } + + return iconsCached; + } + + @Override + public int getAmount() + { + return amount; + } + + @Override + public Predicate getItemPredicate() + { + return stack -> ItemStackUtils.isTool(stack, toolType); + } } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java b/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java deleted file mode 100644 index 8ce9bbd9fbe..00000000000 --- a/src/main/java/com/minecolonies/core/colony/expeditions/colony/requirements/IColonyExpeditionRequirement.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.minecolonies.core.colony.expeditions.colony.requirements; - -import net.minecraftforge.items.IItemHandler; - -/** - * Interface for defining different types of colony expedition requirements. - */ -public interface IColonyExpeditionRequirement -{ - /** - * Whether the requirement is present in the provided inventory. - * - * @param itemHandler the inventory. - * @return whether the requirement is present or not. - */ - boolean isFulFilled(final IItemHandler itemHandler); -} \ No newline at end of file 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 9956de0e2e6..6b56b119a0d 100755 --- a/src/main/java/com/minecolonies/core/colony/jobs/AbstractJob.java +++ b/src/main/java/com/minecolonies/core/colony/jobs/AbstractJob.java @@ -229,6 +229,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/VisitorManager.java b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java index 542b585d46d..514a12c8263 100644 --- a/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java +++ b/src/main/java/com/minecolonies/core/colony/managers/VisitorManager.java @@ -11,9 +11,11 @@ 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.WorldUtil; import com.minecolonies.core.Network; import com.minecolonies.core.colony.VisitorData; +import com.minecolonies.core.colony.expeditions.ExpeditionBuilder; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; import com.minecolonies.core.colony.interactionhandling.ExpeditionaryInteraction; @@ -33,6 +35,7 @@ import static com.minecolonies.api.util.constant.Constants.SLIGHTLY_UP; import static com.minecolonies.api.util.constant.ExpeditionConstants.EXPEDITION_INTERACTION_INQUIRY; import static com.minecolonies.api.util.constant.PathingConstants.HALF_A_BLOCK; +import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION; import static com.minecolonies.core.entity.visitor.ExpeditionaryVisitorType.EXTRA_DATA_EXPEDITION_TYPE; /** @@ -233,6 +236,24 @@ public IVisitorData spawnOrCreateVisitor(final IVisitorType visitorType, IVisito return data; } + public void spawnExpeditionary() + { + final ColonyExpeditionType expeditionType = ColonyExpeditionTypeManager.getInstance().getRandomExpeditionType(colony); + if (expeditionType != null) + { + final IVisitorData newVisitor = createAndRegisterVisitorData(ModVisitorTypes.expeditionary.get()); + newVisitor.setExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE, expeditionType.getId()); + newVisitor.setExtraDataValue(EXTRA_DATA_EXPEDITION, new ExpeditionBuilder()); + newVisitor.triggerInteraction(new ExpeditionaryInteraction(Component.translatable(EXPEDITION_INTERACTION_INQUIRY, expeditionType.getToText()), + ChatPriority.IMPORTANT)); + + spawnOrCreateVisitor(ModVisitorTypes.expeditionary.get(), + newVisitor, + colony.getWorld(), + BlockPosUtil.getSurroundingEmptyBlock(colony.getWorld(), colony.getBuildingManager().getTownHall().getPosition(), 1, 2)); + } + } + @Override public IVisitorData createAndRegisterVisitorData(final IVisitorType visitorType) { @@ -277,15 +298,7 @@ public void onColonyTick(final IColony colony) if (visitorMap.values().stream().noneMatch(f -> f.getVisitorType().equals(ModVisitorTypes.expeditionary.get()))) { - final ColonyExpeditionType expeditionType = ColonyExpeditionTypeManager.getInstance().getRandomExpeditionType(colony); - if (expeditionType != null) - { - final IVisitorData newVisitor = createAndRegisterVisitorData(ModVisitorTypes.expeditionary.get()); - newVisitor.setExtraDataValue(EXTRA_DATA_EXPEDITION_TYPE, expeditionType.getId()); - newVisitor.triggerInteraction(new ExpeditionaryInteraction(Component.translatable(EXPEDITION_INTERACTION_INQUIRY), ChatPriority.IMPORTANT)); - - spawnOrCreateVisitor(ModVisitorTypes.expeditionary.get(), newVisitor, colony.getWorld(), colony.getBuildingManager().getTownHall().getPosition()); - } + spawnExpeditionary(); } } } 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 index 0e31b59f922..7f05ec32818 100644 --- a/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIExpeditionary.java +++ b/src/main/java/com/minecolonies/core/entity/ai/visitor/EntityAIExpeditionary.java @@ -5,10 +5,13 @@ 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.pathfinding.AbstractAdvancedPathNavigate; 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. */ @@ -35,8 +38,8 @@ public EntityAIExpeditionary(@NotNull final AbstractEntityVisitor entity) this.visitor = entity; ITickRateStateMachine stateMachine = entity.getEntityStateController(); - stateMachine.addTransition(new TickingTransition<>(EntityState.INIT, this::isEntityLoaded, () -> VisitorState.IDLE, 50)); - stateMachine.addTransition(new TickingTransition<>(VisitorState.IDLE, () -> true, this::decide, 50)); + stateMachine.addTransition(new TickingTransition<>(EntityState.INIT, this::isEntityLoaded, () -> VisitorState.WANDERING, 50)); + stateMachine.addTransition(new TickingTransition<>(VisitorState.WANDERING, () -> true, this::wander, 50)); } /** @@ -46,7 +49,7 @@ public EntityAIExpeditionary(@NotNull final AbstractEntityVisitor entity) */ private boolean isEntityLoaded() { - if (visitor.getCitizenColonyHandler().getColony() == null || visitor.getCitizenData() == null || visitor.getCitizenData().getHomeBuilding() == null) + if (visitor.getCitizenColonyHandler().getColony() == null || visitor.getCitizenData() == null) { return false; } @@ -61,10 +64,11 @@ private boolean isEntityLoaded() * * @return next state */ - private VisitorState decide() + private VisitorState wander() { - visitor.isWorkerAtSiteWithMove(townhall.getPosition(), 20); - return VisitorState.IDLE; + visitor.getNavigation().moveToRandomPos(10, DEFAULT_SPEED, townhall.getCorners(), AbstractAdvancedPathNavigate.RestrictionType.XYZ); + + return VisitorState.WANDERING; } /** @@ -72,9 +76,6 @@ private VisitorState decide() */ public enum VisitorState implements IState { - IDLE, - SLEEPING, - SITTING, WANDERING } } \ No newline at end of file diff --git a/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java index 7e074233a5c..61994afcfcb 100644 --- a/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/ExpeditionaryVisitorType.java @@ -2,16 +2,21 @@ import com.minecolonies.api.entity.ModEntities; import com.minecolonies.api.entity.visitor.*; -import com.minecolonies.core.colony.expeditions.Expedition; +import com.minecolonies.core.colony.expeditions.ExpeditionBuilder; import com.minecolonies.core.entity.ai.visitor.EntityAIExpeditionary; 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; + /** * Visitor type for expeditionary visitors in the town hall. */ @@ -21,7 +26,7 @@ public class ExpeditionaryVisitorType implements IVisitorType * Extra data fields. */ public static final ColonyExpeditionTypeData EXTRA_DATA_EXPEDITION_TYPE = new ColonyExpeditionTypeData(); - public static final ExpeditionData EXTRA_DATA_EXPEDITION = new ExpeditionData(); + public static final ExpeditionBuilderData EXTRA_DATA_EXPEDITION = new ExpeditionBuilderData(); @Override public ResourceLocation getId() @@ -47,6 +52,15 @@ public List> getExtraDataKeys() return List.of(EXTRA_DATA_EXPEDITION_TYPE, EXTRA_DATA_EXPEDITION); } + @Override + public @NotNull 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; + } + /** * Extra data for storing the expedition type instance. */ @@ -75,36 +89,27 @@ public void deserializeNBT(final CompoundTag compoundTag) } /** - * Extra data for storing the expedition instance. + * Extra data for storing the expedition builder instance. */ - public static class ExpeditionData extends AbstractVisitorExtraData> + public static class ExpeditionBuilderData extends AbstractVisitorExtraData { - private static final String TAG_VALUE = "value"; - - public ExpeditionData() + public ExpeditionBuilderData() { - super("expedition", Optional.empty()); + super("expedition", new ExpeditionBuilder()); } @Override public CompoundTag serializeNBT() { final CompoundTag compound = new CompoundTag(); - getValue().ifPresent(val -> { - final CompoundTag valueCompound = new CompoundTag(); - val.write(valueCompound); - compound.put(TAG_VALUE, valueCompound); - }); + getValue().write(compound); return compound; } @Override public void deserializeNBT(final CompoundTag compoundTag) { - if (compoundTag.contains(TAG_VALUE)) - { - setValue(Optional.of(Expedition.loadFromNBT(compoundTag.getCompound(TAG_VALUE)))); - } + setValue(ExpeditionBuilder.loadFromNBT(compoundTag)); } } } diff --git a/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java index 6e64b0d3c84..e6949a927a5 100644 --- a/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java +++ b/src/main/java/com/minecolonies/core/entity/visitor/RegularVisitorType.java @@ -66,19 +66,15 @@ public List> getExtraDataKeys() @Override public @NotNull 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 - ); + InventoryUtils.spawnItemStack(player.level, player.getX(), player.getY(), player.getZ(), remainingItem); } if (!level.isClientSide()) diff --git a/src/main/java/com/minecolonies/core/network/NetworkChannel.java b/src/main/java/com/minecolonies/core/network/NetworkChannel.java index c6602157cc6..8b1c49d009a 100755 --- a/src/main/java/com/minecolonies/core/network/NetworkChannel.java +++ b/src/main/java/com/minecolonies/core/network/NetworkChannel.java @@ -26,6 +26,7 @@ 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.TransferItemsMessage; import com.minecolonies.core.network.messages.splitting.SplitPacketMessage; import com.minecolonies.core.research.GlobalResearchTreeMessage; import io.netty.buffer.ByteBuf; @@ -200,6 +201,7 @@ public void registerCommonMessages() registerMessage(++idx, ToggleRecipeMessage.class, ToggleRecipeMessage::new); registerMessage(++idx, ColonyNameStyleMessage.class, ColonyNameStyleMessage::new); registerMessage(++idx, InteractionClose.class, InteractionClose::new); + registerMessage(++idx, TransferItemsMessage.class, TransferItemsMessage::new); //Client side only registerMessage(++idx, BlockParticleEffectMessage.class, BlockParticleEffectMessage::new); 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..533a00c2f21 --- /dev/null +++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/visitor/expeditionary/TransferItemsMessage.java @@ -0,0 +1,94 @@ +package com.minecolonies.core.network.messages.server.colony.visitor.expeditionary; + +import com.minecolonies.api.colony.IColony; +import com.minecolonies.api.colony.IVisitorData; +import com.minecolonies.api.colony.IVisitorViewData; +import com.minecolonies.api.util.InventoryUtils; +import com.minecolonies.api.util.Log; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionType; +import com.minecolonies.core.colony.expeditions.colony.ColonyExpeditionTypeManager; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement; +import com.minecolonies.core.colony.expeditions.colony.requirements.ColonyExpeditionRequirement.RequirementHandler; +import com.minecolonies.core.network.messages.server.AbstractColonyServerMessage; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +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 +{ + private int visitorId; + + private ResourceLocation expeditionType; + + private ResourceLocation requirementId; + + public TransferItemsMessage() + { + super(); + } + + public TransferItemsMessage(final IVisitorViewData visitorViewData, final ColonyExpeditionType expeditionType, final ResourceLocation requirementId) + { + super(visitorViewData.getColony()); + this.visitorId = visitorViewData.getId(); + this.expeditionType = expeditionType.getId(); + this.requirementId = requirementId; + } + + @Override + protected void onExecute(final Context ctxIn, final boolean isLogicalServer, final IColony colony) + { + if (!isLogicalServer) + { + return; + } + + final ColonyExpeditionType colonyExpeditionType = ColonyExpeditionTypeManager.getInstance().getExpeditionType(expeditionType); + if (colonyExpeditionType == null) + { + Log.getLogger().warn("Transferring items for expedition failed, expedition type '{}' does not exist on the server side.", expeditionType); + return; + } + + final IVisitorData visitor = colony.getVisitorManager().getCivilian(visitorId); + + final Optional requirement = colonyExpeditionType.getRequirements().stream() + .filter(f -> f.getId().equals(requirementId)) + .findFirst(); + if (requirement.isPresent()) + { + final RequirementHandler handler = requirement.get().createHandler(visitor::getInventory); + final int needed = handler.getAmount() - handler.getAmountAvailable(); + + if (needed > 0) + { + InventoryUtils.transferItemStackIntoNextFreeSlotFromItemHandler(new InvWrapper(ctxIn.getSender().getInventory()), + handler.getItemPredicate(), + needed, + visitor.getInventory()); + } + } + } + + @Override + protected void toBytesOverride(final FriendlyByteBuf buf) + { + buf.writeInt(visitorId); + buf.writeResourceLocation(expeditionType); + buf.writeResourceLocation(requirementId); + } + + @Override + protected void fromBytesOverride(final FriendlyByteBuf buf) + { + visitorId = buf.readInt(); + expeditionType = buf.readResourceLocation(); + requirementId = buf.readResourceLocation(); + } +} 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..e62db566a9c --- /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 @@