Skip to content

Commit

Permalink
fix #37 entity pathing
Browse files Browse the repository at this point in the history
  • Loading branch information
CammiePone committed Jan 9, 2025
1 parent bac5871 commit a80197c
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 222 deletions.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ dependencies {
modCompileOnly libs.patchouli
modLocalRuntime libs.patchouli

modImplementation libs.smartbrainlib
modImplementation libs.resourceful.config

modImplementation libs.reach.attr
Expand Down
2 changes: 0 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ reach_attr = "2.4.0"
modmenu = "7.2.2"
emi = "1.1.18+1.20.1"
lazydfu = "0.1.3"
smartbrainlib = "5654969" # 1.15
resourceful_config = "2.1.2"
firstperson = "SiM8ZZnj" # 2.4.6
pehkui = "3.7.8+1.14.4-1.20.1"
Expand Down Expand Up @@ -47,7 +46,6 @@ datasync_fabric = { module = "dev.upcraft.datasync:datasync-minecraft-1.20.1-fab
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
emi = { module = "dev.emi:emi-fabric", version.ref = "emi" }
lazydfu = { module = "maven.modrinth:lazydfu", version.ref = "lazydfu" }
smartbrainlib = { module = "curse.maven:smartbrainlib-661293", version.ref = "smartbrainlib" }
resourceful_config = { module = "com.teamresourceful.resourcefulconfig:resourcefulconfig-fabric-1.20.1", version.ref = "resourceful_config" }
firstperson = { module = "maven.modrinth:first-person-model", version.ref = "firstperson" }
pehkui = { module = "maven.modrinth:pehkui", version.ref = "pehkui" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.cammiescorner.arcanuscontinuum.common.entities;

import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.NeutralMob;

import java.util.UUID;

public interface Summon extends NeutralMob {
LivingEntity getCaster();
UUID getOwnerId();
void setOwner(LivingEntity entity);
boolean wantsToAttack(LivingEntity target, LivingEntity caster);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dev.cammiescorner.arcanuscontinuum.common.entities.goals;

import dev.cammiescorner.arcanuscontinuum.common.entities.Summon;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.target.TargetGoal;
import net.minecraft.world.entity.ai.targeting.TargetingConditions;

import java.util.EnumSet;

public class CasterHurtByTargetGoal<T extends Mob & Summon> extends TargetGoal {
private final T summon;
private LivingEntity ownerLastHurtBy;
private int timestamp;

public CasterHurtByTargetGoal(T tameAnimal) {
super(tameAnimal, false);
this.summon = tameAnimal;
this.setFlags(EnumSet.of(Flag.TARGET));
}

public boolean canUse() {
LivingEntity caster = summon.getCaster();

if(caster == null) {
return false;
}
else {
ownerLastHurtBy = caster.getLastHurtByMob();
int i = caster.getLastHurtByMobTimestamp();
return i != timestamp && canAttack(ownerLastHurtBy, TargetingConditions.DEFAULT) && summon.wantsToAttack(ownerLastHurtBy, caster);
}
}

public void start() {
LivingEntity caster = summon.getCaster();
mob.setTarget(ownerLastHurtBy);

if(caster != null)
timestamp = caster.getLastHurtByMobTimestamp();

super.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dev.cammiescorner.arcanuscontinuum.common.entities.goals;

import dev.cammiescorner.arcanuscontinuum.common.entities.Summon;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.target.TargetGoal;
import net.minecraft.world.entity.ai.targeting.TargetingConditions;

import java.util.EnumSet;

public class CasterHurtTargetGoal<T extends Mob & Summon> extends TargetGoal {
private final T summon;
private LivingEntity ownerLastHurt;
private int timestamp;

public CasterHurtTargetGoal(T tameAnimal) {
super(tameAnimal, false);
this.summon = tameAnimal;
this.setFlags(EnumSet.of(Flag.TARGET));
}

public boolean canUse() {
LivingEntity caster = this.summon.getCaster();

if(caster == null) {
return false;
}
else {
ownerLastHurt = caster.getLastHurtMob();
int i = caster.getLastHurtMobTimestamp();
return i != timestamp && canAttack(ownerLastHurt, TargetingConditions.DEFAULT) && summon.wantsToAttack(ownerLastHurt, caster);
}
}

public void start() {
LivingEntity caster = summon.getCaster();
mob.setTarget(ownerLastHurt);

if(caster != null)
timestamp = caster.getLastHurtMobTimestamp();

super.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package dev.cammiescorner.arcanuscontinuum.common.entities.goals;

import dev.cammiescorner.arcanuscontinuum.common.entities.Summon;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;

import java.util.EnumSet;

public class FollowCasterGoal<T extends Mob & Summon> extends Goal {
private static final int MIN_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 2;
private static final int MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 3;
private static final int MAX_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 1;
private final T summon;
private LivingEntity owner;
private final LevelReader level;
private final double speedModifier;
private final PathNavigation navigation;
private int timeToRecalcPath;
private final float stopDistance;
private final float startDistance;
private float oldWaterCost;
private final boolean canFly;

public FollowCasterGoal(T summon, double speedModifier, float startDistance, float stopDistance, boolean canFly) {
this.summon = summon;
this.level = summon.level();
this.speedModifier = speedModifier;
this.navigation = summon.getNavigation();
this.startDistance = startDistance;
this.stopDistance = stopDistance;
this.canFly = canFly;
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));

if(!(summon.getNavigation() instanceof GroundPathNavigation) && !(summon.getNavigation() instanceof FlyingPathNavigation))
throw new IllegalArgumentException("Unsupported mob type for FollowCasterGoal");
}

public boolean canUse() {
LivingEntity livingEntity = summon.getCaster();

if(livingEntity == null)
return false;
else if(livingEntity.isSpectator())
return false;
else if(unableToMove())
return false;
else if(summon.distanceToSqr(livingEntity) < (double) (startDistance * startDistance))
return false;
else
owner = livingEntity;

return true;
}

public boolean canContinueToUse() {
if(navigation.isDone())
return false;
else if(unableToMove())
return false;
else
return !(summon.distanceToSqr(owner) <= (double) (stopDistance * stopDistance));
}

private boolean unableToMove() {
return summon.isPassenger() || summon.isLeashed();
}

public void start() {
timeToRecalcPath = 0;
oldWaterCost = summon.getPathfindingMalus(BlockPathTypes.WATER);
summon.setPathfindingMalus(BlockPathTypes.WATER, 0f);
}

public void stop() {
owner = null;
navigation.stop();
summon.setPathfindingMalus(BlockPathTypes.WATER, oldWaterCost);
}

public void tick() {
summon.getLookControl().setLookAt(owner, 10f, (float) summon.getMaxHeadXRot());

if(--timeToRecalcPath <= 0) {
timeToRecalcPath = adjustedTickDelay(10);

if(summon.distanceToSqr(owner) >= 144)
teleportToOwner();
else
navigation.moveTo(owner, speedModifier);
}
}

private void teleportToOwner() {
BlockPos blockPos = owner.blockPosition();

for(int i = 0; i < 10; ++i) {
int j = randomIntInclusive(-MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING, MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING);
int k = randomIntInclusive(-MAX_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING, MAX_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING);
int l = randomIntInclusive(-MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING, MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING);
boolean bl = maybeTeleportTo(blockPos.getX() + j, blockPos.getY() + k, blockPos.getZ() + l);

if(bl)
return;
}

}

private boolean maybeTeleportTo(int x, int y, int z) {
if(Math.abs((double) x - owner.getX()) < MIN_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING && Math.abs((double) z - owner.getZ()) < MIN_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING) {
return false;
}
else if(!canTeleportTo(new BlockPos(x, y, z))) {
return false;
}
else {
summon.moveTo((double) x + 0.5, y, (double) z + 0.5, summon.getYRot(), summon.getXRot());
navigation.stop();
return true;
}
}

private boolean canTeleportTo(BlockPos pos) {
BlockPathTypes blockPathTypes = WalkNodeEvaluator.getBlockPathTypeStatic(level, pos.mutable());
if(blockPathTypes != BlockPathTypes.WALKABLE) {
return false;
}
else {
BlockState blockState = level.getBlockState(pos.below());

if(!canFly && blockState.getBlock() instanceof LeavesBlock) {
return false;
}
else {
BlockPos blockPos = pos.subtract(summon.blockPosition());
return level.noCollision(summon, summon.getBoundingBox().move(blockPos));
}
}
}

private int randomIntInclusive(int min, int max) {
return summon.getRandom().nextInt(max - min + 1) + min;
}
}
Loading

0 comments on commit a80197c

Please sign in to comment.