Skip to content

Commit df86c61

Browse files
committed
Changed the algorithm to determine how pawns wander around the fortress to make it more natural
1 parent 4400dae commit df86c61

File tree

11 files changed

+126
-84
lines changed

11 files changed

+126
-84
lines changed

src/building/java/net/remmintan/mods/minefortress/blocks/building/FortressBuildingBlockEntity.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import net.minecraft.screen.ScreenHandler
1919
import net.minecraft.server.world.ServerWorld
2020
import net.minecraft.text.Text
2121
import net.minecraft.util.math.BlockPos
22+
import net.minecraft.world.Heightmap
2223
import net.minecraft.world.World
2324
import net.remmintan.mods.minefortress.blocks.FortressBlocks
2425
import net.remmintan.mods.minefortress.core.dtos.ItemInfo
@@ -34,6 +35,9 @@ import net.remmintan.mods.minefortress.gui.building.BuildingScreenHandler
3435
import org.slf4j.Logger
3536
import org.slf4j.LoggerFactory
3637
import java.util.*
38+
import kotlin.math.abs
39+
import kotlin.math.max
40+
import kotlin.math.min
3741
import kotlin.streams.asSequence
3842

3943
private const val MAX_BLOCKS_PER_UPDATE = 10
@@ -120,6 +124,31 @@ class FortressBuildingBlockEntity(pos: BlockPos?, state: BlockState?) :
120124
error("Trying to get managers on the client side")
121125
}
122126

127+
override fun getRandomPosToComeToBuilding(): BlockPos? {
128+
val world = world ?: return null
129+
val start = start ?: return null
130+
val end = end ?: return null
131+
132+
val startY = min(start.y + metadata.floorLevel, end.y)
133+
val endY = max(start.y + metadata.floorLevel, end.y)
134+
135+
var attempts = 0
136+
do {
137+
val randPos = BlockPos.iterateRandomly(world.random, 1, pos, 10)
138+
.map {
139+
val y = world.getTopY(Heightmap.Type.WORLD_SURFACE, it.x, it.z)
140+
BlockPos(it.x, y, it.z)
141+
}
142+
.first()
143+
144+
145+
val probablyOnTheRoof = endY - startY > 3 && abs(randPos.y - startY) > abs(endY - randPos.y)
146+
if (!probablyOnTheRoof) return randPos
147+
} while (++attempts < 5)
148+
149+
return null
150+
}
151+
123152
override fun getHireHandler() = hireHandler
124153

125154
override fun getFurnacePos(): List<BlockPos>? =

src/core/java/net/remmintan/mods/minefortress/core/interfaces/buildings/IFortressBuilding.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import net.minecraft.block.BlockState;
44
import net.minecraft.block.entity.BlockEntity;
5-
import net.minecraft.block.entity.FurnaceBlockEntity;
65
import net.minecraft.entity.mob.HostileEntity;
76
import net.minecraft.util.math.BlockBox;
87
import net.minecraft.util.math.BlockPos;
@@ -12,6 +11,7 @@
1211
import net.remmintan.mods.minefortress.core.dtos.buildings.BlueprintMetadata;
1312
import net.remmintan.mods.minefortress.core.interfaces.automation.area.IAutomationArea;
1413
import net.remmintan.mods.minefortress.core.interfaces.blueprints.ProfessionType;
14+
import org.jetbrains.annotations.Nullable;
1515

1616
import java.util.List;
1717
import java.util.Map;
@@ -28,6 +28,9 @@ default BlockPos getPos() {
2828
}
2929
}
3030

31+
@Nullable
32+
BlockPos getRandomPosToComeToBuilding();
33+
3134
default List<String> getUpgrades() {
3235
return getMetadata().getRequirement().getUpgrades();
3336
}

src/core/java/net/remmintan/mods/minefortress/core/interfaces/buildings/IServerBuildingsManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import net.remmintan.mods.minefortress.core.interfaces.server.IServerManager;
99
import net.remmintan.mods.minefortress.core.interfaces.server.ITickableManager;
1010
import net.remmintan.mods.minefortress.core.interfaces.server.IWritableManager;
11+
import org.jetbrains.annotations.Nullable;
1112

