Commit 437a3695 by Jonathan Thomas

Added `PlayerBaseGoal` class to allow **goals/behaviors** to **continue** after…

Added `PlayerBaseGoal` class to allow **goals/behaviors** to **continue** after a player **respawns** / logs out / logs in. Fixed teleport issue to be closer to player position, and fixed a regression caused by teleport that stopped following entities that were too far away.
parent 13979b85
Pipeline #12563 passed with stages
in 2 minutes 21 seconds
......@@ -7,15 +7,17 @@ All notable changes to **CreatureChat** are documented in this file. The format
## [Unreleased]
### Added
- New **PROTECT** behavior: defend a player from attacks!
- **Native attack abilities** (when using the ATTACK or PROTECT behaviors) for hostile mob types
- New **PROTECT** behavior: defend a player from attacks
- New **UNPROTECT** behavior: stop defending a player from attacks
- **Native ATTACK abilities** (when using the attack or protect behaviors) for hostile mob types
- **End of Game** triggered by max friendship with the **EnderDragon**!
### Changed
- Improved **FLEE** behavior, to make it more reliable and more random.
- Improved **FOLLOW** behavior, supporting teleporting entities (Enderman, Endermite, and Shulker)
- Improved **FOLLOW** behavior, support **teleporting** entities (*Enderman, Endermite, and Shulker*)
- Refactored **ATTACK** behavior to allow more flexibility (in order to support PROTECT behavior)
- Updated ServerEntityFinder::getEntityByUUID to be more generic and so it can find players and mobs.
- Updated `ServerEntityFinder::getEntityByUUID` to be more generic and so it can find players and mobs.
- Added `PlayerBaseGoal` class to allow **goals/behaviors** to **continue** after a player **respawns** / logs out / logs in
## [1.0.6] - 2024-06-17
......
......@@ -2,7 +2,6 @@ package com.owlmaddie.goals;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.RangedAttackMob;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.mob.Angerable;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.mob.MobEntity;
......@@ -18,9 +17,8 @@ import java.util.EnumSet;
* The {@code AttackPlayerGoal} class instructs a Mob Entity to show aggression towards a target Entity.
* For passive entities like chickens (or hostile entities in creative mode), damage is simulated with particles.
*/
public class AttackPlayerGoal extends Goal {
public class AttackPlayerGoal extends PlayerBaseGoal {
protected final MobEntity attackerEntity;
protected LivingEntity targetEntity;
protected final double speed;
protected enum EntityState { MOVING_TOWARDS_PLAYER, IDLE, CHARGING, ATTACKING, LEAPING }
protected EntityState currentState = EntityState.IDLE;
......@@ -31,7 +29,7 @@ public class AttackPlayerGoal extends Goal {
protected final double ATTACK_DISTANCE = 4D; // 2 blocks away
public AttackPlayerGoal(LivingEntity targetEntity, MobEntity attackerEntity, double speed) {
this.targetEntity = targetEntity;
super(targetEntity);
this.attackerEntity = attackerEntity;
this.speed = speed;
this.setControls(EnumSet.of(Control.MOVE, Control.LOOK, Control.TARGET));
......@@ -44,12 +42,12 @@ public class AttackPlayerGoal extends Goal {
@Override
public boolean canStart() {
return isGoalActive();
return super.canStart() && isGoalActive();
}
@Override
public boolean shouldContinue() {
return isGoalActive();
return super.canStart() && isGoalActive();
}
@Override
......
package com.owlmaddie.goals;
import net.minecraft.entity.ai.FuzzyTargeting;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.ai.pathing.Path;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.mob.PathAwareEntity;
......@@ -14,14 +13,13 @@ import java.util.EnumSet;
* The {@code FleePlayerGoal} class instructs a Mob Entity to flee from the current player
* and only recalculates path when it has reached its destination and the player is close again.
*/
public class FleePlayerGoal extends Goal {
public class FleePlayerGoal extends PlayerBaseGoal {
private final MobEntity entity;
private ServerPlayerEntity targetPlayer;
private final double speed;
private final float fleeDistance;
public FleePlayerGoal(ServerPlayerEntity player, MobEntity entity, double speed, float fleeDistance) {
this.targetPlayer = player;
super(player);
this.entity = entity;
this.speed = speed;
this.fleeDistance = fleeDistance;
......@@ -30,12 +28,12 @@ public class FleePlayerGoal extends Goal {
@Override
public boolean canStart() {
return this.targetPlayer != null && this.entity.squaredDistanceTo(this.targetPlayer) < fleeDistance * fleeDistance;
return super.canStart() && this.entity.squaredDistanceTo(this.targetEntity) < fleeDistance * fleeDistance;
}
@Override
public boolean shouldContinue() {
return this.targetPlayer != null && this.entity.squaredDistanceTo(this.targetPlayer) < fleeDistance * fleeDistance;
return super.canStart() && this.entity.squaredDistanceTo(this.targetEntity) < fleeDistance * fleeDistance;
}
@Override
......@@ -46,7 +44,7 @@ public class FleePlayerGoal extends Goal {
private void fleeFromPlayer() {
int roundedFleeDistance = Math.round(fleeDistance);
Vec3d fleeTarget = FuzzyTargeting.findFrom((PathAwareEntity)this.entity, roundedFleeDistance,
roundedFleeDistance, this.entity.getPos());
roundedFleeDistance, this.targetEntity.getPos());
if (fleeTarget != null) {
Path path = this.entity.getNavigation().findPathTo(fleeTarget.x, fleeTarget.y, fleeTarget.z, 0);
......
......@@ -2,7 +2,6 @@ package com.owlmaddie.goals;
import com.owlmaddie.controls.LookControls;
import net.minecraft.entity.ai.FuzzyTargeting;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.ai.pathing.EntityNavigation;
import net.minecraft.entity.mob.*;
import net.minecraft.server.network.ServerPlayerEntity;
......@@ -11,16 +10,15 @@ import net.minecraft.util.math.Vec3d;
import java.util.EnumSet;
/**
* The {@code FollowPlayerGoal} class instructs a Mob Entity to follow the current player.
* The {@code FollowPlayerGoal} class instructs a Mob Entity to follow the current target entity.
*/
public class FollowPlayerGoal extends Goal {
public class FollowPlayerGoal extends PlayerBaseGoal {
private final MobEntity entity;
private ServerPlayerEntity targetPlayer;
private final EntityNavigation navigation;
private final double speed;
public FollowPlayerGoal(ServerPlayerEntity player, MobEntity entity, double speed) {
this.targetPlayer = player;
super(player);
this.entity = entity;
this.speed = speed;
this.navigation = entity.getNavigation();
......@@ -30,13 +28,13 @@ public class FollowPlayerGoal extends Goal {
@Override
public boolean canStart() {
// Start only if the target player is more than 8 blocks away
return this.targetPlayer != null && this.targetPlayer.isAlive() && this.entity.squaredDistanceTo(this.targetPlayer) > 64;
return super.canStart() && this.entity.squaredDistanceTo(this.targetEntity) > 64;
}
@Override
public boolean shouldContinue() {
// Continue unless the entity gets within 3 blocks of the player
return this.targetPlayer != null && this.targetPlayer.isAlive() && this.entity.squaredDistanceTo(this.targetPlayer) > 9;
return super.canStart() && this.entity.squaredDistanceTo(this.targetEntity) > 9;
}
@Override
......@@ -47,22 +45,24 @@ public class FollowPlayerGoal extends Goal {
@Override
public void tick() {
if (this.entity.squaredDistanceTo(this.targetPlayer) > 256) {
// If the entity is too far away (more than 16 blocks), teleport it within 8 blocks of the player
if (this.entity instanceof EndermanEntity || this.entity instanceof EndermiteEntity || this.entity instanceof ShulkerEntity) {
Vec3d targetPos = findTeleportPosition(8);
if (this.entity instanceof EndermanEntity || this.entity instanceof EndermiteEntity || this.entity instanceof ShulkerEntity) {
// Certain entities should teleport to the player if they get too far
if (this.entity.squaredDistanceTo(this.targetEntity) > 256) {
Vec3d targetPos = findTeleportPosition(12);
if (targetPos != null) {
this.entity.teleport(targetPos.x, targetPos.y, targetPos.z);
}
}
} else {
// Look at the player and start moving towards them
LookControls.lookAtPlayer(this.targetPlayer, this.entity);
this.navigation.startMovingTo(this.targetPlayer, this.speed);
if (this.targetEntity instanceof ServerPlayerEntity) {
LookControls.lookAtPlayer((ServerPlayerEntity)this.targetEntity, this.entity);
}
this.navigation.startMovingTo(this.targetEntity, this.speed);
}
}
private Vec3d findTeleportPosition(int distance) {
return FuzzyTargeting.findTo((PathAwareEntity)this.entity, distance, distance, this.targetPlayer.getPos());
return FuzzyTargeting.findTo((PathAwareEntity)this.entity, distance, distance, this.targetEntity.getPos());
}
}
package com.owlmaddie.goals;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
/**
* The {@code PlayerBaseGoal} class sets a targetEntity, and will automatically update the targetEntity
* when a player die's and respawns, or logs back in, etc... Other types of targetEntity classes will
* be set to null after they die.
*/
public abstract class PlayerBaseGoal extends Goal {
protected LivingEntity targetEntity;
private final int updateInterval = 20;
private int tickCounter = 0;
public PlayerBaseGoal(LivingEntity targetEntity) {
this.targetEntity = targetEntity;
}
@Override
public boolean canStart() {
if (++tickCounter >= updateInterval) {
tickCounter = 0;
updateTargetEntity();
}
return targetEntity != null && targetEntity.isAlive();
}
private void updateTargetEntity() {
if (targetEntity != null && !targetEntity.isAlive()) {
if (targetEntity instanceof ServerPlayerEntity) {
ServerWorld world = (ServerWorld) targetEntity.getWorld();
ServerPlayerEntity lookupPlayer = (ServerPlayerEntity)world.getPlayerByUuid(targetEntity.getUuid());
if (lookupPlayer != null && lookupPlayer.isAlive()) {
// Update player to alive player with same UUID
targetEntity = lookupPlayer;
}
} else {
targetEntity = null;
}
}
}
}
......@@ -29,7 +29,7 @@ public class ProtectPlayerGoal extends AttackPlayerGoal {
}
if (this.targetEntity != null && !this.targetEntity.isAlive()) {
// clear target
// clear dead target
this.targetEntity = null;
}
......
......@@ -47,7 +47,7 @@ Include as many behaviors as needed at the end of the message. These are the ONL
<UNFOLLOW> Stop following the player location. If the player asks you to stay, wait, or stop following them, please output this behavior.
<FLEE> Flee from the player (if you are weak or timid). If the player threatens or scares you, please output this behavior to stay away from the player.
<ATTACK> Attack the player (if you are strong and brave). If the player threatens or scares you, please output this behavior to attack the player and defend yourself.
<PROTECT> Protect the player when they are attacked (if you are strong and brave).
<PROTECT> Protect the player when they are attacked (if you are strong and brave). This only protects the player.
<UNPROTECT> Stop protecting the player
Output Syntax:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment