Skip to content

Commit b27aeb2

Browse files
Fixed NPC pathing. Fixed Swimming. Fixed base skin texture
1 parent 5f05597 commit b27aeb2

File tree

6 files changed

+112
-97
lines changed

6 files changed

+112
-97
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ local.properties
4444
.cproject
4545

4646
# PDT-specific
47-
.buildpath
47+
.buildpath
48+
*.png~

src/main/java/com/femboynuggy/helpfulnpcs/client/WorkerScreen.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66

77
import com.femboynuggy.helpfulnpcs.HelpfulNPCs;
88
import com.femboynuggy.helpfulnpcs.container.WorkerContainer;
9-
import com.femboynuggy.helpfulnpcs.entity.WorkerEntity;
109
import com.femboynuggy.helpfulnpcs.network.SetWorkerCommandPacket;
1110
import com.mojang.blaze3d.systems.RenderSystem;
1211

13-
import net.minecraft.client.Minecraft;
1412
import net.minecraft.client.gui.GuiGraphics;
1513
import net.minecraft.client.gui.components.Button;
1614
import net.minecraft.client.gui.components.EditBox;

src/main/java/com/femboynuggy/helpfulnpcs/entity/WorkerEntity.java

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import net.minecraft.core.Direction;
1616
import net.minecraft.core.particles.ParticleTypes;
1717
import net.minecraft.nbt.CompoundTag;
18-
import net.minecraft.nbt.Tag;
1918
import net.minecraft.network.FriendlyByteBuf;
2019
import net.minecraft.network.protocol.Packet;
2120
import net.minecraft.network.protocol.game.ClientGamePacketListener;
@@ -29,27 +28,29 @@
2928
import net.minecraft.world.entity.EntityType;
3029
import net.minecraft.world.entity.EquipmentSlot;
3130
import net.minecraft.world.entity.LivingEntity;
31+
import net.minecraft.world.entity.Mob;
3232
import net.minecraft.world.entity.MobSpawnType;
3333
import net.minecraft.world.entity.PathfinderMob;
3434
import net.minecraft.world.entity.SpawnGroupData;
3535
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
3636
import net.minecraft.world.entity.ai.attributes.Attributes;
3737
import net.minecraft.world.entity.ai.control.MoveControl;
38+
import net.minecraft.world.entity.ai.goal.FloatGoal;
3839
import net.minecraft.world.entity.ai.goal.Goal;
3940
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
4041
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
42+
import net.minecraft.world.entity.ai.goal.OpenDoorGoal;
4143
import net.minecraft.world.entity.ai.goal.PanicGoal;
4244
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
4345
import net.minecraft.world.entity.ai.goal.RangedBowAttackGoal;
4446
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
45-
import net.minecraft.world.entity.ai.goal.target.OwnerHurtTargetGoal;
4647
import net.minecraft.world.entity.ai.goal.target.TargetGoal;
4748
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
49+
import net.minecraft.world.entity.ai.navigation.PathNavigation;
4850
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
4951
import net.minecraft.world.entity.monster.RangedAttackMob;
5052
import net.minecraft.world.entity.player.Player;
5153
import net.minecraft.world.entity.projectile.AbstractArrow;
52-
import net.minecraft.world.entity.projectile.Arrow;
5354
import net.minecraft.world.entity.projectile.ProjectileUtil;
5455
import net.minecraft.world.item.AxeItem;
5556
import net.minecraft.world.item.BowItem;
@@ -66,7 +67,6 @@
6667
import net.minecraftforge.items.ItemStackHandler;
6768
import net.minecraftforge.network.NetworkHooks;
6869
import net.minecraftforge.network.PlayMessages;
69-
import net.minecraft.world.entity.TamableAnimal;
7070

7171
public class WorkerEntity extends PathfinderMob implements RangedAttackMob {
7272
// renamed from DATA_GHOST_ITEM → DATA_TARGET_ITEM
@@ -128,8 +128,8 @@ protected void onContentsChanged(int slot) {
128128
public WorkerEntity(EntityType<? extends WorkerEntity> type, Level world) {
129129
super(type, world);
130130
this.setPersistenceRequired();
131-
this.moveControl = new MoveControl(this);
132-
this.navigation = new GroundPathNavigation(this, world);
131+
//this.moveControl = new MoveControl(this);
132+
//this.navigation = new GroundPathNavigation(this, world);
133133
}
134134

135135
public WorkerEntity(PlayMessages.SpawnEntity msg, Level world) {
@@ -141,6 +141,28 @@ public WorkerEntity(PlayMessages.SpawnEntity msg, Level world) {
141141
public WorkerEntity(FriendlyByteBuf buf, Level world) {
142142
this(ModEntities.WORKER.get(), world);
143143
}
144+
145+
@Override
146+
protected void registerGoals() {
147+
super.registerGoals();
148+
149+
this.goalSelector.addGoal(0, new FloatGoal(this));
150+
this.goalSelector.addGoal(0, new NaturalRegenGoal(this, 20*10, 20));
151+
this.goalSelector.addGoal(0, new OpenDoorGoal(this, true));
152+
153+
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
154+
this.targetSelector.addGoal(1, new DefendMasterHurtByTargetGoal(this));
155+
this.goalSelector.addGoal(1, new WorkerBowAttackGoal(this, 1.2D, 20, 15.0F));
156+
157+
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.5D, true));
158+
this.targetSelector.addGoal(2, new DefendMasterHurtTargetGoal(this));
159+
160+
this.goalSelector.addGoal(3, new PanicGoal(this, 1.25));
161+
this.goalSelector.addGoal(4, new FollowMasterGoal(this, 1.5D, 5.0F));
162+
this.goalSelector.addGoal(4, new CommandMoveToGoal(this, 1.0D));
163+
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 8.0F, 1.0F));
164+
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 6.0F));
165+
}
144166