1213
import java.util.List;
1314
import java.util.Map;
@@ -28,6 +29,8 @@ public interface IServerBuildingsManager extends IServerManager, IWritableManage
2829
Optional<IFortressBuilding> findNearest(BlockPos pos);
2930
Optional<IFortressBuilding> findNearest(BlockPos pos, ProfessionType requirement);
3031

32+
@Nullable BlockPos getRandomPositionToGoTo();
33+
3134
Optional<HostileEntity> getRandomBuildingAttacker();
3235
Optional<BlockPos> getFreeBed();
3336
boolean isPartOfAnyBuilding(BlockPos pos);

src/core/java/net/remmintan/mods/minefortress/core/interfaces/server/IServerFortressManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public interface IServerFortressManager extends IFortressManager, IWritableManag
5656
void addPawn(LivingEntity pawn);
5757
Optional<BlockPos> getRandomPositionAroundCampfire();
5858

59+
@Nullable
60+
BlockPos getRandomFortressPosition();
61+
5962
Optional<LivingEntity> spawnPawnNearCampfire();
6063
void setSpawnPawns(boolean spawnPawns);
6164
void spawnDebugEntitiesAroundCampfire(EntityType<? extends IFortressAwareEntity> entityType, int num, ServerPlayerEntity player);

src/main/java/org/minefortress/entity/Colonist.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ protected void initGoals() {
164164
this.goalSelector.add(6, new PawnExecuteAreaBasedTaskGoal(this));
165165
this.goalSelector.add(8, new WanderAroundTheFortressGoal(this));
166166
this.goalSelector.add(8, new SleepOnTheBedGoal(this));
167+
this.goalSelector.add(9, new ReturnToFireGoal(this));
167168
this.goalSelector.add(10, new LookAroundGoal(this));
168169

169170
this.targetSelector.add(1, new FortressRevengeGoal(this).setGroupRevenge());

src/main/java/org/minefortress/entity/ai/goal/AbstractFortressGoal.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ protected AbstractFortressGoal(Colonist colonist) {
2121
}
2222
}
2323

24-
protected String getColonistName() {
25-
return colonist.getName().getString();
26-
}
27-
2824
protected boolean isHungry() {
2925
return colonist.getEatControl().map(IEatControl::isHungry).orElse(false);
3026
}

