diff --git a/documentation/formatter/intellij/Minecolonies.xml b/documentation/formatter/intellij/Minecolonies.xml
index 7a4d1b49b2a..4ad74daf113 100644
--- a/documentation/formatter/intellij/Minecolonies.xml
+++ b/documentation/formatter/intellij/Minecolonies.xml
@@ -15,6 +15,8 @@
+
+
@@ -66,8 +68,8 @@
-
-
+
+
diff --git a/gradle.properties b/gradle.properties
index 4c9690af47a..4b8f8b88c24 100755
--- a/gradle.properties
+++ b/gradle.properties
@@ -79,7 +79,6 @@ usesSonarQube=true
usesCrowdInTranslationManagement=true
crowdInDownloadDirectory=src/main/resources/assets/minecolonies/lang
-usesCrowdInBuildingWithFilteredBranches=true
-usesCrowdInUploadWithFilteredBranches=true
+usesCrowdInUploadWithFilteredBranchesSpec=(version|release)\/.+
additionalModsInDataGen=structurize;domum_ornamentum
\ No newline at end of file
diff --git a/src/main/java/com/minecolonies/api/colony/ICitizenData.java b/src/main/java/com/minecolonies/api/colony/ICitizenData.java
index 32664fa1a65..ba8469709a3 100644
--- a/src/main/java/com/minecolonies/api/colony/ICitizenData.java
+++ b/src/main/java/com/minecolonies/api/colony/ICitizenData.java
@@ -432,4 +432,11 @@ default boolean hasCustomTexture()
* @return true if so.
*/
boolean hasQuestAssignment();
+
+ /**
+ * Get the home position of the citizen.
+ * @return the pos to go home to.
+ */
+ @Nullable
+ BlockPos getHomePosition();
}
diff --git a/src/main/java/com/minecolonies/api/colony/IColony.java b/src/main/java/com/minecolonies/api/colony/IColony.java
index 2466d921c6c..b30f2377ad8 100755
--- a/src/main/java/com/minecolonies/api/colony/IColony.java
+++ b/src/main/java/com/minecolonies/api/colony/IColony.java
@@ -125,10 +125,10 @@ public interface IColony
*
* @return The team name
*/
- default String getTeamName()
+ static String getTeamName(final Level level, final int id)
{
- final String dim = getDimension().location().getPath();
- return TEAM_COLONY_NAME + "_" + (dim.length() > 10 ? dim.hashCode() : dim) + "_" + getID();
+ final String dim = level.dimension().location().getPath();
+ return TEAM_COLONY_NAME + "_" + (dim.length() > 10 ? dim.hashCode() : dim) + "_" + id;
}
/**
diff --git a/src/main/java/com/minecolonies/api/colony/buildings/IBuilding.java b/src/main/java/com/minecolonies/api/colony/buildings/IBuilding.java
index 02ce24e5873..287c507fa24 100755
--- a/src/main/java/com/minecolonies/api/colony/buildings/IBuilding.java
+++ b/src/main/java/com/minecolonies/api/colony/buildings/IBuilding.java
@@ -513,4 +513,10 @@ default void writeToItemStack(final ItemStack stack)
getColony().writeToItemStack(stack);
new BuildingId(getID()).writeToItemStack(stack);
}
+
+ /**
+ * Get the standing position for a building.
+ * @return the standing pos.
+ */
+ BlockPos getStandingPosition();
}
diff --git a/src/main/java/com/minecolonies/api/colony/managers/interfaces/IRegisteredStructureManager.java b/src/main/java/com/minecolonies/api/colony/managers/interfaces/IRegisteredStructureManager.java
index 072f56b34e2..ba1a766c50c 100644
--- a/src/main/java/com/minecolonies/api/colony/managers/interfaces/IRegisteredStructureManager.java
+++ b/src/main/java/com/minecolonies/api/colony/managers/interfaces/IRegisteredStructureManager.java
@@ -91,10 +91,10 @@ public interface IRegisteredStructureManager
* Get the first building matching the conditions.
*
* @param predicate the predicate matching the building.
- * @return the position or null.
+ * @return the building or null.
*/
@Nullable
- BlockPos getFirstBuildingMatching(final Predicate predicate);
+ IBuilding getFirstBuildingMatching(final Predicate predicate);
/**
* Register a new leisure site.
diff --git a/src/main/java/com/minecolonies/api/compatibility/CompatibilityManager.java b/src/main/java/com/minecolonies/api/compatibility/CompatibilityManager.java
index 47f86ec1987..e8fb5f7ef82 100755
--- a/src/main/java/com/minecolonies/api/compatibility/CompatibilityManager.java
+++ b/src/main/java/com/minecolonies/api/compatibility/CompatibilityManager.java
@@ -17,7 +17,6 @@
import com.minecolonies.api.util.constant.NbtTagConstants;
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import net.minecraft.client.multiplayer.ClientLevel;
-import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
@@ -37,7 +36,6 @@
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.Level;
-import net.minecraft.world.level.block.AirBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.FurnaceBlockEntity;
@@ -148,16 +146,6 @@ public class CompatibilityManager implements ICompatibilityManager
*/
private static ImmutableList allItems = ImmutableList.of();
- /**
- * Free block positions everyone can interact with.
- */
- private final Set freeBlocks = new HashSet<>();
-
- /**
- * Free positions everyone can interact with.
- */
- private final Set freePositions = new HashSet<>();
-
/**
* Hashmap of mobs we may or may not attack.
*/
@@ -195,8 +183,6 @@ private void clear()
recruitmentCostsWeights.clear();
diseases.clear();
diseaseList.clear();
- freeBlocks.clear();
- freePositions.clear();
monsters = ImmutableSet.of();
creativeModeTabMap.clear();
}
@@ -215,7 +201,6 @@ public void discover(@NotNull final RecipeManager recipeManager, final Level lev
discoverLuckyOres();
discoverRecruitCosts();
discoverDiseases();
- discoverFreeBlocksAndPos();
discoverModCompat();
discoverCompostRecipes(recipeManager);
@@ -272,7 +257,6 @@ public void deserialize(@NotNull final RegistryFriendlyByteBuf buf, final Client
discoverLuckyOres();
discoverRecruitCosts();
discoverDiseases();
- discoverFreeBlocksAndPos();
discoverModCompat();
}
@@ -591,18 +575,6 @@ public ItemStack getRandomLuckyOre(final double chanceBonus, final int buildingL
return ItemStack.EMPTY;
}
- @Override
- public boolean isFreeBlock(final Block block)
- {
- return freeBlocks.contains(block);
- }
-
- @Override
- public boolean isFreePos(final BlockPos block)
- {
- return freePositions.contains(block);
- }
-
@Override
public CreativeModeTab getCreativeTab(final ItemStorage checkItem)
{
@@ -992,32 +964,6 @@ private static Tuple readLeafSaplingEntryFromNBT(@NotNu
return new Tuple<>(NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), compound), new ItemStorage(ItemStack.parseOptional(provider, compound.getCompound(NbtTagConstants.STACK)), false, true));
}
- /**
- * Load free blocks and pos from the config and add to colony.
- */
- private void discoverFreeBlocksAndPos()
- {
- for (final String s : MinecoloniesAPIProxy.getInstance().getConfig().getServer().freeToInteractBlocks.get())
- {
- try
- {
- final Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.parse(s));
- if (block != null && !(block instanceof AirBlock))
- {
- freeBlocks.add(block);
- }
- }
- catch (final Exception ex)
- {
- final BlockPos pos = BlockPosUtil.getBlockPosOfString(s);
- if (pos != null)
- {
- freePositions.add(pos);
- }
- }
- }
- }
-
/**
* Inits compats
*/
diff --git a/src/main/java/com/minecolonies/api/compatibility/ICompatibilityManager.java b/src/main/java/com/minecolonies/api/compatibility/ICompatibilityManager.java
index 5ea4abe477d..45271e75c32 100755
--- a/src/main/java/com/minecolonies/api/compatibility/ICompatibilityManager.java
+++ b/src/main/java/com/minecolonies/api/compatibility/ICompatibilityManager.java
@@ -6,7 +6,6 @@
import com.minecolonies.api.util.Disease;
import com.minecolonies.api.util.Tuple;
import net.minecraft.client.multiplayer.ClientLevel;
-import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.RegistryFriendlyByteBuf;
@@ -249,20 +248,6 @@ public interface ICompatibilityManager
*/
ItemStack getRandomLuckyOre(final double chanceBonus, final int buildingLevel);
- /**
- * Check if the block is configured to bypass the colony restrictions.
- * @param block the block to check.
- * @return true if so.
- */
- boolean isFreeBlock(Block block);
-
- /**
- * Check if the position is configured to bypass the colony restrictions.
- * @param block the position to check.
- * @return true if so.
- */
- boolean isFreePos(BlockPos block);
-
/**
* Get the creative tab for a stack.
* @param checkItem the storage wrapper.
diff --git a/src/main/java/com/minecolonies/api/configuration/ServerConfiguration.java b/src/main/java/com/minecolonies/api/configuration/ServerConfiguration.java
index b12da3e1d0a..dc64cb66d8c 100755
--- a/src/main/java/com/minecolonies/api/configuration/ServerConfiguration.java
+++ b/src/main/java/com/minecolonies/api/configuration/ServerConfiguration.java
@@ -97,7 +97,6 @@ public class ServerConfiguration extends AbstractConfiguration
public final BooleanValue enableColonyProtection;
public final EnumValue turnOffExplosionsInColonies;
- public final ConfigValue> freeToInteractBlocks;
/* -------------------------------------------------------------------------------- *
* ------------------- ######## Compatibility Settings ######## ------------------- *
@@ -201,7 +200,6 @@ public ServerConfiguration(final Builder builder)
enableColonyProtection = defineBoolean("enablecolonyprotection", true);
turnOffExplosionsInColonies = defineEnum("turnoffexplosionsincolonies", Explosions.DAMAGE_ENTITIES);
- freeToInteractBlocks = defineList("freetointeractblocks", () -> "block ID or position (x y z)", stringValidator, "dirt", "0 0 0");
swapToCategory("compatibility");
diff --git a/src/main/java/com/minecolonies/api/entity/citizen/AbstractEntityCitizen.java b/src/main/java/com/minecolonies/api/entity/citizen/AbstractEntityCitizen.java
index 32d83d1258c..d84ceaf319b 100755
--- a/src/main/java/com/minecolonies/api/entity/citizen/AbstractEntityCitizen.java
+++ b/src/main/java/com/minecolonies/api/entity/citizen/AbstractEntityCitizen.java
@@ -52,6 +52,7 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.items.IItemHandler;
+import net.minecraft.world.scores.PlayerTeam;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -239,6 +240,18 @@ public boolean isNoAi()
return false;
}
+ @Override
+ @Nullable
+ protected PlayerTeam getAssignedTeam()
+ {
+ final ICitizenColonyHandler citizenColonyHandler = getCitizenColonyHandler();
+ if (citizenColonyHandler == null)
+ {
+ return null;
+ }
+ return citizenColonyHandler.getTeam(level());
+ }
+
/**
* Sets the textures of all citizens and distinguishes between male and female.
*/
diff --git a/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenColonyHandler.java b/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenColonyHandler.java
index 9894600f529..122a60d4711 100755
--- a/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenColonyHandler.java
+++ b/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenColonyHandler.java
@@ -3,6 +3,8 @@
import com.minecolonies.api.colony.IColony;
import com.minecolonies.api.colony.buildings.IBuilding;
import net.minecraft.network.syncher.EntityDataAccessor;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.scores.PlayerTeam;
import org.jetbrains.annotations.Nullable;
public interface ICitizenColonyHandler
@@ -73,4 +75,10 @@ public interface ICitizenColonyHandler
void onSyncDataUpdate(EntityDataAccessor> dataAccessor);
boolean registered();
+
+ /**
+ * Get the citizen team.
+ * @return the team.
+ */
+ PlayerTeam getTeam(final Level level);
}
diff --git a/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenSleepHandler.java b/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenSleepHandler.java
index 9067a0b6a1a..e7cb2806574 100755
--- a/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenSleepHandler.java
+++ b/src/main/java/com/minecolonies/api/entity/citizen/citizenhandlers/ICitizenSleepHandler.java
@@ -24,13 +24,6 @@ public interface ICitizenSleepHandler
*/
void onWakeUp();
- /**
- * Determines the home position
- *
- * @return home pos or null
- */
- BlockPos findHomePos();
-
/**
* Get the bed location of the citizen.
*
diff --git a/src/main/java/com/minecolonies/api/entity/mobs/AbstractEntityRaiderMob.java b/src/main/java/com/minecolonies/api/entity/mobs/AbstractEntityRaiderMob.java
index f9553630107..5c824e0e495 100644
--- a/src/main/java/com/minecolonies/api/entity/mobs/AbstractEntityRaiderMob.java
+++ b/src/main/java/com/minecolonies/api/entity/mobs/AbstractEntityRaiderMob.java
@@ -49,17 +49,13 @@
import static com.minecolonies.api.util.constant.ColonyManagerConstants.NO_COLONY_ID;
import static com.minecolonies.api.util.constant.NbtTagConstants.*;
import static com.minecolonies.api.util.constant.RaiderConstants.*;
+import static com.minecolonies.core.util.TeamUtils.checkOrCreateTeam;
/**
* Abstract for all raider entities.
*/
public abstract class AbstractEntityRaiderMob extends AbstractFastMinecoloniesEntity implements IThreatTableEntity, Enemy
{
- /**
- * Difficulty at which raiders team up
- */
- private static final double TEAM_DIFFICULTY = 2.0d;
-
/**
* The percent of life taken per damage modifier
*/
@@ -523,14 +519,14 @@ public SpawnGroupData finalizeSpawn(
final ServerLevelAccessor worldIn,
final DifficultyInstance difficultyIn,
final MobSpawnType reason,
- @Nullable final SpawnGroupData p_21437_)
+ @Nullable final SpawnGroupData spawnDataIn)
{
RaiderMobUtils.setEquipment(this);
- return super.finalizeSpawn(worldIn, difficultyIn, reason, p_21437_);
+ return super.finalizeSpawn(worldIn, difficultyIn, reason, spawnDataIn);
}
@Override
- public void remove(RemovalReason reason)
+ public void remove(@NotNull final RemovalReason reason)
{
if (!level().isClientSide && colony != null && eventID > 0)
{
@@ -725,38 +721,15 @@ public void initStatsFor(final double baseHealth, final double difficulty, final
this.setEnvDamageImmunity(true);
}
- if (difficulty >= TEAM_DIFFICULTY)
- {
- level().getScoreboard().addPlayerToTeam(getScoreboardName(), checkOrCreateTeam());
- }
-
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(baseHealth);
this.setHealth(this.getMaxHealth());
}
- /**
- * Creates or gets the scoreboard team
- *
- * @return Scoreboard team
- */
- private PlayerTeam checkOrCreateTeam()
- {
- if (this.level().getScoreboard().getPlayerTeam(getTeamName()) == null)
- {
- this.level().getScoreboard().addPlayerTeam(getTeamName());
- this.level().getScoreboard().getPlayerTeam(getTeamName()).setAllowFriendlyFire(false);
- }
- return this.level().getScoreboard().getPlayerTeam(getTeamName());
- }
-
- /**
- * Gets the scoreboard team name
- *
- * @return
- */
- protected String getTeamName()
+ @Override
+ @Nullable
+ protected PlayerTeam getAssignedTeam()
{
- return RAID_TEAM;
+ return checkOrCreateTeam(level(), RAID_TEAM);
}
/**
diff --git a/src/main/java/com/minecolonies/api/entity/other/AbstractFastMinecoloniesEntity.java b/src/main/java/com/minecolonies/api/entity/other/AbstractFastMinecoloniesEntity.java
index a1e8a06fd4d..b528e542123 100644
--- a/src/main/java/com/minecolonies/api/entity/other/AbstractFastMinecoloniesEntity.java
+++ b/src/main/java/com/minecolonies/api/entity/other/AbstractFastMinecoloniesEntity.java
@@ -15,11 +15,13 @@
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.portal.DimensionTransition;
import net.minecraft.world.phys.Vec3;
+import net.minecraft.world.scores.PlayerTeam;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Special abstract minecolonies mob that overrides laggy vanilla behaviour.
@@ -56,6 +58,11 @@ public abstract class AbstractFastMinecoloniesEntity extends PathfinderMob imple
*/
private long lastHorizontalCollision = 0;
+ /**
+ * Last knockback time
+ */
+ protected long lastKnockBack = 0;
+
/**
* Create a new instance.
*
@@ -293,6 +300,78 @@ public void updateSwimAmount()
}
+ /**
+ * Get the team this entity is assigned to.
+ *
+ * @return the team instance.
+ */
+ @Nullable
+ protected abstract PlayerTeam getAssignedTeam();
+
+ @Override
+ @Nullable
+ public final PlayerTeam getTeam()
+ {
+ final PlayerTeam assignedTeam = getAssignedTeam();
+ registerToTeamInternal(assignedTeam);
+ return assignedTeam;
+ }
+
+ /**
+ * Register this entity to its own assigned team.
+ */
+ public void registerToTeam()
+ {
+ registerToTeamInternal(getAssignedTeam());
+ }
+
+ /**
+ * Internal method for team registration.
+ *
+ * @param team the team to register to.
+ */
+ private void registerToTeamInternal(@Nullable final PlayerTeam team)
+ {
+ if (team != null && !isInTeam(team))
+ {
+ level().getScoreboard().addPlayerToTeam(getScoreboardName(), team);
+ }
+ }
+
+ /**
+ * Remove the entity from its own assigned team.
+ */
+ public void removeFromTeam()
+ {
+ final PlayerTeam team = getAssignedTeam();
+ if (team != null && isInTeam(team))
+ {
+ level().getScoreboard().removePlayerFromTeam(getScoreboardName(), team);
+ }
+ }
+
+ /**
+ * Check if the current entity is assigned to the provided team.
+ *
+ * @param team the input team.
+ * @return true if so.
+ */
+ private boolean isInTeam(@NotNull final PlayerTeam team)
+ {
+ return Objects.equals(level().getScoreboard().getPlayersTeam(getScoreboardName()), team);
+ }
+
+ @Override
+ public void remove(@NotNull final RemovalReason reason)
+ {
+ super.remove(reason);
+ final PlayerTeam playersTeam = level().getScoreboard().getPlayersTeam(getScoreboardName());
+ if (playersTeam != null)
+ {
+ level().getScoreboard().removePlayerFromTeam(getScoreboardName(), playersTeam);
+ }
+ }
+
/**
* Static Byte values to avoid frequent autoboxing
*/
@@ -317,4 +396,14 @@ public boolean isShiftKeyDown()
{
return (this.entityData.get(DATA_SHARED_FLAGS_ID)).byteValue() == ENABLE.byteValue();
}
+
+ @Override
+ public void knockback(double power, double xRatio, double zRatio)
+ {
+ if (level().getGameTime() - lastKnockBack > 20 * 3)
+ {
+ lastKnockBack = level().getGameTime();
+ super.knockback(power, xRatio, zRatio);
+ }
+ }
}
diff --git a/src/main/java/com/minecolonies/api/entity/pathfinding/IPathJob.java b/src/main/java/com/minecolonies/api/entity/pathfinding/IPathJob.java
index 1561ea029d5..d4d79b7ae07 100644
--- a/src/main/java/com/minecolonies/api/entity/pathfinding/IPathJob.java
+++ b/src/main/java/com/minecolonies/api/entity/pathfinding/IPathJob.java
@@ -1,7 +1,10 @@
package com.minecolonies.api.entity.pathfinding;
-import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
import com.minecolonies.core.entity.pathfinding.PathingOptions;
+import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.level.Level;
import net.minecraft.world.level.pathfinder.Path;
import java.util.concurrent.Callable;
@@ -22,4 +25,10 @@ public interface IPathJob extends Callable
* @return
*/
public PathingOptions getPathingOptions();
+
+ Mob getEntity();
+
+ Level getActualWorld();
+
+ BlockPos getStart();
}
diff --git a/src/main/java/com/minecolonies/api/events/ColonyEvents.java b/src/main/java/com/minecolonies/api/events/ColonyEvents.java
new file mode 100644
index 00000000000..122da1f3689
--- /dev/null
+++ b/src/main/java/com/minecolonies/api/events/ColonyEvents.java
@@ -0,0 +1,38 @@
+package com.minecolonies.api.events;
+
+import com.minecolonies.api.colony.IColony;
+import com.minecolonies.api.colony.event.ColonyDeletedEvent;
+import com.minecolonies.api.util.Log;
+import net.neoforged.bus.api.Event;
+import net.neoforged.neoforge.common.NeoForge;
+
+/**
+ * Event manager for all forge events.
+ */
+public class ColonyEvents
+{
+ /**
+ * Event triggered when a colony is being deleted.
+ *
+ * @param colony the colony in question.
+ */
+ public static void deleteColony(final IColony colony)
+ {
+ sendEventSafe(new ColonyDeletedEvent(colony));
+ }
+
+ /**
+ * Underlying logic for transmitting an event.
+ */
+ private static void sendEventSafe(final Event event)
+ {
+ try
+ {
+ NeoForge.EVENT_BUS.post(event);
+ }
+ catch (final Exception e)
+ {
+ Log.getLogger().atError().withThrowable(e).log("Exception occurred during {} event", event.getClass().getName());
+ }
+ }
+}
diff --git a/src/main/java/com/minecolonies/api/util/BlockPosUtil.java b/src/main/java/com/minecolonies/api/util/BlockPosUtil.java
index 42f2fa25864..7819e108dfa 100755
--- a/src/main/java/com/minecolonies/api/util/BlockPosUtil.java
+++ b/src/main/java/com/minecolonies/api/util/BlockPosUtil.java
@@ -309,21 +309,6 @@ public static String getString(@NotNull final BlockPos position)
return "{x=" + position.getX() + ", y=" + position.getY() + ", z=" + position.getZ() + "}";
}
- /**
- * this checks that you are not in liquid. Will check for all liquids, even those from other mods before TP
- *
- * @param sender uses the player to get the world
- * @param blockPos for the current block LOC
- * @return isSafe true=safe false=water or lava
- */
- public static boolean isPositionSafe(@NotNull final Level sender, final BlockPos blockPos)
- {
- return !(sender.getBlockState(blockPos).getBlock() instanceof AirBlock)
- && !sender.getBlockState(blockPos).liquid()
- && !sender.getBlockState(blockPos.below()).liquid()
- && sender.getWorldBorder().isWithinBounds(blockPos);
- }
-
/**
* this checks that you are not in the air or underground. If so it will look up and down for a good landing spot before TP.
*
diff --git a/src/main/java/com/minecolonies/core/colony/CitizenData.java b/src/main/java/com/minecolonies/core/colony/CitizenData.java
index 5b966ed5033..d8c5e7c1bfe 100755
--- a/src/main/java/com/minecolonies/core/colony/CitizenData.java
+++ b/src/main/java/com/minecolonies/core/colony/CitizenData.java
@@ -1,10 +1,12 @@
package com.minecolonies.core.colony;
+import com.minecolonies.api.IMinecoloniesAPI;
import com.minecolonies.api.MinecoloniesAPIProxy;
import com.minecolonies.api.colony.CitizenNameFile;
import com.minecolonies.api.colony.ICitizenData;
import com.minecolonies.api.colony.IColony;
import com.minecolonies.api.colony.buildings.IBuilding;
+import com.minecolonies.api.colony.buildings.ModBuildings;
import com.minecolonies.api.colony.buildings.modules.IAssignsJob;
import com.minecolonies.api.colony.interactionhandling.ChatPriority;
import com.minecolonies.api.colony.interactionhandling.IInteractionResponseHandler;
@@ -501,8 +503,12 @@ public void initForNewCivilian()
textureId = random.nextInt(255);
saturation = MAX_SATURATION;
- final int levelCap = (int) colony.getOverallHappiness();
+ int levelCap = (int) colony.getOverallHappiness() * 2;
+ if (colony.getCitizenManager().getCitizens().size() < IMinecoloniesAPI.getInstance().getConfig().getServer().initialCitizenAmount.get())
+ {
+ levelCap = Math.max(5, levelCap);
+ }
citizenSkillHandler.init(levelCap);
markDirty(0);
@@ -1990,4 +1996,29 @@ public UUID getCustomTexture()
{
return textureUUID;
}
+ @Nullable
+ public BlockPos getHomePosition()
+ {
+ @Nullable final IBuilding homeBuilding = getHomeBuilding();
+ if (homeBuilding != null)
+ {
+ return homeBuilding.getStandingPosition();
+ }
+
+ if (colony != null)
+ {
+ final IBuilding tavern = colony.getBuildingManager().getFirstBuildingMatching(b -> b.getBuildingType() == ModBuildings.tavern.get());
+ if (tavern != null)
+ {
+ return tavern.getStandingPosition();
+ }
+ else if (colony.getBuildingManager().getTownHall() != null)
+ {
+ return colony.getBuildingManager().getTownHall().getPosition();
+ }
+ return colony.getCenter();
+ }
+
+ return null;
+ }
}
diff --git a/src/main/java/com/minecolonies/core/colony/Colony.java b/src/main/java/com/minecolonies/core/colony/Colony.java
index cc670f39f11..33b4bb90e05 100644
--- a/src/main/java/com/minecolonies/core/colony/Colony.java
+++ b/src/main/java/com/minecolonies/core/colony/Colony.java
@@ -85,6 +85,7 @@
import static com.minecolonies.api.util.constant.NbtTagConstants.*;
import static com.minecolonies.api.util.constant.TranslationConstants.*;
import static com.minecolonies.core.MineColonies.getConfig;
+import static com.minecolonies.core.util.TeamUtils.checkOrCreateTeam;
/**
* This class describes a colony and contains all the data and methods for manipulating a Colony.
@@ -374,7 +375,7 @@ public class Colony implements IColony
this.colonyFlag = new BannerPatternLayers.Builder().add(Utils.getRegistryValue(BannerPatterns.BASE, world), DyeColor.WHITE).build();
this.dimensionId = world.dimension();
onWorldLoad(world);
- checkOrCreateTeam();
+ checkOrCreateTeam(world, IColony.getTeamName(world, id), false);
}
colonyStateMachine = new TickRateStateMachine<>(INACTIVE, e ->
@@ -635,23 +636,11 @@ public void updateAttackingPlayers()
}
@Override
+ @Nullable
public PlayerTeam getTeam()
{
// This getter will create the team if it doesn't exist. Could do something different though in the future.
- return checkOrCreateTeam();
- }
-
- /**
- * Check or create the team.
- */
- private PlayerTeam checkOrCreateTeam()
- {
- if (this.world.getScoreboard().getPlayerTeam(getTeamName()) == null)
- {
- this.world.getScoreboard().addPlayerTeam(getTeamName());
- this.world.getScoreboard().getPlayerTeam(getTeamName()).setAllowFriendlyFire(false);
- }
- return this.world.getScoreboard().getPlayerTeam(getTeamName());
+ return checkOrCreateTeam(world, IColony.getTeamName(world, id), false);
}
/**
@@ -663,10 +652,13 @@ public void setColonyColor(final ChatFormatting colonyColor)
{
if (this.world != null)
{
- checkOrCreateTeam();
this.colonyTeamColor = colonyColor;
- this.world.getScoreboard().getPlayerTeam(getTeamName()).setColor(colonyColor);
- this.world.getScoreboard().getPlayerTeam(getTeamName()).setPlayerPrefix(Component.literal(colonyColor.toString()));
+ final PlayerTeam team = getTeam();
+ if (team != null)
+ {
+ team.setColor(colonyColor);
+ team.setPlayerPrefix(Component.literal(colonyColor.toString()));
+ }
}
this.markDirty();
}
@@ -1858,7 +1850,7 @@ && getConfig().getServer().forceLoadColony.get())
{
checkChunkAndRegisterTicket(chunkPos, chunk);
}
- else
+ else if (buildingManager.keepChunkColonyLoaded(chunk))
{
this.pendingChunks.add(chunkPos);
}
@@ -1920,8 +1912,8 @@ public void setTextureStyle(final String style)
public String getTextureStyleId()
{
if (MineColonies.getConfig().getServer().holidayFeatures.get() &&
- (LocalDateTime.now().getDayOfMonth() >= 29 && LocalDateTime.now().getMonth() == Month.OCTOBER)
- || (LocalDateTime.now().getDayOfMonth() <= 2 && LocalDateTime.now().getMonth() == Month.NOVEMBER))
+ ((LocalDateTime.now().getDayOfMonth() >= 29 && LocalDateTime.now().getMonth() == Month.OCTOBER)
+ || (LocalDateTime.now().getDayOfMonth() <= 2 && LocalDateTime.now().getMonth() == Month.NOVEMBER)))
{
return "nether";
}
diff --git a/src/main/java/com/minecolonies/core/colony/ColonyManager.java b/src/main/java/com/minecolonies/core/colony/ColonyManager.java
index 4ce599255cf..d05414a894c 100755
--- a/src/main/java/com/minecolonies/core/colony/ColonyManager.java
+++ b/src/main/java/com/minecolonies/core/colony/ColonyManager.java
@@ -15,6 +15,7 @@
import com.minecolonies.api.compatibility.CompatibilityManager;
import com.minecolonies.api.compatibility.ICompatibilityManager;
import com.minecolonies.api.crafting.IRecipeManager;
+import com.minecolonies.api.events.ColonyEvents;
import com.minecolonies.api.sounds.SoundManager;
import com.minecolonies.api.util.BlockPosUtil;
import com.minecolonies.api.util.ColonyUtils;
@@ -216,6 +217,7 @@ private void deleteColony(@Nullable final IColony iColony, final boolean canDest
return;
}
+ ColonyEvents.deleteColony(colony);
cap.deleteColony(id);
BackUpHelper.markColonyDeleted(colony.getID(), colony.getDimension());
colony.getImportantMessageEntityPlayers()
diff --git a/src/main/java/com/minecolonies/core/colony/ColonyView.java b/src/main/java/com/minecolonies/core/colony/ColonyView.java
index 55646a70eb7..22a9a36c3b1 100644
--- a/src/main/java/com/minecolonies/core/colony/ColonyView.java
+++ b/src/main/java/com/minecolonies/core/colony/ColonyView.java
@@ -1258,7 +1258,7 @@ public boolean isDay()
@Override
public PlayerTeam getTeam()
{
- return getWorld().getScoreboard().getPlayerTeam(getTeamName());
+ return getWorld().getScoreboard().getPlayerTeam(IColony.getTeamName(getWorld(), id));
}
@Override
diff --git a/src/main/java/com/minecolonies/core/colony/buildings/AbstractBuilding.java b/src/main/java/com/minecolonies/core/colony/buildings/AbstractBuilding.java
index 9c22a1824b7..72f78221d83 100644
--- a/src/main/java/com/minecolonies/core/colony/buildings/AbstractBuilding.java
+++ b/src/main/java/com/minecolonies/core/colony/buildings/AbstractBuilding.java
@@ -7,6 +7,7 @@
import com.ldtteam.structurize.api.RotationMirror;
import com.ldtteam.structurize.blueprints.v1.Blueprint;
import com.ldtteam.structurize.storage.StructurePacks;
+import com.ldtteam.structurize.util.BlockUtils;
import com.minecolonies.api.MinecoloniesAPIProxy;
import com.minecolonies.api.blocks.AbstractBlockHut;
import com.minecolonies.api.colony.ICitizenData;
@@ -61,12 +62,14 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
+import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.BlockTags;
import net.minecraft.util.Tuple;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
@@ -108,6 +111,11 @@ public abstract class AbstractBuilding extends AbstractBuildingContainer
public static final ISettingKey USE_SHEARS = new SettingKey<>(BoolSetting.class, new ResourceLocation(Constants.MOD_ID, "useshears"));
+ /**
+ * Best possible standing pos score.
+ */
+ private static final int BEST_STANDING_SCORE = 10;
+
/**
* The data store id for request system related data.
*/
@@ -154,6 +162,11 @@ public abstract class AbstractBuilding extends AbstractBuildingContainer
*/
public int pickUpDay = -1;
+ /**
+ * Cached position for citizen standing position next to hut block.
+ */
+ private BlockPos cachedStandingPosition;
+
/**
* Constructor for a AbstractBuilding.
*
@@ -995,6 +1008,7 @@ public void onUpgradeComplete(final int newLevel)
getModulesByType(IBuildingEventsModule.class).forEach(module -> module.onUpgradeComplete(newLevel));
colony.getResearchManager().checkAutoStartResearch();
colony.getBuildingManager().onBuildingUpgradeComplete(this, newLevel);
+ cachedStandingPosition = null;
}
@Override
@@ -1050,6 +1064,61 @@ public void resetGuardBuildingNear()
this.recheckGuardBuildingNear = true;
}
+ @Override
+ public BlockPos getStandingPosition()
+ {
+ if (cachedStandingPosition == null)
+ {
+ if (!WorldUtil.isEntityBlockLoaded(colony.getWorld(), getPosition()))
+ {
+ return getPosition();
+ }
+
+ BlockPos bestPos = getPosition();
+ int bestScore = 0;
+
+ //Return true if the building is null to stall the worker
+ for (final Direction dir : Direction.Plane.HORIZONTAL)
+ {
+ final BlockPos currentPos = getPosition().relative(dir);
+ final BlockState hereState = colony.getWorld().getBlockState(currentPos);
+ // Check air here and air above.
+ if ((!hereState.getBlock().properties().hasCollision || hereState.is(BlockTags.WOOL_CARPETS))
+ && !colony.getWorld().getBlockState(currentPos.above()).getBlock().properties().hasCollision
+ && BlockUtils.isAnySolid(colony.getWorld().getBlockState(currentPos.below())))
+ {
+ int localScore = BEST_STANDING_SCORE;
+ if (colony.getWorld().canSeeSky(currentPos))
+ {
+ // More critical
+ localScore-=2;
+ }
+ if (colony.getWorld().getBlockState(getPosition()).getValue(AbstractBlockHut.FACING) == dir)
+ {
+ // Less critical
+ localScore--;
+ }
+
+ if (localScore == BEST_STANDING_SCORE)
+ {
+ cachedStandingPosition = currentPos;
+ return cachedStandingPosition;
+ }
+
+ if (localScore > bestScore)
+ {
+ bestScore = localScore;
+ bestPos = currentPos;
+ }
+ }
+ }
+
+ // prefer default rotation of the building facing this.
+ cachedStandingPosition = bestPos;
+ }
+ return cachedStandingPosition == null ? getPosition() : cachedStandingPosition;
+ }
+
//------------------------- Starting Required Tools/Item handling -------------------------//
@Override
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 5aaf4050e6b..f08e5e244b1 100755
--- a/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java
+++ b/src/main/java/com/minecolonies/core/colony/managers/CitizenManager.java
@@ -139,10 +139,10 @@ public void registerCivilian(final AbstractCivilianEntity entity)
final Optional existingCitizen = data.getEntity();
- if (!existingCitizen.isPresent())
+ if (existingCitizen.isEmpty())
{
data.setEntity(entity);
- entity.level().getScoreboard().addPlayerToTeam(entity.getScoreboardName(), colony.getTeam());
+ entity.registerToTeam();
return;
}
@@ -159,18 +159,7 @@ public void unregisterCivilian(final AbstractCivilianEntity entity)
final ICitizenData data = citizens.get(entity.getCivilianID());
if (data != null && data.getEntity().isPresent() && data.getEntity().get() == entity)
{
- try
- {
- if (colony.getWorld().getScoreboard().getPlayersTeam(entity.getScoreboardName()) == colony.getTeam())
- {
- colony.getWorld().getScoreboard().removePlayerFromTeam(entity.getScoreboardName(), colony.getTeam());
- }
- }
- catch (Exception ignored)
- {
- // For some weird reason we can get an exception here, though the exception is thrown for team != colony team which we check == on before
- }
-
+ entity.removeFromTeam();
citizens.get(entity.getCivilianID()).setEntity(null);
}
}
@@ -300,10 +289,15 @@ private ICitizenData spawnCitizenOnPosition(
if (world instanceof ServerLevel serverLevel)
{
- final Entity existing = serverLevel.getEntity(citizenData.getUUID());
+ Entity existing = serverLevel.getEntity(citizenData.getUUID());
if (existing != null)
{
existing.discard();
+ existing = serverLevel.getEntity(citizenData.getUUID());
+ if (existing != null)
+ {
+ serverLevel.entityManager.stopTracking(existing);
+ }
}
}
diff --git a/src/main/java/com/minecolonies/core/colony/managers/RegisteredStructureManager.java b/src/main/java/com/minecolonies/core/colony/managers/RegisteredStructureManager.java
index 51e1968c102..b38ee247bd7 100644
--- a/src/main/java/com/minecolonies/core/colony/managers/RegisteredStructureManager.java
+++ b/src/main/java/com/minecolonies/core/colony/managers/RegisteredStructureManager.java
@@ -355,14 +355,14 @@ public BlockPos getRandomLeisureSite()
{
final boolean isRaining = colony.getWorld().isRaining();
- BlockPos pos = null;
+ IBuilding building = null;
final int randomDist = RANDOM.nextInt(4);
if (randomDist < 1)
{
- pos = getFirstBuildingMatching(b -> b instanceof BuildingTownHall && b.getBuildingLevel() >= 3);
- if (pos != null)
+ building = getFirstBuildingMatching(b -> b instanceof BuildingTownHall && b.getBuildingLevel() >= 3);
+ if (building != null)
{
- return pos;
+ return building.getPosition();
}
}
@@ -370,28 +370,28 @@ public BlockPos getRandomLeisureSite()
{
if (!isRaining && RANDOM.nextBoolean())
{
- pos = getFirstBuildingMatching(b -> b instanceof BuildingMysticalSite && b.getBuildingLevel() >= 1);
- if (pos != null)
+ building = getFirstBuildingMatching(b -> b instanceof BuildingMysticalSite && b.getBuildingLevel() >= 1);
+ if (building != null)
{
- return pos;
+ return building.getPosition();
}
}
else
{
- pos = getFirstBuildingMatching(b -> b instanceof BuildingLibrary && b.getBuildingLevel() >= 1);
- if (pos != null)
+ building = getFirstBuildingMatching(b -> b instanceof BuildingLibrary && b.getBuildingLevel() >= 1);
+ if (building != null)
{
- return pos;
+ return building.getPosition();
}
}
}
if (randomDist < 3)
{
- pos = getFirstBuildingMatching(b -> b.hasModule(BuildingModules.TAVERN_VISITOR) && b.getBuildingLevel() >= 1);
- if (pos != null)
+ building = getFirstBuildingMatching(b -> b.hasModule(BuildingModules.TAVERN_VISITOR) && b.getBuildingLevel() >= 1);
+ if (building != null)
{
- return pos;
+ return building.getPosition();
}
}
@@ -405,13 +405,13 @@ public BlockPos getRandomLeisureSite()
@Nullable
@Override
- public BlockPos getFirstBuildingMatching(final Predicate predicate)
+ public IBuilding getFirstBuildingMatching(final Predicate predicate)
{
for (final IBuilding building : buildings.values())
{
if (predicate.test(building))
{
- return building.getPosition();
+ return building;
}
}
return null;
diff --git a/src/main/java/com/minecolonies/core/colony/permissions/ColonyPermissionEventHandler.java b/src/main/java/com/minecolonies/core/colony/permissions/ColonyPermissionEventHandler.java
index 95aecaef483..055e315aa16 100755
--- a/src/main/java/com/minecolonies/core/colony/permissions/ColonyPermissionEventHandler.java
+++ b/src/main/java/com/minecolonies/core/colony/permissions/ColonyPermissionEventHandler.java
@@ -451,7 +451,7 @@ public void onRightClickItem(final PlayerInteractEvent.RightClickItem event)
*/
private boolean isFreeToInteractWith(@Nullable final Block block, final BlockPos pos)
{
- return (block != null && (IColonyManager.getInstance().getCompatibilityManager().isFreeBlock(block) || colony.getFreeBlocks().contains(block) || block.defaultBlockState().is(ModTags.colonyProtectionException))) || colony.getFreePositions().contains(pos) || IColonyManager.getInstance().getCompatibilityManager().isFreePos(pos);
+ return (block != null && (colony.getFreeBlocks().contains(block) || block.defaultBlockState().is(ModTags.colonyProtectionException))) || colony.getFreePositions().contains(pos);
}
/**
diff --git a/src/main/java/com/minecolonies/core/commands/EntryPoint.java b/src/main/java/com/minecolonies/core/commands/EntryPoint.java
index 5f2f11028b9..f2ee1586fb4 100755
--- a/src/main/java/com/minecolonies/core/commands/EntryPoint.java
+++ b/src/main/java/com/minecolonies/core/commands/EntryPoint.java
@@ -27,92 +27,91 @@ public static void register(final CommandDispatcher dispatch
* Kill commands subtree
*/
final CommandTree killCommands = new CommandTree("kill")
- .addNode(new CommandKillAnimal().build())
- .addNode(new CommandKillChicken().build())
- .addNode(new CommandKillCow().build())
- .addNode(new CommandKillMonster().build())
- .addNode(new CommandKillPig().build())
- .addNode(new CommandKillRaider().build())
- .addNode(new CommandKillSheep().build());
+ .addNode(new CommandKillAnimal().build())
+ .addNode(new CommandKillChicken().build())
+ .addNode(new CommandKillCow().build())
+ .addNode(new CommandKillMonster().build())
+ .addNode(new CommandKillPig().build())
+ .addNode(new CommandKillRaider().build())
+ .addNode(new CommandKillSheep().build());
/*
* Colony commands subtree
*/
final CommandTree colonyCommands = new CommandTree("colony")
- .addNode(new CommandAddOfficer().build())
- .addNode(new CommandSetRank().build())
- .addNode(new CommandChangeOwner().build())
- .addNode(new CommandClaimChunks().build())
- .addNode(new CommandShowClaim().build())
- .addNode(new CommandTeleport().build())
- .addNode(new CommandDeleteColony().build())
- .addNode(new CommandCanRaiderSpawn().build())
- .addNode(new CommandRaid().build())
- .addNode(new CommandHomeTeleport().build())
- .addNode(new CommandListColonies().build())
- .addNode(new CommandSetDeletable().build())
- .addNode(new CommandReclaimChunks().build())
- .addNode(new CommandLoadBackup().build())
- .addNode(new CommandLoadAllBackups().build())
- .addNode(new CommandColonyInfo().build())
- .addNode(new CommandColonyPrintStats().build())
- .addNode(new CommandColonyRaidsInfo().build())
- .addNode(new CommandColonyChunks().build())
- .addNode(new CommandRSReset().build())
- .addNode(new CommandRSResetAll().build())
- .addNode(new CommandSetAbandoned().build())
- .addNode(new CommandExportColony().build());
+ .addNode(new CommandAddOfficer().build())
+ .addNode(new CommandSetRank().build())
+ .addNode(new CommandChangeOwner().build())
+ .addNode(new CommandClaimChunks().build())
+ .addNode(new CommandShowClaim().build())
+ .addNode(new CommandTeleport().build())
+ .addNode(new CommandDeleteColony().build())
+ .addNode(new CommandCanRaiderSpawn().build())
+ .addNode(new CommandRaid().build())
+ .addNode(new CommandHomeTeleport().build())
+ .addNode(new CommandListColonies().build())
+ .addNode(new CommandSetDeletable().build())
+ .addNode(new CommandReclaimChunks().build())
+ .addNode(new CommandLoadBackup().build())
+ .addNode(new CommandLoadAllBackups().build())
+ .addNode(new CommandColonyInfo().build())
+ .addNode(new CommandColonyPrintStats().build())
+ .addNode(new CommandColonyRaidsInfo().build())
+ .addNode(new CommandColonyChunks().build())
+ .addNode(new CommandRSReset().build())
+ .addNode(new CommandRSResetAll().build())
+ .addNode(new CommandSetAbandoned().build())
+ .addNode(new CommandExportColony().build());
/*
* Citizen commands subtree
*/
final CommandTree citizenCommands = new CommandTree("citizens")
- .addNode(new CommandCitizenInfo().build())
- .addNode(new CommandCitizenKill().build())
- .addNode(new CommandCitizenList().build())
- .addNode(new CommandCitizenReload().build())
- .addNode(new CommandCitizenSpawnNew().build())
- .addNode(new CommandCitizenTeleport().build())
- .addNode(new CommandCitizenTriggerWalkTo().build())
- .addNode(new CommandCitizenTrack().build());
+ .addNode(new CommandCitizenInfo().build())
+ .addNode(new CommandCitizenKill().build())
+ .addNode(new CommandCitizenList().build())
+ .addNode(new CommandCitizenReload().build())
+ .addNode(new CommandCitizenSpawnNew().build())
+ .addNode(new CommandCitizenTeleport().build())
+ .addNode(new CommandCitizenTriggerWalkTo().build())
+ .addNode(new CommandCitizenTrack().build())
+ .addNode(new CommandTrackType().build());
/*
* Root minecolonies command tree, all subtrees are added here.
*/
final CommandTree minecoloniesRoot = new CommandTree(Constants.MOD_ID)
- .addNode(killCommands)
- .addNode(colonyCommands)
- .addNode(new CommandHomeTeleport().build())
- .addNode(citizenCommands)
- .addNode(new CommandWhereAmI().build())
- .addNode(new CommandWhoAmI().build())
- .addNode(new CommandRTP().build())
- .addNode(new CommandUnloadForcedChunks().build())
- .addNode(new CommandRaidAll().build())
- .addNode(new CommandBackup().build())
- .addNode(new CommandResetPlayerSupplies().build())
- .addNode(new CommandHelp().build())
- .addNode(new ScanCommand().build())
- .addNode(new CommandPruneWorld().build());
+ .addNode(killCommands)
+ .addNode(colonyCommands)
+ .addNode(new CommandHomeTeleport().build())
+ .addNode(citizenCommands)
+ .addNode(new CommandWhereAmI().build())
+ .addNode(new CommandWhoAmI().build())
+ .addNode(new CommandUnloadForcedChunks().build())
+ .addNode(new CommandRaidAll().build())
+ .addNode(new CommandBackup().build())
+ .addNode(new CommandResetPlayerSupplies().build())
+ .addNode(new CommandHelp().build())
+ .addNode(ScanCommand.build())
+ .addNode(new CommandPruneWorld().build());
/*
* Root minecolonies alias command tree, all subtrees are added here.
*/
final CommandTree minecoloniesRootAlias = new CommandTree("mc")
- .addNode(new CommandEntityTrack().build())
- .addNode(killCommands)
- .addNode(colonyCommands)
- .addNode(new CommandHomeTeleport().build())
- .addNode(citizenCommands)
- .addNode(new CommandWhereAmI().build())
- .addNode(new CommandWhoAmI().build())
- .addNode(new CommandRTP().build())
- .addNode(new CommandUnloadForcedChunks().build())
- .addNode(new CommandRaidAll().build())
- .addNode(new CommandBackup().build())
- .addNode(new CommandResetPlayerSupplies().build())
- .addNode(new CommandHelp().build())
- .addNode(new CommandPruneWorld().build());
+ .addNode(new CommandEntityTrack().build())
+ .addNode(killCommands)
+ .addNode(colonyCommands)
+ .addNode(new CommandHomeTeleport().build())
+ .addNode(citizenCommands)
+ .addNode(new CommandWhereAmI().build())
+ .addNode(new CommandWhoAmI().build())
+ .addNode(new CommandUnloadForcedChunks().build())
+ .addNode(new CommandRaidAll().build())
+ .addNode(new CommandBackup().build())
+ .addNode(new CommandResetPlayerSupplies().build())
+ .addNode(new CommandHelp().build())
+ .addNode(new CommandPruneWorld().build());
// Adds all command trees to the dispatcher to register the commands.
dispatcher.register(minecoloniesRoot.build());
diff --git a/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenInfo.java b/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenInfo.java
index 53dd93ba079..dac535c575f 100755
--- a/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenInfo.java
+++ b/src/main/java/com/minecolonies/core/commands/citizencommands/CommandCitizenInfo.java
@@ -71,7 +71,7 @@ public int onExecute(final CommandContext context)
final BlockPos citizenPosition = entityCitizen.blockPosition();
context.getSource()
.sendSuccess(() -> Component.translatableEscape(CommandTranslationConstants.COMMAND_CITIZEN_INFO_POSITION, citizenPosition.getX(), citizenPosition.getY(), citizenPosition.getZ()), true);
- final BlockPos homePosition = entityCitizen.getRestrictCenter();
+ final BlockPos homePosition = citizenData.getHomePosition();
context.getSource()
.sendSuccess(() -> Component.translatableEscape(CommandTranslationConstants.COMMAND_CITIZEN_INFO_HOME_POSITION, homePosition.getX(), homePosition.getY(), homePosition.getZ()), true);
diff --git a/src/main/java/com/minecolonies/core/commands/citizencommands/CommandTrackType.java b/src/main/java/com/minecolonies/core/commands/citizencommands/CommandTrackType.java
new file mode 100644
index 00000000000..d9df47de08b
--- /dev/null
+++ b/src/main/java/com/minecolonies/core/commands/citizencommands/CommandTrackType.java
@@ -0,0 +1,61 @@
+package com.minecolonies.core.commands.citizencommands;
+
+import com.minecolonies.core.commands.commandTypes.IMCCommand;
+import com.minecolonies.core.commands.commandTypes.IMCOPCommand;
+import com.minecolonies.core.entity.pathfinding.PathfindingUtils;
+import com.mojang.brigadier.arguments.StringArgumentType;
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
+import com.mojang.brigadier.context.CommandContext;
+import net.minecraft.commands.CommandSourceStack;
+import net.minecraft.network.chat.Component;
+
+/**
+ * Displays information about a chosen citizen in a chosen colony.
+ */
+public class CommandTrackType implements IMCOPCommand
+{
+ /**
+ * What happens when the command is executed after preConditions are successful.
+ *
+ * @param context the context of the command execution
+ */
+ @Override
+ public int onExecute(final CommandContext context)
+ {
+ if (!context.getSource().isPlayer())
+ {
+ return 1;
+ }
+
+ final String className = StringArgumentType.getString(context, "pathjobname");
+ if (className.equals("clear"))
+ {
+ PathfindingUtils.trackByType.entrySet().removeIf(entry -> entry.getValue().equals(context.getSource().getPlayer().getUUID()));
+ context.getSource().sendSystemMessage(Component.literal("Removed tracking for player"));
+ return 1;
+ }
+
+ PathfindingUtils.trackByType.put(className, context.getSource().getPlayer().getUUID());
+ context.getSource().sendSystemMessage(Component.literal("Tracking enabled for pathjobs containing: " + className));
+ return 1;
+ }
+
+ /**
+ * Name string of the command.
+ */
+ @Override
+ public String getName()
+ {
+ return "trackPathType";
+ }
+
+ @Override
+ public LiteralArgumentBuilder build()
+ {
+ return IMCCommand.newLiteral(getName())
+ .then(IMCCommand.newArgument("pathjobname", StringArgumentType.word()).suggests((context, builder) -> {
+ builder.suggest("clear");
+ return builder.buildFuture();
+ }).executes(this::checkPreConditionAndExecute));
+ }
+}
diff --git a/src/main/java/com/minecolonies/core/commands/colonycommands/CommandColonyPrintStats.java b/src/main/java/com/minecolonies/core/commands/colonycommands/CommandColonyPrintStats.java
index dbe519a9580..de81e9f319d 100644
--- a/src/main/java/com/minecolonies/core/commands/colonycommands/CommandColonyPrintStats.java
+++ b/src/main/java/com/minecolonies/core/commands/colonycommands/CommandColonyPrintStats.java
@@ -2,6 +2,7 @@
import com.minecolonies.api.colony.IColony;
import com.minecolonies.api.colony.IColonyManager;
+import com.minecolonies.api.colony.buildings.IBuilding;
import com.minecolonies.api.util.Log;
import com.minecolonies.core.colony.events.raid.RaidManager;
import com.minecolonies.core.commands.commandTypes.IMCCommand;
@@ -82,16 +83,24 @@ public int onExecute(final CommandContext context)
context.getSource().sendSuccess(() -> literalAndRemember(last.toString()), false);
}
- if (colony.getBuildingManager().getBuildings().size() > 0)
+ if (!colony.getBuildingManager().getBuildings().isEmpty())
{
+ int count = 0;
+ int levels = 0;
+
+ for (final IBuilding building : colony.getBuildingManager().getBuildings().values())
+ {
+ if (building.getBuildingLevel() != 0)
+ {
+ count++;
+ levels += building.getBuildingLevel();
+ }
+ }
+
+ final double average = (double) levels / count;
+
context.getSource()
- .sendSuccess(() -> literalAndRemember("Buildings:" + colony.getBuildingManager().getBuildings().size() + " average level:" + colony.getBuildingManager()
- .getBuildings()
- .values()
- .stream()
- .filter(iBuilding -> iBuilding.getBuildingLevel() != 0)
- .collect(
- Collectors.summingInt(ibuilding -> ibuilding.getBuildingLevel())) / colony.getBuildingManager().getBuildings().size()), false);
+ .sendSuccess(() -> literalAndRemember("Buildings:" + colony.getBuildingManager().getBuildings().size() + " average level:" + average), false);
context.getSource()
.sendSuccess(() -> literalAndRemember(colony.getBuildingManager()
.getBuildings()
diff --git a/src/main/java/com/minecolonies/core/commands/colonycommands/CommandRaid.java b/src/main/java/com/minecolonies/core/commands/colonycommands/CommandRaid.java
index 2e595ed47f5..e8d4f3fd8c5 100644
--- a/src/main/java/com/minecolonies/core/commands/colonycommands/CommandRaid.java
+++ b/src/main/java/com/minecolonies/core/commands/colonycommands/CommandRaid.java
@@ -5,6 +5,7 @@
import com.minecolonies.api.colony.IColonyManager;
import com.minecolonies.api.colony.colonyEvents.registry.ColonyEventTypeRegistryEntry;
import com.minecolonies.api.colony.managers.interfaces.IRaiderManager;
+import com.minecolonies.api.util.Log;
import com.minecolonies.api.util.constant.translation.CommandTranslationConstants;
import com.minecolonies.core.colony.events.raid.norsemenevent.NorsemenShipRaidEvent;
import com.minecolonies.core.colony.events.raid.pirateEvent.PirateGroundRaidEvent;
@@ -39,18 +40,28 @@ public int onExecute(final CommandContext context)
public int onSpecificExecute(final CommandContext context)
{
- if(!checkPreCondition(context))
+ try
{
- return 0;
+ if (!checkPreCondition(context))
+ {
+ return 0;
+ }
+ return raidExecute(context, StringArgumentType.getString(context, RAID_TYPE_ARG));
}
- return raidExecute(context, StringArgumentType.getString(context, RAID_TYPE_ARG));
+ catch (Throwable e)
+ {
+ Log.getLogger().warn("Error during running command:", e);
+ }
+
+ return 0;
}
/**
* Actually find the colony and assign the raid event.
- * @param context command context from the user.
- * @param raidType type of raid, or "" if determining naturally.
- * @return zero if failed, one if successful.
+ *
+ * @param context command context from the user.
+ * @param raidType type of raid, or "" if determining naturally.
+ * @return zero if failed, one if successful.
*/
public int raidExecute(final CommandContext context, final String raidType)
{
@@ -64,7 +75,7 @@ public int raidExecute(final CommandContext context, final S
}
final boolean allowShips = BoolArgumentType.getBool(context, SHIP_ARG);
- if(StringArgumentType.getString(context, RAID_TIME_ARG).equals(RAID_NOW))
+ if (StringArgumentType.getString(context, RAID_TIME_ARG).equals(RAID_NOW))
{
final IRaiderManager.RaidSpawnResult result = colony.getRaiderManager().raiderEvent(raidType, true, allowShips);
if (result == IRaiderManager.RaidSpawnResult.SUCCESS)
@@ -74,11 +85,13 @@ public int raidExecute(final CommandContext context, final S
}
context.getSource().sendFailure(Component.translatableEscape(CommandTranslationConstants.COMMAND_RAID_NOW_FAILURE, colony.getName(), result));
}
- else if(StringArgumentType.getString(context, RAID_TIME_ARG).equals(RAID_TONIGHT))
+ else if (StringArgumentType.getString(context, RAID_TIME_ARG).equals(RAID_TONIGHT))
{
if (!colony.getRaiderManager().canRaid(true))
{
- context.getSource().sendSuccess(() -> Component.translatableEscape(CommandTranslationConstants.COMMAND_RAID_NOW_FAILURE, colony.getName(), IRaiderManager.RaidSpawnResult.CANNOT_RAID), true);
+ context.getSource()
+ .sendSuccess(() -> Component.translatable(CommandTranslationConstants.COMMAND_RAID_NOW_FAILURE, colony.getName(), IRaiderManager.RaidSpawnResult.CANNOT_RAID),
+ true);
return 1;
}
colony.getRaiderManager().setRaidNextNight(true, raidType, allowShips);
@@ -100,10 +113,10 @@ public String getName()
public LiteralArgumentBuilder build()
{
final List raidTypes = new ArrayList<>();
- for(final ColonyEventTypeRegistryEntry type : IMinecoloniesAPI.getInstance().getColonyEventRegistry())
+ for (final ColonyEventTypeRegistryEntry type : IMinecoloniesAPI.getInstance().getColonyEventRegistry())
{
- if(!type.getRegistryName().getPath().equals(PirateGroundRaidEvent.PIRATE_GROUND_RAID_EVENT_TYPE_ID.getPath())
- && !type.getRegistryName().getPath().equals(NorsemenShipRaidEvent.NORSEMEN_RAID_EVENT_TYPE_ID.getPath()))
+ if (!type.getRegistryName().getPath().equals(PirateGroundRaidEvent.PIRATE_GROUND_RAID_EVENT_TYPE_ID.getPath())
+ && !type.getRegistryName().getPath().equals(NorsemenShipRaidEvent.NORSEMEN_RAID_EVENT_TYPE_ID.getPath()))
{
raidTypes.add(type.getRegistryName().getPath());
}
@@ -116,11 +129,11 @@ public LiteralArgumentBuilder build()
return IMCCommand.newLiteral(getName())
.then(IMCCommand.newArgument(RAID_TIME_ARG, StringArgumentType.string())
.suggests((ctx, builder) -> SharedSuggestionProvider.suggest(opt, builder))
- .then(IMCCommand.newArgument(COLONYID_ARG, IntegerArgumentType.integer(1))
- .then(IMCCommand.newArgument(RAID_TYPE_ARG, StringArgumentType.string())
- .suggests((ctx, builder) -> SharedSuggestionProvider.suggest(raidTypes, builder))
- .then(IMCCommand.newArgument(SHIP_ARG, BoolArgumentType.bool())
- .executes(this::onSpecificExecute)))
- .executes(this::checkPreConditionAndExecute)));
+ .then(IMCCommand.newArgument(COLONYID_ARG, IntegerArgumentType.integer(1))
+ .then(IMCCommand.newArgument(RAID_TYPE_ARG, StringArgumentType.string())
+ .suggests((ctx, builder) -> SharedSuggestionProvider.suggest(raidTypes, builder))
+ .then(IMCCommand.newArgument(SHIP_ARG, BoolArgumentType.bool())
+ .executes(this::onSpecificExecute)))
+ .executes(this::checkPreConditionAndExecute)));
}
}
diff --git a/src/main/java/com/minecolonies/core/commands/generalcommands/CommandRTP.java b/src/main/java/com/minecolonies/core/commands/generalcommands/CommandRTP.java
deleted file mode 100755
index ddd64cbb2d4..00000000000
--- a/src/main/java/com/minecolonies/core/commands/generalcommands/CommandRTP.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package com.minecolonies.core.commands.generalcommands;
-
-import com.minecolonies.api.colony.IColony;
-import com.minecolonies.api.colony.IColonyManager;
-import com.minecolonies.api.util.BlockPosUtil;
-import com.minecolonies.api.util.MessageUtils;
-import com.minecolonies.api.util.constant.translation.CommandTranslationConstants;
-import com.minecolonies.core.MineColonies;
-import com.minecolonies.core.commands.commandTypes.IMCCommand;
-import com.mojang.authlib.GameProfile;
-import com.mojang.brigadier.builder.LiteralArgumentBuilder;
-import com.mojang.brigadier.context.CommandContext;
-import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import net.minecraft.commands.CommandSourceStack;
-import net.minecraft.commands.arguments.GameProfileArgument;
-import net.minecraft.core.BlockPos;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.world.entity.Entity;
-import net.minecraft.world.entity.player.Player;
-import net.minecraft.world.level.Level;
-
-import java.util.Random;
-
-import static com.minecolonies.core.commands.CommandArgumentNames.PLAYERNAME_ARG;
-
-public class CommandRTP implements IMCCommand
-{
- private static final int STARTING_Y = 250;
- private static final double SAFETY_DROP = 6;
- private static final int FALL_DISTANCE = 5;
-
- /**
- * What happens when the command is executed
- *
- * @param context the context of the command execution
- */
- @Override
- public int onExecute(final CommandContext context)
- {
- rtp((Player) context.getSource().getEntity());
- return 1;
- }
-
- /**
- * Executes rtp for a target
- *
- * @param context the command context
- * @return 1 if the command executed successfully, 0 otherwise
- * @throws CommandSyntaxException if the syntax isn't correct
- */
- private int executeOtherPlayerRTP(final CommandContext context) throws CommandSyntaxException
- {
- final Entity sender = context.getSource().getEntity();
-
- if (!checkPreCondition(context) || !IMCCommand.isPlayerOped((Player) sender))
- {
- return 0;
- }
-
- GameProfile profile = GameProfileArgument.getGameProfiles(context, PLAYERNAME_ARG).stream().findFirst().orElse(null);
-
- if (profile == null || context.getSource().getServer().getPlayerList().getPlayer(profile.getId()) == null)
- {
- // could not find player with given name.
- MessageUtils.format(CommandTranslationConstants.COMMAND_PLAYER_NOT_FOUND, profile != null ? profile.getName() : "null").sendTo((Player) sender);
- return 0;
- }
-
- rtp(context.getSource().getServer().getPlayerList().getPlayer(profile.getId()));
- return 1;
- }
-
- @Override
- public boolean checkPreCondition(final CommandContext context)
- {
- final Entity sender = context.getSource().getEntity();
- if (!(sender instanceof Player))
- {
- return false;
- }
-
- if (!MineColonies.getConfig().getServer().canPlayerUseRTPCommand.get())
- {
- MessageUtils.format(CommandTranslationConstants.COMMAND_RTP_NOT_ALLOWED).sendTo((Player) sender);
- return false;
- }
- else if (!MineColonies.getConfig().getServer().allowOtherDimColonies.get() && context.getSource().getLevel().dimension() != Level.OVERWORLD)
- {
- MessageUtils.format(CommandTranslationConstants.COMMAND_RTP_WRONG_DIMENSION).sendTo((Player) sender);
- return false;
- }
- return true;
- }
-
- /**
- * Randomly teleports the given player.
- *
- * @param player player to teleport
- */
- private void rtp(final Player player)
- {
- //Now the position will be calculated, we will try up to 4 times to find a save position.
- for (int attCounter = 0; attCounter <= 4; attCounter++)
- {
- /* this math is to get negative numbers */
- final int x = getRandCoordinate();
- final int z = getRandCoordinate();
- final BlockPos spawnPoint = ((ServerLevel) player.getCommandSenderWorld()).getSharedSpawnPos();
- if (player.getCommandSenderWorld().getWorldBorder().getAbsoluteMaxSize()
- < BlockPosUtil.getDistance2D(spawnPoint, spawnPoint.offset(x, 0, z)))
- {
- continue;
- }
-
- final BlockPos tpPos = new BlockPos(x, STARTING_Y, z);
-
- final IColony colony = IColonyManager.getInstance().getClosestColony(player.getCommandSenderWorld(), tpPos);
- /* Check for a close by colony*/
- if (colony != null
- && BlockPosUtil.getDistance2D(colony.getCenter(), tpPos) < MineColonies.getConfig().getServer().maxColonySize.get() * 32)
- {
- continue;
- }
-
- /*Search for a ground position*/
- final BlockPos groundPosition = BlockPosUtil.findLand(tpPos, player.getCommandSenderWorld());
-
- /*If no position found*/
- if (groundPosition == null)
- {
- continue;
- }
-
- if (BlockPosUtil.isPositionSafe(player.getCommandSenderWorld(), groundPosition.below()))
- {
- player.setHealth(player.getMaxHealth());
- player.teleportTo(groundPosition.getX(), groundPosition.getY() + SAFETY_DROP, groundPosition.getZ());
- player.setHealth(player.getMaxHealth());
-
- MessageUtils.format(CommandTranslationConstants.COMMAND_RTP_SUCCESS).sendTo(player);
-
- //.fallDistance is used to cancel out fall damage basically if you have -5 it will reduce fall damage by 2.5 hearts
- player.fallDistance = -FALL_DISTANCE;
- return;
- }
- }
- MessageUtils.format(CommandTranslationConstants.COMMAND_RTP_NO_POSITION).sendTo(player);
- }
-
- /**
- * Get a random coordinate to teleport to.
- *
- * @return a random coordinate.
- */
- private static int getRandCoordinate()
- {
- final Random rnd = new Random();
- int x = 0;
-
- /* keeping X out of the spawn radius */
- while (x > -MineColonies.getConfig().getServer().minDistanceFromWorldSpawn.get() && x < MineColonies.getConfig().getServer().minDistanceFromWorldSpawn.get())
- {
- x = rnd.nextInt(MineColonies.getConfig().getServer().maxDistanceFromWorldSpawn.get() * 2) - MineColonies.getConfig().getServer().maxDistanceFromWorldSpawn.get();
- }
- return x;
- }
-
- /**
- * Name string of the command.
- *
- * @return this commands name.
- */
- @Override
- public String getName()
- {
- return "rtp";
- }
-
- @Override
- public LiteralArgumentBuilder build()
- {
- return IMCCommand.newLiteral(getName())
- .then(IMCCommand.newArgument(PLAYERNAME_ARG, GameProfileArgument.gameProfile()).executes(this::executeOtherPlayerRTP))
- .executes(this::checkPreConditionAndExecute);
- }
-}
diff --git a/src/main/java/com/minecolonies/core/compatibility/jei/FloristRecipeCategory.java b/src/main/java/com/minecolonies/core/compatibility/jei/FloristRecipeCategory.java
new file mode 100644
index 00000000000..74638936504
--- /dev/null
+++ b/src/main/java/com/minecolonies/core/compatibility/jei/FloristRecipeCategory.java
@@ -0,0 +1,164 @@
+package com.minecolonies.core.compatibility.jei;
+
+import com.minecolonies.api.blocks.ModBlocks;
+import com.minecolonies.api.colony.buildings.ModBuildings;
+import com.minecolonies.api.colony.jobs.ModJobs;
+import com.minecolonies.api.crafting.ItemStorage;
+import com.minecolonies.api.items.ModItems;
+import com.minecolonies.core.colony.buildings.workerbuildings.BuildingFlorist;
+import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
+import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
+import mezz.jei.api.helpers.IGuiHelper;
+import mezz.jei.api.recipe.IFocusGroup;
+import mezz.jei.api.recipe.RecipeIngredientRole;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.block.state.BlockState;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import static com.minecolonies.api.util.constant.Constants.MAX_BUILDING_LEVEL;
+import static com.minecolonies.api.util.constant.TranslationConstants.PARTIAL_JEI_INFO;
+
+/**
+ * JEI recipe category showing supported flowers.
+ */
+public class FloristRecipeCategory extends JobBasedRecipeCategory
+{
+ /**
+ * Constructor
+ */
+ public FloristRecipeCategory(@NotNull final IGuiHelper guiHelper)
+ {
+ super(ModJobs.florist.get().produceJob(null), ModRecipeTypes.FLOWERS,
+ new ItemStack(ModBuildings.florist.get().getBuildingBlock()), guiHelper);
+ }
+
+ private static final int LOOT_SLOTS_X = CITIZEN_X + CITIZEN_W + 4;
+ private static final int LOOT_SLOTS_W = WIDTH - LOOT_SLOTS_X;
+ private static final int MAX_LOOT_SLOTS = 24;
+
+ @NotNull
+ @Override
+ protected List generateInfoBlocks(@NotNull final FloristRecipeCategory.FloristRecipe recipe)
+ {
+ return Collections.singletonList(
+ Component.translatable(PARTIAL_JEI_INFO + "onelevelrestriction",
+ recipe.level()));
+ }
+
+ @Override
+ public void setRecipe(@NotNull final IRecipeLayoutBuilder builder,
+ @NotNull final FloristRecipeCategory.FloristRecipe recipe,
+ @NotNull final IFocusGroup focuses)
+ {
+ builder.addSlot(RecipeIngredientRole.CATALYST, WIDTH - 18, CITIZEN_Y - 20)
+ .setSlotName("compost")
+ .setBackground(this.slot, -1, -1)
+ .addItemStack(new ItemStack(ModItems.compost));
+
+ final int initialColumns = LOOT_SLOTS_W / this.slot.getWidth();
+ final int rows = (recipe.flowers().size() + initialColumns - 1) / initialColumns;
+ final int columns = (recipe.flowers().size() + rows - 1) / rows;
+ final int startX = LOOT_SLOTS_X + (LOOT_SLOTS_W - (columns * this.slot.getWidth())) / 2;
+ int x = startX;
+ int y = CITIZEN_Y + CITIZEN_H - rows * this.slot.getHeight() + 1;
+ int c = 0;
+
+ for (final List flowers : recipe.flowers())
+ {
+ builder.addSlot(RecipeIngredientRole.OUTPUT, x, y)
+ .setBackground(this.chanceSlot, -1, -1)
+ .addItemStacks(flowers);
+ if (++c >= columns)
+ {
+ c = 0;
+ x = startX;
+ y += this.slot.getHeight();
+ }
+ else
+ {
+ x += this.slot.getWidth();
+ }
+ }
+ }
+
+ @Override
+ public void draw(@NotNull final FloristRecipeCategory.FloristRecipe recipe,
+ @NotNull final IRecipeSlotsView recipeSlotsView,
+ @NotNull final GuiGraphics stack,
+ final double mouseX, final double mouseY)
+ {
+ super.draw(recipe, recipeSlotsView, stack, mouseX, mouseY);
+
+ final BlockState block = ModBlocks.blockCompostedDirt.defaultBlockState();
+ RenderHelper.renderBlock(stack, block, WIDTH - 38, CITIZEN_Y - 20, 100, -30F, 30F, 16F);
+ }
+
+ @NotNull
+ public static List findRecipes()
+ {
+ final List recipes = new ArrayList<>();
+
+ for (int level = 1; level <= MAX_BUILDING_LEVEL; ++level)
+ {
+ recipes.add(new FloristRecipe(level, compactify(BuildingFlorist.getPlantablesForBuildingLevel(level))));
+ }
+
+ return recipes;
+ }
+
+ /**
+ * Produces no more than MAX_LOOT_SLOTS lists of lists of items, to avoid overflowing the GUI with slots.
+ */
+ private static List> compactify(@NotNull final Set flowers)
+ {
+ final List> slots = new ArrayList<>();
+ final List flowerList = flowers.stream().map(ItemStorage::getItemStack).toList();
+
+ if (flowerList.size() < MAX_LOOT_SLOTS)
+ {
+ for (final ItemStack item : flowerList)
+ {
+ slots.add(List.of(item));
+ }
+ }
+ else
+ {
+ int itemsPerList = flowerList.size() / MAX_LOOT_SLOTS;
+ int extraItems = flowerList.size() % MAX_LOOT_SLOTS;
+ int currentIndex = 0;
+
+ for (int i = 0; i < MAX_LOOT_SLOTS; ++i)
+ {
+ final List sublist = new ArrayList<>();
+ for (int j = 0; j < itemsPerList; ++j)
+ {
+ sublist.add(flowerList.get(currentIndex++));
+ }
+ if (extraItems > 0)
+ {
+ sublist.add(flowerList.get(currentIndex++));
+ --extraItems;
+ }
+ slots.add(sublist);
+ }
+ }
+
+ return slots;
+ }
+
+ /**
+ * Represents the flowers available at the specified level.
+ * @param level the building level.
+ * @param flowers the flowers available at that level, grouped into display slots.
+ */
+ public record FloristRecipe(int level, @NotNull List> flowers)
+ {
+ }
+}
diff --git a/src/main/java/com/minecolonies/core/compatibility/jei/JEIPlugin.java b/src/main/java/com/minecolonies/core/compatibility/jei/JEIPlugin.java
index ae63f3733da..59f4f7053d9 100644
--- a/src/main/java/com/minecolonies/core/compatibility/jei/JEIPlugin.java
+++ b/src/main/java/com/minecolonies/core/compatibility/jei/JEIPlugin.java
@@ -56,7 +56,8 @@ public void registerCategories(@NotNull final IRecipeCategoryRegistration regist
new CropRecipeCategory(guiHelper),
new ToolRecipeCategory(guiHelper),
new CompostRecipeCategory(guiHelper),
- new FishermanRecipeCategory(guiHelper)
+ new FishermanRecipeCategory(guiHelper),
+ new FloristRecipeCategory(guiHelper)
);
categories.clear();
@@ -129,6 +130,7 @@ public void registerRecipes(@NotNull final IRecipeRegistration registration)
registration.addRecipes(ModRecipeTypes.CROPS, CropRecipeCategory.findRecipes());
registration.addRecipes(ModRecipeTypes.COMPOSTING, CompostRecipeCategory.findRecipes());
registration.addRecipes(ModRecipeTypes.FISHING, FishermanRecipeCategory.findRecipes());
+ registration.addRecipes(ModRecipeTypes.FLOWERS, FloristRecipeCategory.findRecipes());
final ClientLevel level = Objects.requireNonNull(Minecraft.getInstance().level);
final Map> vanilla = RecipeAnalyzer.buildVanillaRecipesMap(level.getRecipeManager(), level);
@@ -162,6 +164,7 @@ public void registerRecipeCatalysts(@NotNull final IRecipeCatalystRegistration r
registration.addRecipeCatalyst(ModBlocks.blockBarrel, ModRecipeTypes.COMPOSTING);
registration.addRecipeCatalyst(ModBlocks.blockHutComposter, ModRecipeTypes.COMPOSTING);
registration.addRecipeCatalyst(ModBlocks.blockHutFisherman, ModRecipeTypes.FISHING);
+ registration.addRecipeCatalyst(ModBlocks.blockHutFlorist, ModRecipeTypes.FLOWERS);
for (final JobBasedRecipeCategory> category : this.categories)
{
diff --git a/src/main/java/com/minecolonies/core/compatibility/jei/ModRecipeTypes.java b/src/main/java/com/minecolonies/core/compatibility/jei/ModRecipeTypes.java
index 269efb25009..82a0e008395 100644
--- a/src/main/java/com/minecolonies/core/compatibility/jei/ModRecipeTypes.java
+++ b/src/main/java/com/minecolonies/core/compatibility/jei/ModRecipeTypes.java
@@ -20,6 +20,9 @@ public class ModRecipeTypes
public static final RecipeType TOOLS =
RecipeType.create(MOD_ID, "tools", ToolUsage.class);
+ public static final RecipeType FLOWERS =
+ RecipeType.create(MOD_ID, "flowers", FloristRecipeCategory.FloristRecipe.class);
+
private ModRecipeTypes()
{
// purely static
diff --git a/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAIMournCitizen.java b/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAIMournCitizen.java
index 2e6d4bcd0af..5c5ac71880c 100755
--- a/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAIMournCitizen.java
+++ b/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAIMournCitizen.java
@@ -112,7 +112,16 @@ public EntityAIMournCitizen(final EntityCitizen citizen, final double speed)
private IState walkToTownHall()
{
final BlockPos pos = getMournLocation();
- citizen.getNavigation().moveToXYZ(pos.getX(), pos.getY(), pos.getZ(), this.speed);
+ if (pos == null)
+ {
+ return CitizenAIState.IDLE;
+ }
+
+ if (!citizen.isWorkerAtSiteWithMove(pos, 3))
+ {
+ return MourningState.WALKING_TO_TOWNHALL;
+ }
+
return CitizenAIState.IDLE;
}
@@ -287,9 +296,13 @@ private IState decide()
if (this.graveyard == null)
{
- this.graveyard =
+ final IBuilding graveyardBuilding =
citizen.getCitizenColonyHandler().getColony().getBuildingManager().getFirstBuildingMatching(b -> b instanceof BuildingGraveyard && b.getFirstModuleOccurance(
GraveyardManagementModule.class).hasRestingCitizen(citizen.getCitizenData().getCitizenMournHandler().getDeceasedCitizens()));
+ if (graveyardBuilding != null)
+ {
+ this.graveyard = graveyardBuilding.getPosition();
+ }
}
if (graveyard != null)
@@ -323,11 +336,11 @@ public void reset()
protected BlockPos getMournLocation()
{
final IColony colony = citizen.getCitizenColonyHandler().getColony();
- if (colony == null || !colony.getBuildingManager().hasTownHall())
+ if (colony != null && colony.getBuildingManager().hasTownHall())
{
- return citizen.getRestrictCenter();
+ return colony.getBuildingManager().getTownHall().getStandingPosition();
}
- return colony.getBuildingManager().getTownHall().getPosition();
+ return citizen.getCitizenData().getHomePosition();
}
}
diff --git a/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAISleep.java b/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAISleep.java
index bc36b0536ce..2af7e223b0e 100755
--- a/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAISleep.java
+++ b/src/main/java/com/minecolonies/core/entity/ai/minimal/EntityAISleep.java
@@ -13,6 +13,7 @@
import com.minecolonies.api.util.CompatibilityUtils;
import com.minecolonies.api.util.SoundUtils;
import com.minecolonies.api.util.WorldUtil;
+import com.minecolonies.core.colony.buildings.AbstractBuilding;
import com.minecolonies.core.colony.buildings.modules.BuildingModules;
import com.minecolonies.core.entity.citizen.EntityCitizen;
import com.minecolonies.core.network.messages.client.SleepingParticleMessage;
@@ -112,7 +113,7 @@ private IState walkHome()
final IBuilding homeBuilding = citizen.getCitizenData().getHomeBuilding();
if (homeBuilding == null)
{
- @Nullable final BlockPos homePosition = citizen.getRestrictCenter();
+ @Nullable final BlockPos homePosition = citizen.getCitizenData().getHomePosition();
if (homePosition.distSqr(BlockPos.containing(Math.floor(citizen.getX()), citizen.getY(), Math.floor(citizen.getZ()))) <= RANGE_TO_BE_HOME)
{
return FIND_BED;
@@ -164,11 +165,11 @@ private void findBedAndTryToSleep()
}
final IColony colony = citizen.getCitizenColonyHandler().getColony();
- if (colony != null && colony.getBuildingManager().getBuilding(citizen.getRestrictCenter()) != null)
+ if (colony != null && citizen.getCitizenData().getHomeBuilding() instanceof AbstractBuilding hut)
{
+ final BlockPos homePos = citizen.getCitizenData().getHomePosition();
if (usedBed == null)
{
- final IBuilding hut = colony.getBuildingManager().getBuilding(citizen.getRestrictCenter());
List bedList = new ArrayList<>();
if (hut.hasModule(BuildingModules.BED))
@@ -196,7 +197,7 @@ private void findBedAndTryToSleep()
}
}
- usedBed = citizen.getRestrictCenter();
+ usedBed = homePos;
}
if (citizen.isWorkerAtSiteWithMove(usedBed, 3))
@@ -236,8 +237,8 @@ private IState sleep()
*/
private void goHome()
{
- final BlockPos pos = citizen.getCitizenSleepHandler().findHomePos();
- if (!citizen.isWorkerAtSiteWithMove(pos, 2) && citizen.getPose() == Pose.SLEEPING)
+ final BlockPos pos = citizen.getCitizenData().getHomePosition();
+ if (pos != null && !citizen.isWorkerAtSiteWithMove(pos, 2) && citizen.getPose() == Pose.SLEEPING)
{
citizen.setPose(Pose.STANDING);
}
diff --git a/src/main/java/com/minecolonies/core/entity/ai/workers/AbstractEntityAIBasic.java b/src/main/java/com/minecolonies/core/entity/ai/workers/AbstractEntityAIBasic.java
index f3650ee2ad4..56ccecf93f5 100755
--- a/src/main/java/com/minecolonies/core/entity/ai/workers/AbstractEntityAIBasic.java
+++ b/src/main/java/com/minecolonies/core/entity/ai/workers/AbstractEntityAIBasic.java
@@ -3,6 +3,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
+import com.ldtteam.structurize.util.BlockUtils;
import com.minecolonies.api.colony.ICitizenData;
import com.minecolonies.api.colony.buildings.IBuilding;
import com.minecolonies.api.colony.interactionhandling.ChatPriority;
@@ -11,10 +12,10 @@
import com.minecolonies.api.colony.requestsystem.location.ILocation;
import com.minecolonies.api.colony.requestsystem.request.IRequest;
import com.minecolonies.api.colony.requestsystem.request.RequestState;
-import com.minecolonies.api.colony.requestsystem.requestable.Tool;
import com.minecolonies.api.colony.requestsystem.requestable.IDeliverable;
import com.minecolonies.api.colony.requestsystem.requestable.RequestTag;
import com.minecolonies.api.colony.requestsystem.requestable.Stack;
+import com.minecolonies.api.colony.requestsystem.requestable.Tool;
import com.minecolonies.api.colony.requestsystem.resolver.IRequestResolver;
import com.minecolonies.api.crafting.ItemStorage;
import com.minecolonies.api.entity.ai.statemachine.AIEventTarget;
@@ -43,11 +44,11 @@
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
-import net.minecraft.tags.BlockTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
@@ -775,9 +776,17 @@ public int getTotalRequiredAmount(final ItemStack deliveredItemStack)
protected final boolean walkToBuilding()
{
@Nullable final IBuilding ownBuilding = building;
- //Return true if the building is null to stall the worker
- return ownBuilding == null
- || walkToBlock(ownBuilding.getPosition());
+ if (ownBuilding == null)
+ {
+ return true;
+ }
+ final BlockPos standingPos = ownBuilding.getStandingPosition();
+ int range = 1;
+ if (standingPos.equals(ownBuilding.getPosition()))
+ {
+ range = 3;
+ }
+ return walkToBlock(ownBuilding.getStandingPosition(), range);
}
/**
@@ -1468,28 +1477,9 @@ public BlockPos getWorkingPosition(final BlockPos targetPosition)
*/
public BlockPos getWorkingPosition(final int distance, final BlockPos targetPos, final int offset)
{
- if (offset > MAX_ADDITIONAL_RANGE_TO_BUILD)
- {
- return targetPos;
- }
-
- // TODO: Use pathfinding for this instead? Or find around Util
-
- @NotNull final Direction[] directions = {Direction.EAST, Direction.WEST, Direction.NORTH, Direction.SOUTH};
-
- //then get a solid place with two air spaces above it in any direction.
- for (final Direction direction : directions)
- {
- @NotNull final BlockPos positionInDirection = getPositionInDirection(direction, distance + offset, targetPos);
- if (EntityUtils.checkForFreeSpace(world, positionInDirection)
- && world.getBlockState(positionInDirection.above()).is(BlockTags.SAPLINGS))
- {
- return positionInDirection;
- }
- }
-
- //if necessary we call it recursively and add some "offset" to the sides.
- return getWorkingPosition(distance, targetPos, offset + 1);
+ // TODO: Use pathfinding for this instead! Get rid of all those getWork position stuff
+ final BlockPos workPos = BlockPosUtil.findSpawnPosAround(world, targetPos);
+ return workPos == null ? targetPos : workPos;
}
/**
diff --git a/src/main/java/com/minecolonies/core/entity/ai/workers/production/agriculture/EntityAIWorkFisherman.java b/src/main/java/com/minecolonies/core/entity/ai/workers/production/agriculture/EntityAIWorkFisherman.java
index e1139a64248..56c22b61fc6 100755
--- a/src/main/java/com/minecolonies/core/entity/ai/workers/production/agriculture/EntityAIWorkFisherman.java
+++ b/src/main/java/com/minecolonies/core/entity/ai/workers/production/agriculture/EntityAIWorkFisherman.java
@@ -7,18 +7,19 @@
import com.minecolonies.api.entity.ai.statemachine.states.IAIState;
import com.minecolonies.api.entity.citizen.AbstractEntityCitizen;
import com.minecolonies.api.equipment.ModEquipmentTypes;
-import com.minecolonies.core.entity.pathfinding.Pathfinding;
-import com.minecolonies.core.entity.pathfinding.PathfindingUtils;
-import com.minecolonies.core.entity.pathfinding.pathjobs.PathJobFindWater;
-import com.minecolonies.core.entity.pathfinding.pathresults.WaterPathResult;
import com.minecolonies.api.loot.ModLootTables;
import com.minecolonies.api.sounds.EventType;
import com.minecolonies.api.util.*;
import com.minecolonies.core.colony.buildings.workerbuildings.BuildingFisherman;
import com.minecolonies.core.colony.interactionhandling.StandardInteraction;
import com.minecolonies.core.colony.jobs.JobFisherman;
-import com.minecolonies.core.entity.other.NewBobberEntity;
import com.minecolonies.core.entity.ai.workers.AbstractEntityAISkill;
+import com.minecolonies.core.entity.citizen.EntityCitizen;
+import com.minecolonies.core.entity.other.NewBobberEntity;
+import com.minecolonies.core.entity.pathfinding.Pathfinding;
+import com.minecolonies.core.entity.pathfinding.PathfindingUtils;
+import com.minecolonies.core.entity.pathfinding.pathjobs.PathJobFindWater;
+import com.minecolonies.core.entity.pathfinding.pathresults.WaterPathResult;
import com.minecolonies.core.util.WorkerUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
@@ -44,8 +45,8 @@
import static com.minecolonies.api.entity.ai.statemachine.states.AIWorkerState.*;
import static com.minecolonies.api.util.constant.Constants.TICKS_SECOND;
-import static com.minecolonies.api.util.constant.StatisticsConstants.FISH_CAUGHT;
import static com.minecolonies.api.util.constant.EquipmentLevelConstants.TOOL_LEVEL_WOOD_OR_GOLD;
+import static com.minecolonies.api.util.constant.StatisticsConstants.FISH_CAUGHT;
import static com.minecolonies.api.util.constant.TranslationConstants.WATER_TOO_FAR;
import static com.minecolonies.core.colony.buildings.modules.BuildingModules.STATS_MODULE;
import static com.minecolonies.core.entity.other.NewBobberEntity.XP_PER_CATCH;
@@ -415,10 +416,6 @@ private IAIState findNewWater()
pathResult = searchWater(SEARCH_RANGE * 3, 1.0D, job.getPonds());
return getState();
}
- if (pathResult.isDone())
- {
- pathResult.getJob().syncDebug();
- }
if (pathResult.failedToReachDestination())
{
return setRandomWater();
diff --git a/src/main/java/com/minecolonies/core/entity/citizen/EntityCitizen.java b/src/main/java/com/minecolonies/core/entity/citizen/EntityCitizen.java
index 3972f9179e4..f98efea8650 100755
--- a/src/main/java/com/minecolonies/core/entity/citizen/EntityCitizen.java
+++ b/src/main/java/com/minecolonies/core/entity/citizen/EntityCitizen.java
@@ -229,11 +229,6 @@ public class EntityCitizen extends AbstractEntityCitizen implements IThreatTable
*/
private ILocation location = null;
- /**
- * Cached team name the entity belongs to.
- */
- private String cachedTeamName;
-
/**
* The current chunkpos.
*/
@@ -245,11 +240,6 @@ public class EntityCitizen extends AbstractEntityCitizen implements IThreatTable
private final ThreatTable threatTable = new ThreatTable<>(this);
private int interactionCooldown = 0;
- /**
- * Cache the entire team object.
- */
- private PlayerTeam cachedTeam;
-
/**
* The citizen AI
*/
@@ -337,7 +327,6 @@ private EntityState initialize()
final IColonyView colonyView = IColonyManager.getInstance().getColonyView(citizenColonyHandler.getColonyId(), level().dimension());
if (colonyView != null)
{
- this.cachedTeamName = colonyView.getTeamName();
this.citizenDataView = colonyView.getCitizen(citizenId);
if (citizenDataView != null)
{
@@ -372,7 +361,7 @@ private void initTasks()
int priority = 0;
this.goalSelector.addGoal(priority, new EntityAIFloat(this));
this.goalSelector.addGoal(priority, new EntityAIInteractToggleAble(this, FENCE_TOGGLE, TRAP_TOGGLE, DOOR_TOGGLE));
- this.goalSelector.addGoal(++priority, new LookAtEntityInteractGoal(this, Player.class, WATCH_CLOSEST2, 1.0F));
+ this.goalSelector.addGoal(++priority, new LookAtEntityInteractGoal(this, Player.class, WATCH_CLOSEST2, 0.2F));
this.goalSelector.addGoal(++priority, new LookAtEntityInteractGoal(this, EntityCitizen.class, WATCH_CLOSEST2_FAR, WATCH_CLOSEST2_FAR_CHANCE));
this.goalSelector.addGoal(++priority, new LookAtEntityGoal(this, LivingEntity.class, WATCH_CLOSEST));
}
@@ -633,6 +622,7 @@ private void eatFoodInteraction(final ItemStack usedStack, final Player player,
}
@Override
+ @NotNull
public String getScoreboardName()
{
return getName().getString() + " (" + getCivilianID() + ")";
@@ -723,6 +713,7 @@ public boolean refreshCitizenDataView()
if (colonyView != null)
{
this.citizenDataView = colonyView.getCitizen(citizenId);
+ // TODO: Why is this here on clientside?
this.getNavigation().getPathingOptions().setCanUseRails(canPathOnRails());
this.getNavigation().getPathingOptions().setCanClimbAdvanced(canClimbVines());
}
@@ -1795,31 +1786,6 @@ public void setRemoved(final RemovalReason reason)
super.setRemoved(reason);
}
- @Override
- public PlayerTeam getTeam()
- {
- if (level() == null || (level().isClientSide && cachedTeamName == null))
- {
- return null;
- }
-
- if (cachedTeam != null)
- {
- return cachedTeam;
- }
-
- if (level().isClientSide)
- {
- cachedTeam = level().getScoreboard().getPlayerTeam(this.cachedTeamName);
- }
- else
- {
- cachedTeam = level().getScoreboard().getPlayerTeam(getScoreboardName());
- }
-
- return cachedTeam;
- }
-
@Override
public void setCustomName(@Nullable final Component name)
{
@@ -1850,28 +1816,6 @@ public boolean requiresCustomPersistence()
return true;
}
- /**
- * Returns the home position of each citizen (His house or town hall).
- *
- * @return location
- */
- @NotNull
- @Override
- public BlockPos getRestrictCenter()
- {
- @Nullable final IBuilding homeBuilding = citizenColonyHandler.getHomeBuilding();
- if (homeBuilding != null)
- {
- return homeBuilding.getPosition();
- }
- else if (citizenColonyHandler.getColony() != null && citizenColonyHandler.getColony().getBuildingManager().getTownHall() != null)
- {
- return citizenColonyHandler.getColony().getBuildingManager().getTownHall().getPosition();
- }
-
- return super.getRestrictCenter();
- }
-
@Nullable
@Override
public AbstractContainerMenu createMenu(final int id, @NotNull final Inventory inv, @NotNull final Player player)
diff --git a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenColonyHandler.java b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenColonyHandler.java
index 0a83b1f6074..0b11c7d0afe 100755
--- a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenColonyHandler.java
+++ b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenColonyHandler.java
@@ -9,10 +9,13 @@
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.scores.PlayerTeam;
import org.jetbrains.annotations.Nullable;
import static com.minecolonies.api.entity.citizen.AbstractEntityCitizen.*;
import static com.minecolonies.api.util.constant.CitizenConstants.SATURATION_DECREASE_FACTOR;
+import static com.minecolonies.core.util.TeamUtils.checkOrCreateTeam;
/**
* Handles all colony related methods for the citizen.
@@ -233,4 +236,10 @@ public void onCitizenRemoved()
citizen.getCitizenData().setLastPosition(citizen.blockPosition());
}
}
+
+ @Override
+ public PlayerTeam getTeam(final Level level)
+ {
+ return checkOrCreateTeam(level, IColony.getTeamName(level, colonyId), false);
+ }
}
diff --git a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSkillHandler.java b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSkillHandler.java
index 91d70be5cf2..ca38135e183 100755
--- a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSkillHandler.java
+++ b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSkillHandler.java
@@ -36,6 +36,14 @@ public class CitizenSkillHandler implements ICitizenSkillHandler
*/
public Map skillMap = new EnumMap<>(Skill.class);
+ public CitizenSkillHandler()
+ {
+ for (final Skill skill : Skill.values())
+ {
+ skillMap.put(skill, new SkillData(1, 0.0D));
+ }
+ }
+
@Override
public void init(final int levelCap)
{
diff --git a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSleepHandler.java b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSleepHandler.java
index cd9841c3919..eef56425549 100755
--- a/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSleepHandler.java
+++ b/src/main/java/com/minecolonies/core/entity/citizen/citizenhandlers/CitizenSleepHandler.java
@@ -216,23 +216,6 @@ private void spawnCitizenFromBed()
citizen.getEntityData().set(DATA_BED_POS, new BlockPos(0, 0, 0));
}
- @Override
- public BlockPos findHomePos()
- {
- final BlockPos pos = citizen.getRestrictCenter();
- if (pos.equals(BlockPos.ZERO))
- {
- if (citizen.getCitizenColonyHandler().getColony().hasTownHall())
- {
- return citizen.getCitizenColonyHandler().getColony().getBuildingManager().getTownHall().getPosition();
- }
-
- return citizen.getCitizenColonyHandler().getColony().getCenter();
- }
-
- return pos;
- }
-
/**
* Get the bed location of the citizen.
*
@@ -247,8 +230,12 @@ public BlockPos getBedLocation()
@Override
public boolean shouldGoSleep()
{
- final BlockPos homePos = findHomePos();
+ final BlockPos homePos = citizen.getCitizenData().getHomePosition();
BlockPos citizenPos = citizen.blockPosition();
+ if (homePos == null)
+ {
+ return false;
+ }
int additionalDist = 0;
diff --git a/src/main/java/com/minecolonies/core/entity/mobs/EntityMercenary.java b/src/main/java/com/minecolonies/core/entity/mobs/EntityMercenary.java
index 81f4ad9f69b..cbc48c2c538 100755
--- a/src/main/java/com/minecolonies/core/entity/mobs/EntityMercenary.java
+++ b/src/main/java/com/minecolonies/core/entity/mobs/EntityMercenary.java
@@ -14,29 +14,40 @@
import com.minecolonies.api.sounds.MercenarySounds;
import com.minecolonies.core.entity.ai.minimal.EntityAIInteractToggleAble;
import com.minecolonies.core.entity.citizen.EntityCitizen;
+import com.minecolonies.core.entity.pathfinding.navigation.AbstractAdvancedPathNavigate;
+import com.minecolonies.core.entity.pathfinding.navigation.MinecoloniesAdvancedPathNavigate;
import com.minecolonies.core.entity.pathfinding.proxy.GeneralEntityWalkToProxy;
import com.minecolonies.core.entity.pathfinding.navigation.MinecoloniesAdvancedPathNavigate;
+import net.minecraft.core.BlockPos;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.chat.Component;
+import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.Tuple;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
-import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.goal.FloatGoal;
+import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
+import net.minecraft.world.entity.animal.horse.Llama;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.monster.Monster;
-import net.minecraft.world.entity.animal.horse.Llama;
+import net.minecraft.world.entity.npc.Npc;
import net.minecraft.world.entity.player.Player;
-import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
-import net.minecraft.nbt.CompoundTag;
-import net.minecraft.util.*;
-import net.minecraft.core.BlockPos;
-import net.minecraft.network.chat.Component;
-import net.minecraft.world.level.LevelAccessor;
+import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.Level;
+import net.minecraft.world.level.LevelAccessor;
+import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
+import net.minecraft.world.scores.PlayerTeam;
import net.neoforged.neoforge.items.IItemHandler;
import org.jetbrains.annotations.NotNull;
@@ -53,14 +64,6 @@
import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INFO_COLONY_MERCENARY_STEAL_CITIZEN;
import static com.minecolonies.core.entity.ai.minimal.EntityAIInteractToggleAble.*;
-import net.minecraft.sounds.SoundEvent;
-import net.minecraft.world.InteractionHand;
-import net.minecraft.world.damagesource.DamageSource;
-import net.minecraft.world.entity.Entity;
-import net.minecraft.world.entity.EntityType;
-import net.minecraft.world.entity.LivingEntity;
-import net.minecraft.world.entity.npc.Npc;
-
/**
* Class for Mercenary entities, which can be spawned to protect the colony
@@ -558,4 +561,11 @@ private static boolean isValidSpawnForMercenaries(final LevelAccessor world, fin
}
return true;
}
+
+ @Override
+ @Nullable
+ protected PlayerTeam getAssignedTeam()
+ {
+ return colony.getTeam();
+ }
}
diff --git a/src/main/java/com/minecolonies/core/entity/mobs/aitasks/RaiderWalkAI.java b/src/main/java/com/minecolonies/core/entity/mobs/aitasks/RaiderWalkAI.java
index d8a8780da8f..fba18ab5653 100644
--- a/src/main/java/com/minecolonies/core/entity/mobs/aitasks/RaiderWalkAI.java
+++ b/src/main/java/com/minecolonies/core/entity/mobs/aitasks/RaiderWalkAI.java
@@ -10,12 +10,12 @@
import com.minecolonies.api.entity.ai.statemachine.tickratestatemachine.TickingTransition;
import com.minecolonies.api.entity.mobs.AbstractEntityRaiderMob;
import com.minecolonies.api.entity.pathfinding.IPathJob;
-import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
import com.minecolonies.api.util.BlockPosUtil;
import com.minecolonies.api.util.Log;
import com.minecolonies.core.colony.buildings.AbstractBuilding;
import com.minecolonies.core.colony.events.raid.HordeRaidEvent;
import com.minecolonies.core.colony.events.raid.pirateEvent.ShipBasedRaiderUtils;
+import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
import net.minecraft.core.BlockPos;
import java.util.List;
@@ -156,7 +156,10 @@ protected BlockPos findRandomPositionToWalkTo()
&& !building.getCorners().getA().equals(building.getCorners().getB()))
{
randomPathResult = raider.getNavigation().moveToRandomPos(10, 0.9, building.getCorners());
- randomPathResult.getJob().getPathingOptions().withCanEnterDoors(true).withToggleCost(0).withNonLadderClimbableCost(0);
+ if (randomPathResult != null)
+ {
+ randomPathResult.getJob().getPathingOptions().withCanEnterDoors(true).withToggleCost(0).withNonLadderClimbableCost(0);
+ }
}
else
{
diff --git a/src/main/java/com/minecolonies/core/entity/pathfinding/PathfindingUtils.java b/src/main/java/com/minecolonies/core/entity/pathfinding/PathfindingUtils.java
index edfb59176b0..df10baba2a3 100644
--- a/src/main/java/com/minecolonies/core/entity/pathfinding/PathfindingUtils.java
+++ b/src/main/java/com/minecolonies/core/entity/pathfinding/PathfindingUtils.java
@@ -12,7 +12,6 @@
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
-import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
@@ -28,9 +27,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class PathfindingUtils
@@ -41,33 +38,33 @@ public class PathfindingUtils
private static Object empty = Fluids.EMPTY.defaultFluidState();
/**
- * Which citizens are being tracked by which players.
+ * Which citizens are being tracked by which players. Player to entity uuid
*/
public static final Map trackingMap = new ConcurrentHashMap<>();
+ /**
+ * Map for tracking specific path types, type to player uuid
+ */
+ public static final Map trackByType = new HashMap<>();
+
/**
* Set the set of reached blocks to the client.
*
* @param reached the reached blocks.
* @param mob the tracked mob.
*/
- public static void syncDebugReachedPositions(final HashSet reached, final Mob mob)
+ public static void syncDebugReachedPositions(final HashSet reached, final List players)
{
- if (reached.isEmpty())
+ if (reached.isEmpty() || players.isEmpty())
{
return;
}
- for (final Map.Entry entry : trackingMap.entrySet())
+ final SyncPathReachedMessage message = new SyncPathReachedMessage(reached);
+
+ for (final ServerPlayer player : players)
{
- if (entry.getValue().equals(mob.getUUID()))
- {
- final ServerPlayer player = mob.level().getServer().getPlayerList().getPlayer(entry.getKey());
- if (player != null)
- {
- new SyncPathReachedMessage(reached).sendToPlayer(player);
- }
- }
+ message.sendToPlayer(player);
}
}
@@ -327,8 +324,8 @@ public static boolean isLadder(final BlockState blockState, @Nullable final Path
return true;
}
return blockState.is(BlockTags.CLIMBABLE) && ((options != null && options.canClimbAdvanced()) ||
- blockState.getBlock() instanceof LadderBlock ||
- blockState.is(ModTags.freeClimbBlocks));
+ blockState.getBlock() instanceof LadderBlock ||
+ blockState.is(ModTags.freeClimbBlocks));
}
/**
diff --git a/src/main/java/com/minecolonies/core/entity/pathfinding/navigation/MinecoloniesAdvancedPathNavigate.java b/src/main/java/com/minecolonies/core/entity/pathfinding/navigation/MinecoloniesAdvancedPathNavigate.java
index 7e7df618649..c368c30c8f1 100644
--- a/src/main/java/com/minecolonies/core/entity/pathfinding/navigation/MinecoloniesAdvancedPathNavigate.java
+++ b/src/main/java/com/minecolonies/core/entity/pathfinding/navigation/MinecoloniesAdvancedPathNavigate.java
@@ -18,7 +18,6 @@
import com.minecolonies.core.util.WorkerUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
-import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
@@ -26,8 +25,7 @@
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
-import net.minecraft.world.level.block.BaseRailBlock;
-import net.minecraft.world.level.block.LadderBlock;
+import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.RailShape;
import net.minecraft.world.level.pathfinder.Node;
@@ -111,6 +109,11 @@ public class MinecoloniesAdvancedPathNavigate extends AbstractAdvancedPathNaviga
*/
private BlockPos.MutableBlockPos tempPos = new BlockPos.MutableBlockPos();
+ /**
+ * wanted position for movecontrol
+ */
+ private Vec3 wantedPosition = null;
+
/**
* Instantiates the navigation of an ourEntity.
*
@@ -366,21 +369,24 @@ else if (this.path != null && !this.path.isDone())
}
}
- DebugPackets.sendPathFindingPacket(this.level, this.mob, this.path, this.maxDistanceToWaypoint);
-
- if (!this.isDone())
+ if (this.path != null && !this.path.isDone())
{
- Vec3 vector3d2 = path.getNextEntityPos(mob);
- tempPos.set(Mth.floor(vector3d2.x), Mth.floor(vector3d2.y), Mth.floor(vector3d2.z));
- if (ChunkPos.asLong(tempPos) == mob.chunkPosition().toLong() || WorldUtil.isEntityBlockLoaded(level, tempPos))
+ if ((wantedPosition == null || currentPathIndex != path.getNextNodeIndex() && path.getNextNodeIndex() < path.getNodeCount()))
{
- mob.getMoveControl()
- .setWantedPosition(vector3d2.x,
- getSmartGroundY(this.level, tempPos, vector3d2.y),
- vector3d2.z,
- speedModifier);
+ Vec3 vector3d2 = path.getNextEntityPos(mob);
+ tempPos.set(Mth.floor(vector3d2.x), Mth.floor(vector3d2.y), Mth.floor(vector3d2.z));
+ if (wantedPosition == null || ChunkPos.asLong(tempPos) == mob.chunkPosition().toLong() || WorldUtil.isEntityBlockLoaded(level, tempPos))
+ {
+ wantedPosition = new Vec3(vector3d2.x,
+ getSmartGroundY(this.level, tempPos, vector3d2.y),
+ vector3d2.z);
+ }
}
}
+ if (wantedPosition != null)
+ {
+ mob.getMoveControl().setWantedPosition(wantedPosition.x, wantedPosition.y, wantedPosition.z, speedModifier);
+ }
}
// End of super.tick.
@@ -398,7 +404,7 @@ else if (this.path != null && !this.path.isDone())
*
* @param world the world.
* @param pos the position to check.
- * @param y
+ * @param orgY original y level
* @return the next y level to go to.
*/
public static double getSmartGroundY(final BlockGetter world, final BlockPos.MutableBlockPos pos, final double orgY)
@@ -407,6 +413,11 @@ public static double getSmartGroundY(final BlockGetter world, final BlockPos.Mut
if (!state.isAir())
{
+ if (state.getBlock() instanceof FenceGateBlock || state.getBlock() instanceof DoorBlock || state.getBlock() instanceof TrapDoorBlock)
+ {
+ return orgY;
+ }
+
final VoxelShape voxelshape = state.getCollisionShape(world, pos);
if (!ShapeUtil.isEmpty(voxelshape))
{
@@ -657,7 +668,6 @@ private void processCompletedCalculationResult()
return;
}
- pathResult.getJob().syncDebug();
moveTo(pathResult.getPath(), getSpeedFactor());
if (pathResult != null)
{
@@ -1014,7 +1024,7 @@ protected void followThePath()
if (isTracking)
{
- PathfindingUtils.syncDebugReachedPositions(reached, ourEntity);
+ PathfindingUtils.syncDebugReachedPositions(reached, pathResult.getDebugWatchers());
reached.clear();
}
@@ -1058,7 +1068,7 @@ else if (isTracking)
if (isTracking)
{
- PathfindingUtils.syncDebugReachedPositions(reached, ourEntity);
+ PathfindingUtils.syncDebugReachedPositions(reached, pathResult.getDebugWatchers());
reached.clear();
}
}
diff --git a/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/AbstractPathJob.java b/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/AbstractPathJob.java
index 76760d97607..930d678a9ba 100644
--- a/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/AbstractPathJob.java
+++ b/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/AbstractPathJob.java
@@ -67,6 +67,11 @@ public abstract class AbstractPathJob implements Callable, IPathJob
@NotNull
protected final LevelReader world;
+ /**
+ * The original world, do not use offthread
+ */
+ private final Level actualWorld;
+
/**
* The entity this job belongs to, can be none
*/
@@ -106,8 +111,8 @@ public abstract class AbstractPathJob implements Callable, IPathJob
/**
* Counts of nodes
*/
- private int totalNodesAdded = 0;
- private int totalNodesVisited = 0;
+ private int totalNodesAdded = 0;
+ protected int totalNodesVisited = 0;
/**
* Additional nodes that get explored when reaching the target, useful when the destination is an area or not in a great spot.
@@ -175,6 +180,7 @@ public AbstractPathJob(final Level world, @NotNull final BlockPos start, int ran
final int maxX = (int) (start.getX() + range * 1.3);
final int maxZ = (int) (start.getZ() + range * 1.3);
this.world = new ChunkCache(world, new BlockPos(minX, 0, minZ), new BlockPos(maxX, 0, maxZ));
+ this.actualWorld = world;
this.maxNodes = Math.min(MAX_NODES, range * range);
nodesToVisit = new PriorityQueue<>(range * 2);
@@ -186,11 +192,6 @@ public AbstractPathJob(final Level world, @NotNull final BlockPos start, int ran
result.setJob(this);
this.entity = entity;
-
- if (entity != null && PathfindingUtils.trackingMap.containsValue(entity.getUUID()))
- {
- initDebug();
- }
}
/**
@@ -202,7 +203,7 @@ public AbstractPathJob(final Level world, @NotNull final BlockPos start, int ran
* @param result
* @param entity
*/
- protected AbstractPathJob(final LevelReader chunkCache, @NotNull final BlockPos start, int range, final PathResult result, @Nullable final Mob entity)
+ protected AbstractPathJob(final Level actualWorld, final LevelReader chunkCache, @NotNull final BlockPos start, int range, final PathResult result, @Nullable final Mob entity)
{
range = Math.max(10, range);
this.maxNodes = Math.min(MAX_NODES, range * range);
@@ -211,6 +212,7 @@ protected AbstractPathJob(final LevelReader chunkCache, @NotNull final BlockPos
world = chunkCache;
cachedBlockLookup = new CachingBlockLookup(start, this.world);
+ this.actualWorld = actualWorld;
this.result = result;
result.setJob(this);
@@ -249,14 +251,10 @@ public AbstractPathJob(final Level world, @NotNull final BlockPos start, @NotNul
this.start = new BlockPos(start);
cachedBlockLookup = new CachingBlockLookup(start, this.world);
+ actualWorld = world;
this.result = result;
result.setJob(this);
-
- if (entity != null && PathfindingUtils.trackingMap.containsValue(entity.getUUID()))
- {
- initDebug();
- }
this.entity = entity;
}
@@ -1251,7 +1249,7 @@ protected boolean isPassable(@NotNull final BlockState block, final int x, final
if (!block.isAir())
{
final VoxelShape shape = block.getCollisionShape(world, tempWorldPos.set(x, y, z));
- if (ShapeUtil.max(shape, Direction.Axis.Y) < 0.5 && PathfindingUtils.isDangerous(cachedBlockLookup.getBlockState(x, y - 1, z)))
+ if (!pathingOptions.canPassDanger() && ShapeUtil.max(shape, Direction.Axis.Y) < 0.5 && PathfindingUtils.isDangerous(cachedBlockLookup.getBlockState(x, y - 1, z)))
{
return false;
}
@@ -1315,7 +1313,7 @@ protected boolean isPassable(@NotNull final BlockState block, final int x, final
|| !block.getBlock().properties().hasCollision;
}
}
- else if (PathfindingUtils.isDangerous(block))
+ else if (!pathingOptions.canPassDanger() && PathfindingUtils.isDangerous(block))
{
return false;
}
@@ -1585,23 +1583,26 @@ private boolean calculateSwimming(final BlockState below, final BlockState state
return node.isSwimming();
}
- return PathfindingUtils.isWater(cachedBlockLookup, null, below,null)
- || PathfindingUtils.isWater(cachedBlockLookup, null, state,null)
- || PathfindingUtils.isWater(cachedBlockLookup, null, above,null);
+ return PathfindingUtils.isWater(cachedBlockLookup, null, below, null)
+ || PathfindingUtils.isWater(cachedBlockLookup, null, state, null)
+ || PathfindingUtils.isWater(cachedBlockLookup, null, above, null);
}
/**
* Initializes debug tracking
*/
- private void initDebug()
+ public void initDebug()
{
- debugDrawEnabled = true;
- debugNodesVisited = new HashSet<>();
- debugNodesVisitedLater = new HashSet<>();
- debugNodesNotVisited = new HashSet<>();
- debugNodesPath = new HashSet<>();
- debugNodesOrgPath = new HashSet<>();
- debugNodesExtra = new HashSet<>();
+ if (!debugDrawEnabled)
+ {
+ debugDrawEnabled = true;
+ debugNodesVisited = new HashSet<>();
+ debugNodesVisitedLater = new HashSet<>();
+ debugNodesNotVisited = new HashSet<>();
+ debugNodesPath = new HashSet<>();
+ debugNodesOrgPath = new HashSet<>();
+ debugNodesExtra = new HashSet<>();
+ }
}
/**
@@ -1713,21 +1714,20 @@ private void addPathNodeToDebug(final MNode node)
/**
* Sync the path to the client.
*/
- public void syncDebug()
+ public void syncDebug(final List debugWatchers)
{
- if (debugDrawEnabled && entity != null)
+ if (debugDrawEnabled)
{
- for (final Iterator> iter = PathfindingUtils.trackingMap.entrySet().iterator(); iter.hasNext(); )
+ final SyncPathMessage message = new SyncPathMessage(debugNodesVisited,
+ debugNodesNotVisited,
+ debugNodesPath,
+ debugNodesVisitedLater,
+ debugNodesOrgPath,
+ debugNodesExtra);
+
+ for (final ServerPlayer player : debugWatchers)
{
- final Map.Entry entry = iter.next();
- if (entry.getValue().equals(entity.getUUID()))
- {
- final ServerPlayer player = entity.level().getServer().getPlayerList().getPlayer(entry.getKey());
- if (player != null)
- {
- new SyncPathMessage(debugNodesVisited, debugNodesNotVisited, debugNodesPath, debugNodesVisitedLater, debugNodesOrgPath, debugNodesExtra).sendToPlayer(player);
- }
- }
+ message.sendToPlayer(player);
}
}
}
@@ -1753,4 +1753,22 @@ public PathingOptions getPathingOptions()
{
return pathingOptions;
}
+
+ @Override
+ public Mob getEntity()
+ {
+ return entity;
+ }
+
+ @Override
+ public Level getActualWorld()
+ {
+ return actualWorld;
+ }
+
+ @Override
+ public BlockPos getStart()
+ {
+ return start;
+ }
}
diff --git a/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobFindWater.java b/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobFindWater.java
index d74064fa6a3..14cc020cf7c 100644
--- a/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobFindWater.java
+++ b/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobFindWater.java
@@ -88,7 +88,7 @@ protected boolean isAtDestination(@NotNull final MNode n)
}
}
- final PathJobFindFishingPos job = new PathJobFindFishingPos(world, new BlockPos(n.x, n.y, n.z), hutLocation, 10, entity);
+ final PathJobFindFishingPos job = new PathJobFindFishingPos(getActualWorld(), world, new BlockPos(n.x, n.y, n.z), hutLocation, 10, entity);
job.setPathingOptions(getPathingOptions());
final Path path = job.search();
if (path != null && path.canReach())
@@ -125,13 +125,14 @@ private class PathJobFindFishingPos extends AbstractPathJob
private final int distance;
public PathJobFindFishingPos(
+ final Level actualWorld,
final LevelReader world,
final @NotNull BlockPos start,
final @NotNull BlockPos direction,
final int distance,
final Mob entity)
{
- super(world, start, distance + 100, new PathResult(), entity);
+ super(actualWorld, world, start, distance + 100, new PathResult(), null);
this.direction = direction;
this.distance = distance;
}
diff --git a/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobRaiderPathing.java b/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobRaiderPathing.java
index 668ad5334dd..61550980713 100644
--- a/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobRaiderPathing.java
+++ b/src/main/java/com/minecolonies/core/entity/pathfinding/pathjobs/PathJobRaiderPathing.java
@@ -53,7 +53,7 @@ public PathJobRaiderPathing(
super(world, start, targetSpawnPoint, new PathResult(), null);
this.buildings = buildings;
direction = targetSpawnPoint;
- maxNodes = 5000;
+ maxNodes = 10000;
setPathingOptions(new PathingOptions().withJumpCost(1).withStartSwimCost(1).withSwimCost(1).withCanSwim(true).withCanEnterDoors(true));
}
diff --git a/src/main/java/com/minecolonies/core/entity/pathfinding/pathresults/PathResult.java b/src/main/java/com/minecolonies/core/entity/pathfinding/pathresults/PathResult.java
index 35d5c637352..ccb36dd9d7b 100644
--- a/src/main/java/com/minecolonies/core/entity/pathfinding/pathresults/PathResult.java
+++ b/src/main/java/com/minecolonies/core/entity/pathfinding/pathresults/PathResult.java
@@ -2,10 +2,14 @@
import com.minecolonies.api.util.Log;
import com.minecolonies.core.entity.pathfinding.PathFindingStatus;
+import com.minecolonies.core.entity.pathfinding.PathfindingUtils;
+import com.minecolonies.core.entity.pathfinding.pathjobs.AbstractPathJob;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.pathfinder.Path;
import org.jetbrains.annotations.Nullable;
-import java.util.concurrent.Callable;
+import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -13,7 +17,7 @@
/**
* Creates a pathResult of a certain path.
*/
-public class PathResult>
+public class PathResult
{
/**
* The pathfinding status
@@ -55,6 +59,11 @@ public class PathResult>
*/
public int searchedNodes = 0;
+ /**
+ * The players getting debug information
+ */
+ private List debugWatchers = null;
+
/**
* Get Status of the Path.
*
@@ -169,6 +178,31 @@ public void setJob(final T job)
this.job = job;
}
+ /**
+ * Adds another player to the debug tracking
+ *
+ * @param player ID
+ */
+ public void addTrackingPlayer(final UUID uuid)
+ {
+ if (uuid == null)
+ {
+ Log.getLogger().warn("Trying to add null uuid as tracking player");
+ }
+
+ if (debugWatchers == null)
+ {
+ debugWatchers = new ArrayList<>();
+ }
+
+ debugWatchers.add(uuid);
+
+ if (job != null)
+ {
+ job.initDebug();
+ }
+ }
+
/**
* Starts the job by queing it to an executor
*
@@ -178,10 +212,58 @@ public void startJob(final ExecutorService executorService)
{
if (job != null)
{
+ checkDebugging();
pathCalculation = executorService.submit(job);
}
}
+ /**
+ * Checks for debug tracking
+ */
+ private void checkDebugging()
+ {
+ if (!PathfindingUtils.trackByType.isEmpty())
+ {
+ for (Iterator> iterator = PathfindingUtils.trackByType.entrySet().iterator(); iterator.hasNext(); )
+ {
+ final Map.Entry entry = iterator.next();
+ final Player player = job.getActualWorld().getPlayerByUUID(entry.getValue());
+ if (player == null)
+ {
+ iterator.remove();
+ continue;
+ }
+
+ // Exclude stuff thats not visible
+ if (player.blockPosition().distManhattan(job.getStart()) > 400)
+ {
+ continue;
+ }
+
+ if (job.getClass().getSimpleName().toLowerCase().contains(entry.getKey().toLowerCase()))
+ {
+ addTrackingPlayer(entry.getValue());
+ }
+ }
+ }
+
+ if (job.getEntity() != null && PathfindingUtils.trackingMap.containsValue(job.getEntity().getUUID()))
+ {
+ for (final Map.Entry entry : PathfindingUtils.trackingMap.entrySet())
+ {
+ if (entry.getValue().equals(job.getEntity().getUUID()))
+ {
+ addTrackingPlayer(entry.getKey());
+ }
+ }
+ }
+
+ if (debugWatchers != null)
+ {
+ job.initDebug();
+ }
+ }
+
/**
* Processes the completed calculation results
*/
@@ -197,6 +279,7 @@ public void processCalculationResults()
path = pathCalculation.get();
pathCalculation = null;
setStatus(PathFindingStatus.CALCULATION_COMPLETE);
+ job.syncDebug(getDebugWatchers());
}
catch (InterruptedException | ExecutionException e)
{
@@ -246,4 +329,28 @@ public void cancel()
pathingDoneAndProcessed = true;
}
+
+ /**
+ * Gets the tracking players
+ *
+ * @return
+ */
+ public List getDebugWatchers()
+ {
+ final List newList = new ArrayList<>();
+
+ if (job != null && debugWatchers != null)
+ {
+ for (final UUID playerID : debugWatchers)
+ {
+ final Player player = job.getActualWorld().getPlayerByUUID(playerID);
+ if (player instanceof ServerPlayer serverPlayer)
+ {
+ newList.add(serverPlayer);
+ }
+ }
+ }
+
+ return newList;
+ }
}
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 3d21d2fa56e..cb989d2535d 100644
--- a/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java
+++ b/src/main/java/com/minecolonies/core/entity/visitor/VisitorCitizen.java
@@ -19,6 +19,7 @@
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.minimal.LookAtEntityInteractGoal;
import com.minecolonies.core.entity.ai.visitor.EntityAIVisitor;
import com.minecolonies.core.entity.citizen.EntityCitizen;
import com.minecolonies.core.entity.citizen.citizenhandlers.*;
@@ -150,8 +151,8 @@ private void initTasks()
this.goalSelector.addGoal(priority, new FloatGoal(this));
this.goalSelector.addGoal(++priority, new OpenDoorGoal(this, true));
this.goalSelector.addGoal(priority, new EntityAIInteractToggleAble(this, FENCE_TOGGLE, TRAP_TOGGLE, DOOR_TOGGLE));
- 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 LookAtEntityInteractGoal(this, Player.class, WATCH_CLOSEST2, 0.2F));
+ this.goalSelector.addGoal(++priority, new LookAtEntityInteractGoal(this, EntityCitizen.class, WATCH_CLOSEST2_FAR, WATCH_CLOSEST2_FAR_CHANCE));
this.goalSelector.addGoal(++priority, new LookAtEntityGoal(this, LivingEntity.class, WATCH_CLOSEST));
new EntityAIVisitor(this);
}
diff --git a/src/main/java/com/minecolonies/core/event/EventHandler.java b/src/main/java/com/minecolonies/core/event/EventHandler.java
index c1061da7301..f7563e5dbe4 100755
--- a/src/main/java/com/minecolonies/core/event/EventHandler.java
+++ b/src/main/java/com/minecolonies/core/event/EventHandler.java
@@ -121,7 +121,8 @@ public static void onEntityAdded(@NotNull final EntityJoinLevelEvent event)
{
if (MineColonies.getConfig().getServer().mobAttackCitizens.get() && event.getEntity() instanceof Mob && event.getEntity() instanceof Enemy && !(event.getEntity()
.getType()
- .is(ModTags.mobAttackBlacklist)))
+ .is(ModTags.mobAttackBlacklist))
+ && !(event.getEntity() instanceof AbstractFastMinecoloniesEntity))
{
((Mob) event.getEntity()).targetSelector.addGoal(6,
new NearestAttackableTargetGoal<>((Mob) event.getEntity(), EntityCitizen.class, true, citizen -> !citizen.isInvisible()));
diff --git a/src/main/java/com/minecolonies/core/items/ItemBannerRallyGuards.java b/src/main/java/com/minecolonies/core/items/ItemBannerRallyGuards.java
index 69561f2e716..cfadaadf4fa 100644
--- a/src/main/java/com/minecolonies/core/items/ItemBannerRallyGuards.java
+++ b/src/main/java/com/minecolonies/core/items/ItemBannerRallyGuards.java
@@ -212,19 +212,20 @@ public static void toggleBanner(final ItemStack banner, final Player playerIn)
return;
}
- boolean activeRaid = false;
if (rallyData.towers().isEmpty())
{
+ rallyData.withActive(false).writeToItemStack(banner);
MessageUtils.format(COM_MINECOLONIES_BANNER_RALLY_GUARDS_TOOLTIP_EMPTY).sendTo(playerIn);
}
else if (rallyData.active())
{
+ rallyData.withActive(false).writeToItemStack(banner);
broadcastPlayerToRally(banner, playerIn.getCommandSenderWorld(), null);
MessageUtils.format(TOOL_RALLY_BANNER_DEACTIVATED).sendTo(playerIn);
}
else
{
- activeRaid = true;
+ rallyData.withActive(true).writeToItemStack(banner);
final IColony colony = getColony(banner, playerIn.level());
if (colony != null && colony.getPermissions().hasPermission(playerIn, Action.RALLY_GUARDS))
{
@@ -240,8 +241,6 @@ else if (rallyData.active())
}
}
}
- rallyData.withActive(activeRaid).writeToItemStack(banner);
-
}
/**
diff --git a/src/main/java/com/minecolonies/core/network/messages/server/colony/ColonyDeleteOwnMessage.java b/src/main/java/com/minecolonies/core/network/messages/server/colony/ColonyDeleteOwnMessage.java
index 021e78f717e..784c8823e21 100755
--- a/src/main/java/com/minecolonies/core/network/messages/server/colony/ColonyDeleteOwnMessage.java
+++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/ColonyDeleteOwnMessage.java
@@ -13,7 +13,8 @@
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.network.handling.IPayloadContext;
-import static com.minecolonies.api.util.constant.TranslationConstants.*;
+import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INFO_COLONY_DESTROY_SUCCESS;
+import static com.minecolonies.api.util.constant.TranslationConstants.MESSAGE_INFO_COLONY_NOT_FOUND;
/**
* Message for deleting an owned colony
@@ -51,15 +52,6 @@ protected void onExecute(final IPayloadContext ctxIn, final ServerPlayer player)
{
IColonyManager.getInstance().deleteColonyByDimension(colony.getID(), false, colony.getDimension());
MessageUtils.format(MESSAGE_INFO_COLONY_DESTROY_SUCCESS).sendTo(player);
-
- try
- {
- NeoForge.EVENT_BUS.post(new ColonyDeletedEvent(colony));
- }
- catch (final Exception e)
- {
- Log.getLogger().error("Error during ColonyDeletedEvent", e);
- }
}
else
{
diff --git a/src/main/java/com/minecolonies/core/util/TeamUtils.java b/src/main/java/com/minecolonies/core/util/TeamUtils.java
new file mode 100644
index 00000000000..88c7a9b3509
--- /dev/null
+++ b/src/main/java/com/minecolonies/core/util/TeamUtils.java
@@ -0,0 +1,48 @@
+package com.minecolonies.core.util;
+
+import net.minecraft.world.level.Level;
+import net.minecraft.world.scores.PlayerTeam;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Utility class for working with {@link net.minecraft.world.scores.Team instances}.
+ */
+public class TeamUtils
+{
+
+ /**
+ * Check or create a team.
+ *
+ * @param level the level to create the team in.
+ * @param name the team name.
+ */
+ @Nullable
+ public static PlayerTeam checkOrCreateTeam(@Nullable final Level level, final String name)
+ {
+ return checkOrCreateTeam(level, name, true);
+ }
+
+ /**
+ * Check or create a team.
+ *
+ * @param level the level to create the team in.
+ * @param name the team name.
+ * @param allowFriendlyFire whether this team allows friendly fire or not.
+ */
+ @Nullable
+ public static PlayerTeam checkOrCreateTeam(@Nullable final Level level, final String name, boolean allowFriendlyFire)
+ {
+ if (level == null)
+ {
+ return null;
+ }
+
+ PlayerTeam team = level.getScoreboard().getPlayerTeam(name);
+ if (team == null)
+ {
+ team = level.getScoreboard().addPlayerTeam(name);
+ }
+ team.setAllowFriendlyFire(allowFriendlyFire);
+ return team;
+ }
+}
diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg
index 9cd640f39f0..12699d88b2f 100755
--- a/src/main/resources/META-INF/accesstransformer.cfg
+++ b/src/main/resources/META-INF/accesstransformer.cfg
@@ -71,4 +71,7 @@ public-f net.minecraft.client.renderer.RenderStateShard setupState
public net.minecraft.world.level.block.Block canSurvive(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z
public net.minecraft.world.entity.projectile.AbstractArrow setPierceLevel(B)V
public net.minecraft.world.entity.projectile.ThrownPotion applySplash(Ljava/lang/Iterable;Lnet/minecraft/world/entity/Entity;)V
-public net.minecraft.world.level.block.StemBlock fruit
\ No newline at end of file
+public net.minecraft.world.level.block.StemBlock fruit
+
+public net.minecraft.world.level.entity.PersistentEntitySectionManager stopTracking(Lnet/minecraft/world/level/entity/EntityAccess;)V # stopTracking
+public net.minecraft.server.level.ServerLevel entityManager # entityManager
\ 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 e3d8499db74..4a704a82fe5 100644
--- a/src/main/resources/assets/minecolonies/lang/manual_en_us.json
+++ b/src/main/resources/assets/minecolonies/lang/manual_en_us.json
@@ -1637,6 +1637,7 @@
"com.minecolonies.coremod.jei.farmer": "In addition to tending fields, the farmer can craft things with hay, seeds, or dirt.",
"com.minecolonies.coremod.jei.fisherman": "Catches fish and other items as usual, plus some bonus items at higher levels.",
"com.minecolonies.coremod.jei.fletcher": "Crafts arrows and leather armor. Also plain items made from wool and string.",
+ "com.minecolonies.coremod.jei.florist": "Grows flowers from compost and composted dirt.",
"com.minecolonies.coremod.jei.glassblower": "Smelts glass itself, plus crafts things made from glass as well.",
"com.minecolonies.coremod.jei.lumberjack": "While they mostly just chop trees, they will also strip and modify logs if required.",
"com.minecolonies.coremod.jei.mechanic": "Crafts mechanisms or other things based on redstone, glowstone, pearls, blaze, etc.",
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher1.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher1.blueprint
index 4a3143ed141..a42abe0dea3 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher1.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher1.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher2.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher2.blueprint
index 58a0795924b..5b54c5928c1 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher2.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher2.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher3.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher3.blueprint
index c21e51c50cb..098e734c76f 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher3.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher3.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher4.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher4.blueprint
index e74b773f992..59d042921bc 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher4.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher4.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher5.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher5.blueprint
index 7c43c245730..3583fb36e05 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher5.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/crusher5.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter1.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter1.blueprint
index 9561a6b8ab9..8f0073fd5a7 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter1.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter1.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter2.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter2.blueprint
index 896c1404930..0d501d3d9cf 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter2.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter2.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter3.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter3.blueprint
index b65ab35cabf..3511060606d 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter3.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter3.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter4.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter4.blueprint
index fcad5feb79d..1cc23fcb018 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter4.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter4.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter5.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter5.blueprint
index 69edb7fc7b6..6686c983173 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter5.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/masonry/sifter5.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman1.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman1.blueprint
index 2d29c5edda4..dabdc068a60 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman1.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman1.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman2.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman2.blueprint
index d2538d5bbf1..06d9eeead0d 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman2.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman2.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman3.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman3.blueprint
index de9c7d4123f..e71d4e5f27a 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman3.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman3.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman4.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman4.blueprint
index 0dcb66be871..f9eaf6780e1 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman4.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman4.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman5.blueprint b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman5.blueprint
index 2aa33fdce93..50861dd3ce2 100644
Binary files a/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman5.blueprint and b/src/main/resources/blueprints/minecolonies/jungle/craftsmanship/storage/deliveryman5.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/military/guardalt1.blueprint b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt1.blueprint
new file mode 100644
index 00000000000..281db75350d
Binary files /dev/null and b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt1.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/military/guardalt2.blueprint b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt2.blueprint
new file mode 100644
index 00000000000..2b43033f46a
Binary files /dev/null and b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt2.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/military/guardalt3.blueprint b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt3.blueprint
new file mode 100644
index 00000000000..0898418c99d
Binary files /dev/null and b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt3.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/military/guardalt4.blueprint b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt4.blueprint
new file mode 100644
index 00000000000..54f8b4c75d3
Binary files /dev/null and b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt4.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/jungle/military/guardalt5.blueprint b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt5.blueprint
new file mode 100644
index 00000000000..992a4fda52c
Binary files /dev/null and b/src/main/resources/blueprints/minecolonies/jungle/military/guardalt5.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt1.blueprint b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt1.blueprint
index 6bf01a3d4c5..c2bdb75c250 100644
Binary files a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt1.blueprint and b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt1.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt2.blueprint b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt2.blueprint
index 58bd1ae32f2..03a0d8c3b12 100644
Binary files a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt2.blueprint and b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt2.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt3.blueprint b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt3.blueprint
index 818f0342ef0..60c1b722dbd 100644
Binary files a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt3.blueprint and b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt3.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt4.blueprint b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt4.blueprint
index 953f9f34133..45b809be017 100644
Binary files a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt4.blueprint and b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt4.blueprint differ
diff --git a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt5.blueprint b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt5.blueprint
index af1406f53ba..589f1922faf 100644
Binary files a/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt5.blueprint and b/src/main/resources/blueprints/minecolonies/shire/fundamentals/foresteralt5.blueprint differ