mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
Rewrite entity minion/discorded ai changes to be non-destructive
This commit is contained in:
parent
b034ed3598
commit
1bbc5b7569
5 changed files with 140 additions and 53 deletions
|
@ -1,6 +1,7 @@
|
||||||
package com.minelittlepony.unicopia.entity;
|
package com.minelittlepony.unicopia.entity;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -16,6 +17,7 @@ import com.minelittlepony.unicopia.entity.ai.BreakHeartGoal;
|
||||||
import com.minelittlepony.unicopia.entity.ai.DynamicTargetGoal;
|
import com.minelittlepony.unicopia.entity.ai.DynamicTargetGoal;
|
||||||
import com.minelittlepony.unicopia.entity.ai.EatMuffinGoal;
|
import com.minelittlepony.unicopia.entity.ai.EatMuffinGoal;
|
||||||
import com.minelittlepony.unicopia.entity.ai.FleeExplosionGoal;
|
import com.minelittlepony.unicopia.entity.ai.FleeExplosionGoal;
|
||||||
|
import com.minelittlepony.unicopia.entity.ai.PredicatedGoal;
|
||||||
import com.minelittlepony.unicopia.entity.ai.PrioritizedActiveTargetGoal;
|
import com.minelittlepony.unicopia.entity.ai.PrioritizedActiveTargetGoal;
|
||||||
import com.minelittlepony.unicopia.entity.ai.TargettingUtil;
|
import com.minelittlepony.unicopia.entity.ai.TargettingUtil;
|
||||||
import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal;
|
import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal;
|
||||||
|
@ -34,6 +36,7 @@ import net.minecraft.entity.passive.*;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.nbt.NbtElement;
|
import net.minecraft.nbt.NbtElement;
|
||||||
|
import net.minecraft.particle.ParticleTypes;
|
||||||
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
|
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
|
||||||
|
@ -45,13 +48,11 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
private final EntityReference<LivingEntity> owner = new EntityReference<>();
|
private final EntityReference<LivingEntity> owner = new EntityReference<>();
|
||||||
|
|
||||||
private Optional<GoalSelector> goals = Optional.empty();
|
private Optional<GoalSelector> goals = Optional.empty();
|
||||||
private Optional<GoalSelector> targets = Optional.empty();
|
|
||||||
|
|
||||||
private int eatTimer;
|
private int eatTimer;
|
||||||
@Nullable
|
@Nullable
|
||||||
private EatMuffinGoal eatMuffinGoal;
|
private EatMuffinGoal eatMuffinGoal;
|
||||||
|
|
||||||
private boolean discordedChanged = true;
|
|
||||||
private int smittenTicks;
|
private int smittenTicks;
|
||||||
|
|
||||||
private final Predicate<LivingEntity> targetPredicate = TargetSelecter.<LivingEntity>validTarget(() -> getOriginatingCaster().getAffinity(), this).and(e -> {
|
private final Predicate<LivingEntity> targetPredicate = TargetSelecter.<LivingEntity>validTarget(() -> getOriginatingCaster().getAffinity(), this).and(e -> {
|
||||||
|
@ -80,9 +81,6 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
@Override
|
@Override
|
||||||
public void setMaster(LivingEntity owner) {
|
public void setMaster(LivingEntity owner) {
|
||||||
this.owner.set(owner);
|
this.owner.set(owner);
|
||||||
if (owner != null) {
|
|
||||||
targets.ifPresent(this::initMinionAi);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMinion() {
|
public boolean isMinion() {
|
||||||
|
@ -104,7 +102,6 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
|
|
||||||
public void setDiscorded(boolean discorded) {
|
public void setDiscorded(boolean discorded) {
|
||||||
this.discorded.set(discorded);
|
this.discorded.set(discorded);
|
||||||
discordedChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -118,17 +115,12 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<GoalSelector> getTargets() {
|
|
||||||
return targets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<GoalSelector> getGoals() {
|
public Optional<GoalSelector> getGoals() {
|
||||||
return goals;
|
return goals;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initAi(GoalSelector goals, GoalSelector targets) {
|
public void initAi(GoalSelector goals, GoalSelector targets) {
|
||||||
this.goals = Optional.of(goals);
|
this.goals = Optional.of(goals);
|
||||||
this.targets = Optional.of(targets);
|
|
||||||
|
|
||||||
if (entity instanceof MagicImmune) {
|
if (entity instanceof MagicImmune) {
|
||||||
return;
|
return;
|
||||||
|
@ -153,48 +145,31 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
goals.add(3, new FleeExplosionGoal(tameable, 6, 1, 1.2));
|
goals.add(3, new FleeExplosionGoal(tameable, 6, 1, 1.2));
|
||||||
}
|
}
|
||||||
|
|
||||||
initMinionAi(targets);
|
|
||||||
initDiscordedAi();
|
|
||||||
|
|
||||||
if (entity instanceof CreeperEntity mob) {
|
if (entity instanceof CreeperEntity mob) {
|
||||||
goals.add(1, new FleeEntityGoal<>(mob, LivingEntity.class, 10, 1.5, 1.9, AmuletSelectors.ALICORN_AMULET));
|
goals.add(1, new FleeEntityGoal<>(mob, LivingEntity.class, 10, 1.5, 1.9, AmuletSelectors.ALICORN_AMULET));
|
||||||
}
|
}
|
||||||
if (entity instanceof PassiveEntity mob) {
|
if (entity instanceof PassiveEntity mob) {
|
||||||
goals.add(1, new FleeEntityGoal<>(mob, LivingEntity.class, 10, 1.1, 1.7, AmuletSelectors.ALICORN_AMULET_AFTER_1_DAYS));
|
goals.add(1, new FleeEntityGoal<>(mob, LivingEntity.class, 10, 1.1, 1.7, AmuletSelectors.ALICORN_AMULET_AFTER_1_DAYS));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void initMinionAi(GoalSelector targets) {
|
final BooleanSupplier isMinion = () -> getMasterReference().isSet();
|
||||||
if (entity instanceof MagicImmune || !getMasterReference().isSet()) {
|
final BooleanSupplier isNotMinion = () -> !isMinion.getAsBoolean();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearGoals(targets);
|
PredicatedGoal.applyToAll(targets, isNotMinion);
|
||||||
targets.add(2, new ActiveEnemyGoal<>(PlayerEntity.class));
|
targets.add(2, new PredicatedGoal(new ActiveEnemyGoal<>(PlayerEntity.class), isMinion));
|
||||||
targets.add(2, new ActiveEnemyGoal<>(HostileEntity.class));
|
targets.add(2, new PredicatedGoal(new ActiveEnemyGoal<>(HostileEntity.class), isMinion));
|
||||||
targets.add(2, new ActiveEnemyGoal<>(SlimeEntity.class));
|
targets.add(2, new PredicatedGoal(new ActiveEnemyGoal<>(SlimeEntity.class), isMinion));
|
||||||
}
|
|
||||||
|
|
||||||
private void initDiscordedAi() {
|
|
||||||
if (entity instanceof MagicImmune || getMasterReference().isSet() || !isDiscorded()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
targets.ifPresent(this::clearGoals);
|
|
||||||
// the brain drain
|
|
||||||
entity.getBrain().clear();
|
|
||||||
if (entity instanceof MobEntity mob) {
|
if (entity instanceof MobEntity mob) {
|
||||||
mob.setTarget(null);
|
final BooleanSupplier isDiscorded = () -> isNotMinion.getAsBoolean() && isDiscorded();
|
||||||
goals.ifPresent(goalSelector -> {
|
PredicatedGoal.applyToAll(goals, () -> !isDiscorded.getAsBoolean());
|
||||||
clearGoals(goalSelector);
|
|
||||||
goalSelector.add(1, new SwimGoal(mob));
|
goals.add(1, new PredicatedGoal(new SwimGoal(mob), isDiscorded));
|
||||||
if (mob instanceof PathAwareEntity pae) {
|
if (mob instanceof PathAwareEntity pae) {
|
||||||
goalSelector.add(5, new WanderAroundFarGoal(pae, 0.8));
|
goals.add(5, new PredicatedGoal(new WanderAroundFarGoal(pae, 0.8), isDiscorded));
|
||||||
}
|
}
|
||||||
goalSelector.add(6, new LookAtEntityGoal(mob, PlayerEntity.class, 8.0f));
|
goals.add(6, new PredicatedGoal(new LookAtEntityGoal(mob, PlayerEntity.class, 8.0f), isDiscorded));
|
||||||
goalSelector.add(6, new LookAroundGoal(mob));
|
goals.add(6, new PredicatedGoal(new LookAroundGoal(mob), isDiscorded));
|
||||||
});
|
|
||||||
} else {
|
|
||||||
goals.ifPresent(this::clearGoals);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,9 +181,8 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean beforeUpdate() {
|
public boolean beforeUpdate() {
|
||||||
if (discordedChanged) {
|
if (isDiscorded()) {
|
||||||
discordedChanged = false;
|
spawnParticles(ParticleTypes.ELECTRIC_SPARK, 1);
|
||||||
initDiscordedAi();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isClient() && smittenTicks > 0) {
|
if (!isClient() && smittenTicks > 0) {
|
||||||
|
@ -220,10 +194,6 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
return super.beforeUpdate();
|
return super.beforeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearGoals(GoalSelector t) {
|
|
||||||
t.clear(g -> true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateConsumption() {
|
private void updateConsumption() {
|
||||||
if (isClient()) {
|
if (isClient()) {
|
||||||
eatTimer = eating.get();
|
eatTimer = eating.get();
|
||||||
|
@ -312,9 +282,6 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
|
||||||
super.fromNBT(compound, lookup);
|
super.fromNBT(compound, lookup);
|
||||||
if (compound.contains("master", NbtElement.COMPOUND_TYPE)) {
|
if (compound.contains("master", NbtElement.COMPOUND_TYPE)) {
|
||||||
getMasterReference().fromNBT(compound.getCompound("master"), lookup);
|
getMasterReference().fromNBT(compound.getCompound("master"), lookup);
|
||||||
if (getMasterReference().isSet()) {
|
|
||||||
targets.ifPresent(this::initMinionAi);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
physics.fromNBT(compound, lookup);
|
physics.fromNBT(compound, lookup);
|
||||||
setDiscorded(compound.getBoolean("discorded"));
|
setDiscorded(compound.getBoolean("discorded"));
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.minelittlepony.unicopia.entity.ai;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.entity.ai.goal.GoalSelector;
|
||||||
|
import net.minecraft.entity.ai.goal.PrioritizedGoal;
|
||||||
|
|
||||||
|
public class PredicatedGoal extends Goal {
|
||||||
|
private final Goal goal;
|
||||||
|
private final BooleanSupplier predicate;
|
||||||
|
|
||||||
|
public static void applyToAll(GoalSelector goals, BooleanSupplier predicate) {
|
||||||
|
Set<PrioritizedGoal> existingTasks = new HashSet<>(goals.getGoals());
|
||||||
|
goals.clear(g -> !(g instanceof PredicatedGoal));
|
||||||
|
existingTasks.forEach(goal -> {
|
||||||
|
if (!(goal.getGoal() instanceof PredicatedGoal)) {
|
||||||
|
goals.add(goal.getPriority(), new PredicatedGoal(goal.getGoal(), predicate));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public PredicatedGoal(Goal goal, BooleanSupplier predicate) {
|
||||||
|
this.goal = goal;
|
||||||
|
this.predicate = predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canStart() {
|
||||||
|
return predicate.getAsBoolean() && goal.canStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldContinue() {
|
||||||
|
return predicate.getAsBoolean() && goal.shouldContinue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canStop() {
|
||||||
|
return goal.canStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
if (predicate.getAsBoolean()) {
|
||||||
|
goal.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
goal.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRunEveryTick() {
|
||||||
|
return goal.shouldRunEveryTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
goal.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setControls(EnumSet<Goal.Control> controls) {
|
||||||
|
goal.setControls(controls);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumSet<Goal.Control> getControls() {
|
||||||
|
return goal.getControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return o != null && getClass() == o.getClass() ? goal.equals(((PredicatedGoal)o).goal) : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return goal.hashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -120,6 +120,11 @@ public class BellItem extends Item {
|
||||||
user.setTarget(null);
|
user.setTarget(null);
|
||||||
user.playSound(USounds.ITEM_GROGAR_BELL_STOP_USING, 0.2F, 0.3F);
|
user.playSound(USounds.ITEM_GROGAR_BELL_STOP_USING, 0.2F, 0.3F);
|
||||||
if (target instanceof Creature creature && (completed || target.asEntity().getHealth() < (target.asEntity().getMaxHealth() * 0.5F) + 1)) {
|
if (target instanceof Creature creature && (completed || target.asEntity().getHealth() < (target.asEntity().getMaxHealth() * 0.5F) + 1)) {
|
||||||
|
ItemStack handStack = creature.asEntity().getStackInHand(Hand.MAIN_HAND);
|
||||||
|
if (!handStack.isEmpty()) {
|
||||||
|
creature.asEntity().setStackInHand(Hand.MAIN_HAND, ItemStack.EMPTY);
|
||||||
|
creature.asEntity().dropStack(handStack);
|
||||||
|
}
|
||||||
creature.setDiscorded(true);
|
creature.setDiscorded(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.minelittlepony.unicopia.mixin;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.*;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.entity.Creature;
|
||||||
|
import com.minelittlepony.unicopia.entity.Living;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.ai.brain.Brain;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
|
||||||
|
@Mixin(Brain.class)
|
||||||
|
abstract class MixinBrain<E extends LivingEntity> {
|
||||||
|
@Inject(method = "tick", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void onTick(ServerWorld world, E entity, CallbackInfo info) {
|
||||||
|
if (Living.living(entity) instanceof Creature c && c.isDiscorded()) {
|
||||||
|
info.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
"MixinBlockEntityType",
|
"MixinBlockEntityType",
|
||||||
"MixinBlockItem",
|
"MixinBlockItem",
|
||||||
"MixinBoatEntity",
|
"MixinBoatEntity",
|
||||||
|
"MixinBrain",
|
||||||
"MixinChunkBlockLightProvider",
|
"MixinChunkBlockLightProvider",
|
||||||
"MixinComponentHolder",
|
"MixinComponentHolder",
|
||||||
"MutableBlockLightStorage",
|
"MutableBlockLightStorage",
|
||||||
|
|
Loading…
Reference in a new issue