145167
@Override
146168
public Packet<ClientGamePacketListener> getAddEntityPacket() {
@@ -149,9 +171,8 @@ public Packet<ClientGamePacketListener> getAddEntityPacket() {
149171

150172
private boolean interacting = false;
151173

152-
// called when you open the GUI in your onInteract or NetworkHooks.openGui callback:
153174
public void setInteracting(boolean b) {
154-
System.out.println("CLIENT: "+b);
175+
//System.out.println("CLIENT: "+b);
155176
this.interacting = b;
156177
}
157178
public boolean isInteracting() {
@@ -201,37 +222,24 @@ public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstanc
201222
return super.finalizeSpawn(world, difficulty, reason, data, tag);
202223
}
203224

225+
@Override
226+
protected PathNavigation createNavigation(Level world) {
227+
GroundPathNavigation nav = new GroundPathNavigation(this, world);
228+
// allow opening wooden doors
229+
nav.setCanOpenDoors(true);
230+
// allow walking **through** an open door block
231+
nav.setCanPassDoors(true);
232+
// if you want them to swim, too:
233+
nav.setCanFloat(true);
234+
return nav;
235+
}
236+
204237
// getters for the four indices:
205238
public int getBodyIndex() { return this.entityData.get(DATA_BODY); }
206239
public int getOutfitIndex() { return this.entityData.get(DATA_OUTFIT); }
207240
public int getEyesIndex() { return this.entityData.get(DATA_EYES); }
208241
public int getHairIndex() { return this.entityData.get(DATA_HAIR); }
209242

210-
@Override
211-
protected void registerGoals() {
212-
super.registerGoals();
213-
214-
this.goalSelector.addGoal(0, new NaturalRegenGoal(this, 20*10, 20));
215-
// MELEE first:
216-
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.5D, true));
217-
// then follow master if we have a weapon:
218-
this.goalSelector.addGoal(3, new FollowMasterGoal(this, 1.5D, 5.0F));
219-
// then bow (if bow in hand):
220-
this.goalSelector.addGoal(1, new WorkerBowAttackGoal(this, 1.2D, 20, 15.0F));
221-
// normal idle/look goals:
222-
this.goalSelector.addGoal(4, new PanicGoal(this, 1.25));
223-
//this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0)); // TODO Might re-implement this later, but for now it's just annoying
224-
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
225-
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 8.0F));
226-
// your move‐to‐listData goal:
227-
this.goalSelector.addGoal(9, new CommandMoveToGoal(this, 1.0D));
228-
229-
// DEFENSIVE target goals:
230-
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
231-
this.targetSelector.addGoal(1, new DefendMasterHurtByTargetGoal(this));
232-
this.targetSelector.addGoal(2, new DefendMasterHurtTargetGoal(this));
233-
}
234-
235243
public static AttributeSupplier.Builder createAttributes() {
236244
return PathfinderMob.createMobAttributes()
237245
.add(Attributes.MAX_HEALTH, 20.0D)

src/main/java/com/femboynuggy/helpfulnpcs/entity/goal/CommandMoveToGoal.java

Lines changed: 67 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,71 +6,71 @@
66

77
import com.femboynuggy.helpfulnpcs.entity.WorkerEntity;
88

9-
import net.minecraft.client.Minecraft;
109
import net.minecraft.core.BlockPos;
1110
import net.minecraft.core.Direction;
1211
import net.minecraft.nbt.CompoundTag;
1312
import net.minecraft.nbt.ListTag;
1413
import net.minecraft.nbt.Tag;
1514
import net.minecraft.world.InteractionHand;
1615
import net.minecraft.world.entity.ai.goal.Goal;
16+
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
1717
import net.minecraft.world.item.ItemStack;
1818
import net.minecraft.world.item.Items;
1919
import net.minecraft.world.level.Level;
2020
import net.minecraft.world.level.block.entity.BlockEntity;
21+
import net.minecraft.world.level.pathfinder.Path;
2122
import net.minecraftforge.common.capabilities.ForgeCapabilities;
2223
import net.minecraftforge.items.IItemHandler;
2324
import net.minecraftforge.items.ItemHandlerHelper;
2425

2526
public class CommandMoveToGoal extends Goal {
2627
private final WorkerEntity mob;
2728
private final double speed;
29+
private Path path;
30+
private int recalcCooldown = 0;
31+
private double stoppingDistance = 3.0;
2832

29-
private enum Stage { GO_TO_TARGET, WAIT_AFTER_OP }
33+
enum Stage { GO_TO_TARGET, WAIT_AFTER_OP }
3034
private Stage stage = Stage.GO_TO_TARGET;
31-
3235
private final List<TargetEntry> entries = new ArrayList<>();
3336
private int currentIndex = 0;
3437
private int waitTicks = 0;
35-
36-
// ← NEW: remember what size we loaded at start()
3738
private int lastListSize = 0;
3839

3940
public CommandMoveToGoal(WorkerEntity mob, double speed) {
4041
this.mob = mob;
4142
this.speed = speed;
4243
this.setFlags(EnumSet.of(Flag.MOVE));
44+
if (!(mob.getNavigation() instanceof GroundPathNavigation))
45+
throw new IllegalArgumentException("Must have GroundPathNavigation");
4346
}
4447

4548
@Override
4649
public boolean canUse() {
47-
// only run this goal if the worker is holding a book & quill
4850
if (mob.getMainHandItem().getItem() != Items.WRITABLE_BOOK) return false;
4951
if (mob.isInteracting()) return false;
50-
boolean ok = parseEntries();
51-
if (ok) lastListSize = entries.size();
52-
return ok;
52+
if (!parseEntries()) return false;
53+
lastListSize = entries.size();
54+
currentIndex = 0;
55+
stage = Stage.GO_TO_TARGET;
56+
waitTicks = 0;
57+
// force an immediate path calculation
58+
recalcCooldown = 0;
59+
computePathToCurrent();
60+
return path != null;
5361
}
5462

5563
@Override
5664
public boolean canContinueToUse() {
57-
// drop out immediately if they let go of the book & quill
5865
if (mob.getMainHandItem().getItem() != Items.WRITABLE_BOOK) return false;
5966
if (mob.isInteracting()) return false;
60-
// also re-parse and check for listData changes
61-
boolean ok = parseEntries();
62-
if (!ok || entries.size() != lastListSize) {
63-
return false;
64-
}
67+
if (!parseEntries() || entries.size() != lastListSize) return false;
6568
return true;
6669
}
6770

6871
@Override
6972
public void start() {
70-
currentIndex = 0;
71-
waitTicks = 0;
72-
stage = Stage.GO_TO_TARGET;
73-
navigateToCurrent();
73+
// nothing more to do; we've already computed path in canUse()
7474
}
7575

7676
@Override
@@ -81,55 +81,62 @@ public void stop() {
8181

8282
@Override
8383
public void tick() {
84-
if (mob.isInteracting()) {
85-
mob.getNavigation().stop();
86-
return;
87-
}
8884
if (entries.isEmpty()) return;
8985
TargetEntry te = entries.get(currentIndex);
9086

91-
double dist2 = mob.position()
92-
.distanceToSqr(te.pos.getX() + 0.5,
93-
te.pos.getY() + 0.5,
94-
te.pos.getZ() + 0.5);
95-
switch(stage) {
96-
case GO_TO_TARGET:
97-
if (dist2 <= 5.0) {
98-
mob.getNavigation().stop();
99-
100-
if (!mob.level().isClientSide) {
101-
mob.swing(InteractionHand.MAIN_HAND, true); // This isn't working for some reason
102-
//System.out.println(">> SERVER: swung arm and broadcasted entity event");
103-
}
104-
105-
if ("extract".equalsIgnoreCase(te.mode)) {
106-
extractFromContainer(te);
107-
} else {
108-
insertToContainer(te);
109-
}
110-
111-
stage = Stage.WAIT_AFTER_OP;
112-
waitTicks = 0;
113-
}
114-
break;
87+
// look at the block
88+
mob.getLookControl().setLookAt(
89+
te.pos.getX()+0.5, te.pos.getY()+0.5, te.pos.getZ()+0.5,
90+
30, 30
91+
);
92+
93+
// if we've lost our path or it’s done, or we’ve been on it too long, recompute
94+
if (mob.getNavigation().isDone()
95+
|| mob.getNavigation().isStuck()
96+
|| --recalcCooldown <= 0) {
97+
computePathToCurrent();
98+
}
11599

116-
case WAIT_AFTER_OP:
117-
if (++waitTicks >= 20) {
118-
currentIndex = (currentIndex + 1) % entries.size();
119-
navigateToCurrent();
120-
stage = Stage.GO_TO_TARGET;
121-
}
122-
break;
100+
// now the nav will automatically follow 'path' around obstacles
101+
// check arrival
102+
double dist2 = mob.position().distanceToSqr(
103+
te.pos.getX()+0.5, te.pos.getY()+0.5, te.pos.getZ()+0.5
104+
);
105+
106+
if (stage == Stage.GO_TO_TARGET && dist2 <= stoppingDistance) {
107+
mob.getNavigation().stop();
108+
109+
mob.swing(InteractionHand.MAIN_HAND);
110+
111+
if ("extract".equalsIgnoreCase(te.mode)) {
112+
extractFromContainer(te);
113+
} else {
114+
insertToContainer(te);
115+
}
116+
117+
stage = Stage.WAIT_AFTER_OP;
118+
waitTicks = 0;
119+
return;
120+
}
121+
122+
if (stage == Stage.WAIT_AFTER_OP && ++waitTicks >= 20) {
123+
currentIndex = (currentIndex + 1) % entries.size();
124+
stage = Stage.GO_TO_TARGET;
125+
// force a fresh path for the new target
126+
computePathToCurrent();
127+
waitTicks = 0;
123128
}
124129
}
125130

126-
private void navigateToCurrent() {
131+
/** Builds a fresh A* path to entries.get(currentIndex) and starts following it. */
132+
private void computePathToCurrent() {
127133
TargetEntry te = entries.get(currentIndex);
128-
mob.getNavigation()
129-
.moveTo(te.pos.getX() + 0.5,
130-
te.pos.getY() + 0.5,
131-
te.pos.getZ() + 0.5,
132-
speed);
134+
// use a small “fudge” parameter so we can step up blocks if needed
135+
this.path = mob.getNavigation().createPath(te.pos, 0);
136+
if (path != null) {
137+
mob.getNavigation().moveTo(path, speed);
138+
recalcCooldown = 40 + mob.getRandom().nextInt(40);
139+
}
133140
}
134141

135142
private boolean parseEntries() {
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"item.helpfulnpcs.contract": "NPC Contract"
2+
"item.helpfulnpcs.contract": "NPC Contract",
3+
"entity.helpfulnpcs.worker": "NPC Worker"
34
}
878 Bytes
Loading

0 commit comments

Comments
 (0)