src/main/java/org/minefortress/entity/ai/goal/ReturnToFireGoal.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public ReturnToFireGoal(Colonist colonist) {
1919

2020
@Override
2121
public boolean canStart() {
22+
if (colonist.getWorld().isDay()) return false;
2223
if(colonist.getTarget() != null && colonist.getTarget().isAlive()) return false;
2324
if(colonist.getTaskControl().hasTask()) return false;
2425
if(!isFarFromCenter()) return false;
@@ -39,7 +40,7 @@ public void start() {
3940

4041
@Override
4142
public boolean shouldContinue() {
42-
return
43+
return colonist.getWorld().isNight() &&
4344
!colonist.getTaskControl().hasTask() &&
4445
!colonist.getMovementHelper().isStuck() &&
4546
(isFarFromCenter() || colonist.getMovementHelper().stillTryingToReachGoal());

src/main/java/org/minefortress/entity/ai/goal/WanderAroundTheFortressGoal.java

Lines changed: 0 additions & 73 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.minefortress.entity.ai.goal
2+
3+
import net.minecraft.util.math.BlockPos
4+
import net.remmintan.mods.minefortress.core.utils.ServerModUtils
5+
import org.minefortress.entity.Colonist
6+
import org.minefortress.entity.colonist.IFortressHungerManager
7+
import java.time.Duration
8+
import java.time.LocalDateTime
9+
10+
class WanderAroundTheFortressGoal(colonist: Colonist) : AbstractFortressGoal(colonist) {
11+
private var goal: BlockPos? = null
12+
private var stopTime: LocalDateTime = LocalDateTime.now()
13+
private var delay: Int = 0
14+
15+
override fun canStart(): Boolean {
16+
if (colonist.eatControl.map { it.isEating }.orElse(false)) return false
17+
if (!isDay || colonist.taskControl.hasTask()) return false
18+
val durationSinceStop = Duration.between(stopTime, LocalDateTime.now()).toMillis()
19+
if (durationSinceStop < delay) return false
20+
21+
goal = ServerModUtils
22+
.getFortressManager(colonist)
23+
.map { it.randomFortressPosition }
24+
.orElse(null)
25+
26+
return goal != null
27+
}
28+
29+
override fun start() {
30+
colonist.currentTaskDesc = "Wandering around"
31+
colonist.putItemInHand(null)
32+
colonist.movementHelper.goTo(goal!!, Colonist.SLOW_MOVEMENT_SPEED)
33+
}
34+
35+
override fun tick() {
36+
super.tick()
37+
colonist.addHunger(IFortressHungerManager.IDLE_EXHAUSTION)
38+
if (colonist.movementHelper.isStuck) {
39+
if (goal != null) {
40+
colonist.resetControls()
41+
colonist.teleport(goal!!.x.toDouble(), goal!!.y.toDouble(), goal!!.z.toDouble())
42+
}
43+
}
44+
}
45+
46+
override fun shouldContinue(): Boolean {
47+
return isDay && !colonist.taskControl.hasTask() && colonist.movementHelper.stillTryingToReachGoal()
48+
}
49+
50+
override fun stop() {
51+
goal = null
52+
colonist.movementHelper.reset()
53+
stopTime = LocalDateTime.now()
54+
delay = colonist.world.random.nextInt(6000) + 5000
55+
}
56+
57+
private val isDay: Boolean
58+
get() = colonist.world.isDay
59+
}

src/main/java/org/minefortress/fortress/ServerFortressManager.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import net.remmintan.mods.minefortress.core.interfaces.automation.IAutomationAreaProvider;
2121
import net.remmintan.mods.minefortress.core.interfaces.automation.area.IAutomationArea;
2222
import net.remmintan.mods.minefortress.core.interfaces.blueprints.ProfessionType;
23+
import net.remmintan.mods.minefortress.core.interfaces.buildings.IServerBuildingsManager;
2324
import net.remmintan.mods.minefortress.core.interfaces.entities.IPawnNameGenerator;
2425
import net.remmintan.mods.minefortress.core.interfaces.entities.pawns.IFortressAwareEntity;
2526
import net.remmintan.mods.minefortress.core.interfaces.entities.pawns.IProfessional;
@@ -421,8 +422,8 @@ public boolean isPositionWithinFortress(BlockPos pos) {
421422
}
422423

423424
public Optional<BlockPos> getRandomPositionAroundCampfire() {
424-
final var fortressCenter = getFortressCenter();
425-
if(fortressCenter == null) return Optional.empty();
425+
final var position = getFortressCenter();
426+
if (position == null) return Optional.empty();
426427

427428
final var random = getWorld().random;
428429

@@ -431,13 +432,23 @@ public Optional<BlockPos> getRandomPositionAroundCampfire() {
431432
final var x = (int) Math.round(radius * Math.cos(angle) * getCampfireWarmRadius());
432433
final var z = (int) Math.round(radius * Math.sin(angle) * getCampfireWarmRadius());
433434

434-
final var blockX = fortressCenter.getX() + x;
435-
final var blockZ = fortressCenter.getZ() + z;
435+
final var blockX = position.getX() + x;
436+
final var blockZ = position.getZ() + z;
436437
final var blockY = getWorld().getTopY(Heightmap.Type.WORLD_SURFACE, blockX, blockZ);
437438

438439
return Optional.of(new BlockPos(blockX, blockY, blockZ));
439440
}
440441

442+
@Override
443+
public @Nullable BlockPos getRandomFortressPosition() {
444+
if (fortressCenter == null || server == null || world == null) return null;
445+
return ServerModUtils.getManagersProvider(server, fortressCenter)
446+
.map(IServerManagersProvider::getBuildingsManager)
447+
.map(IServerBuildingsManager::getRandomPositionToGoTo)
448+
.or(this::getRandomPositionAroundCampfire)
449+
.orElse(null);
450+
}
451+
441452
public double getCampfireWarmRadius() {
442453
return Math.max(Math.sqrt(getTotalColonistsCount()), 4);
443454
}

0 commit comments

Comments
 (0)