implement more config options
This commit is contained in:
@@ -4,6 +4,11 @@ All notable changes since version 1.0.0 are documented in this file.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- Added separate configuration for mob execution time, executor movement, and knocked-player movement.
|
||||
- Added configurable NPC help-up behavior for owned pets, friendly villagers, golems, and allowlisted modded NPCs.
|
||||
- NPC helpers only begin a rescue when they have no hostile target and no living threats remain nearby.
|
||||
|
||||
### Fixed
|
||||
- Improved multiplayer target selection for execute, carry, and help-up interactions so the nearest valid knocked player is selected instead of an arbitrary player.
|
||||
- Added same-dimension and range validation for ongoing player execution and help-up interactions.
|
||||
|
||||
@@ -53,6 +53,7 @@ public class HelpUpHandler {
|
||||
return;
|
||||
}
|
||||
if (helpUpAttempts.containsKey(knockedId)) return;
|
||||
if (NpcHelpUpHandler.isBeingHelped(knockedId)) return;
|
||||
helpUpAttempts.put(knockedId, new HelpUpAttempt(helper.getUUID()));
|
||||
helper.sendSystemMessage(Component.translatable("message.knockedback.help_up_started", target.getDisplayName()));
|
||||
target.sendSystemMessage(Component.translatable("message.knockedback.being_helped", helper.getDisplayName()));
|
||||
|
||||
@@ -56,7 +56,7 @@ public class KnockedManager {
|
||||
applyKnockedEffects(sp);
|
||||
boolean grounded = sp.onGround() || sp.isInWater();
|
||||
sp.setNoGravity(false);
|
||||
if (!grounded) {
|
||||
if (!grounded && !Config.COMMON.knockedMovementEnabled.get()) {
|
||||
sp.setDeltaMovement(0, sp.getDeltaMovement().y() - 0.08, 0);
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,7 @@ public class KnockedManager {
|
||||
MobKillHandler.clearKillAttempt(entity.getUUID());
|
||||
PlayerExecutionHandler.cancelExecution(entity.getUUID());
|
||||
HelpUpHandler.cancelHelpUp(entity.getUUID());
|
||||
NpcHelpUpHandler.cancelHelpUp(entity.getUUID());
|
||||
entity.setPose(Pose.STANDING);
|
||||
if (entity instanceof ServerPlayer sp) {
|
||||
NetworkHandler.CHANNEL.send(
|
||||
@@ -125,7 +126,8 @@ public class KnockedManager {
|
||||
}
|
||||
if (MobKillHandler.isBeingMobExecuted(playerId)
|
||||
|| PlayerExecutionHandler.isBeingPlayerExecuted(playerId)
|
||||
|| HelpUpHandler.isBeingHelped(playerId)) {
|
||||
|| HelpUpHandler.isBeingHelped(playerId)
|
||||
|| NpcHelpUpHandler.isBeingHelped(playerId)) {
|
||||
ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId);
|
||||
if (p != null) {
|
||||
applyKnockedEffects(p);
|
||||
@@ -216,6 +218,7 @@ public class KnockedManager {
|
||||
PlayerExecutionHandler.cancelExecution(playerId);
|
||||
PlayerExecutionHandler.cancelExecutionByExecutor(playerId);
|
||||
HelpUpHandler.cancelHelpUp(playerId);
|
||||
NpcHelpUpHandler.cancelHelpUp(playerId);
|
||||
HelpUpHandler.cancelHelpUpByHelper(playerId);
|
||||
CarryManager.stopCarry(playerId);
|
||||
CarryManager.stopCarrying(playerId);
|
||||
|
||||
@@ -22,7 +22,7 @@ import static net.fretux.knockedback.KnockedManager.setGripped;
|
||||
|
||||
public class MobKillHandler {
|
||||
public static int getExecutionTime() {
|
||||
return Config.COMMON.executionTime.get();
|
||||
return Config.COMMON.mobExecutionTime.get();
|
||||
}
|
||||
private static final Map<UUID, KillAttempt> killAttempts = new HashMap<>();
|
||||
|
||||
@@ -110,7 +110,8 @@ public class MobKillHandler {
|
||||
continue;
|
||||
}
|
||||
if (PlayerExecutionHandler.isBeingPlayerExecuted(knockedId)
|
||||
|| HelpUpHandler.isBeingHelped(knockedId)) {
|
||||
|| HelpUpHandler.isBeingHelped(knockedId)
|
||||
|| NpcHelpUpHandler.isBeingHelped(knockedId)) {
|
||||
setGripped(knocked, false);
|
||||
continue;
|
||||
}
|
||||
@@ -144,7 +145,9 @@ public class MobKillHandler {
|
||||
executeKnockedPlayer(knocked, mob);
|
||||
continue;
|
||||
} else {
|
||||
mob.getNavigation().stop();
|
||||
if (!Config.COMMON.executorMovementEnabled.get()) {
|
||||
mob.getNavigation().stop();
|
||||
}
|
||||
mob.setTarget(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package net.fretux.knockedback;
|
||||
|
||||
import net.fretux.knockedback.config.Config;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.TamableAnimal;
|
||||
import net.minecraft.world.entity.animal.IronGolem;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.npc.Villager;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = KnockedBack.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||
public final class NpcHelpUpHandler {
|
||||
private static final double ASSIST_DISTANCE = 2.25D;
|
||||
private static final Map<UUID, AssistAttempt> ATTEMPTS = new HashMap<>();
|
||||
|
||||
private NpcHelpUpHandler() {}
|
||||
|
||||
public static boolean isBeingHelped(UUID playerId) {
|
||||
return ATTEMPTS.containsKey(playerId);
|
||||
}
|
||||
|
||||
public static void cancelHelpUp(UUID playerId) {
|
||||
AssistAttempt attempt = ATTEMPTS.remove(playerId);
|
||||
if (attempt != null) {
|
||||
Mob helper = findMob(attempt.helperId);
|
||||
if (helper != null) helper.getNavigation().stop();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerTick(TickEvent.ServerTickEvent event) {
|
||||
if (event.phase != TickEvent.Phase.END || !event.side.isServer()) return;
|
||||
if (!Config.COMMON.npcHelpUpEnabled.get()) {
|
||||
for (UUID playerId : new HashSet<>(ATTEMPTS.keySet())) cancelHelpUp(playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
for (UUID playerId : KnockedManager.getKnockedUuids()) {
|
||||
ServerPlayer player = NetworkHandlerHelper.getPlayerByUuid(
|
||||
net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(), playerId);
|
||||
if (player == null || !player.isAlive() || CarryManager.isBeingCarried(playerId)
|
||||
|| HelpUpHandler.isBeingHelped(playerId)
|
||||
|| PlayerExecutionHandler.isBeingPlayerExecuted(playerId)
|
||||
|| MobKillHandler.isBeingMobExecuted(playerId)) {
|
||||
cancelHelpUp(playerId);
|
||||
continue;
|
||||
}
|
||||
|
||||
AssistAttempt attempt = ATTEMPTS.get(playerId);
|
||||
Mob helper = attempt == null ? findHelper(player) : findMob(attempt.helperId);
|
||||
if (!isValidHelper(helper, player) || hasThreats(player, helper)) {
|
||||
cancelHelpUp(playerId);
|
||||
continue;
|
||||
}
|
||||
if (attempt == null) {
|
||||
attempt = new AssistAttempt(helper.getUUID(), Config.COMMON.npcHelpUpTime.get());
|
||||
ATTEMPTS.put(playerId, attempt);
|
||||
}
|
||||
|
||||
if (helper.distanceToSqr(player) > ASSIST_DISTANCE * ASSIST_DISTANCE) {
|
||||
helper.getNavigation().moveTo(player, 1.1D);
|
||||
attempt.timeLeft = Config.COMMON.npcHelpUpTime.get();
|
||||
} else {
|
||||
helper.getNavigation().stop();
|
||||
helper.getLookControl().setLookAt(player, 30.0F, 30.0F);
|
||||
if (--attempt.timeLeft <= 0) complete(playerId, player, helper);
|
||||
}
|
||||
}
|
||||
|
||||
ATTEMPTS.keySet().removeIf(id -> !KnockedManager.getKnockedUuids().contains(id));
|
||||
}
|
||||
|
||||
private static void complete(UUID playerId, ServerPlayer player, Mob helper) {
|
||||
ATTEMPTS.remove(playerId);
|
||||
helper.getNavigation().stop();
|
||||
KnockedManager.removeKnockedState(player);
|
||||
player.heal(Config.COMMON.npcHelpUpHealAmount.get().floatValue());
|
||||
}
|
||||
|
||||
private static Mob findHelper(ServerPlayer player) {
|
||||
double range = Config.COMMON.npcHelpUpSearchRange.get();
|
||||
return ((ServerLevel) player.level()).getEntitiesOfClass(Mob.class,
|
||||
player.getBoundingBox().inflate(range), mob -> isValidHelper(mob, player))
|
||||
.stream().filter(mob -> !hasThreats(player, mob))
|
||||
.min(java.util.Comparator.comparingDouble(player::distanceToSqr)).orElse(null);
|
||||
}
|
||||
|
||||
private static boolean isValidHelper(Mob mob, ServerPlayer player) {
|
||||
if (mob == null || !mob.isAlive() || mob.level() != player.level()) return false;
|
||||
ResourceLocation id = ForgeRegistries.ENTITY_TYPES.getKey(mob.getType());
|
||||
if (id == null || !Config.COMMON.npcHelpUpAllowlist.get().contains(id.toString())) return false;
|
||||
if (mob instanceof TamableAnimal pet) return pet.isTame() && pet.isOwnedBy(player);
|
||||
if (mob instanceof Villager villager) return villager.getPlayerReputation(player) > 0;
|
||||
if (mob instanceof IronGolem golem) return golem.isPlayerCreated() || golem.getTarget() != player;
|
||||
return !(mob instanceof Monster) && mob.getTarget() != player;
|
||||
}
|
||||
|
||||
private static boolean hasThreats(ServerPlayer player, Mob helper) {
|
||||
if (helper.getTarget() != null && helper.getTarget().isAlive()) return true;
|
||||
double range = Config.COMMON.npcThreatRange.get();
|
||||
AABB area = player.getBoundingBox().minmax(helper.getBoundingBox()).inflate(range);
|
||||
return player.level().getEntitiesOfClass(Mob.class, area).stream().anyMatch(mob ->
|
||||
mob.isAlive() && mob != helper && (mob instanceof Monster
|
||||
|| mob.getTarget() == player || mob.getTarget() == helper));
|
||||
}
|
||||
|
||||
private static Mob findMob(UUID id) {
|
||||
var server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer();
|
||||
if (server == null) return null;
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
var entity = level.getEntity(id);
|
||||
if (entity instanceof Mob mob) return mob;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final class AssistAttempt {
|
||||
private final UUID helperId;
|
||||
private int timeLeft;
|
||||
|
||||
private AssistAttempt(UUID helperId, int timeLeft) {
|
||||
this.helperId = helperId;
|
||||
this.timeLeft = timeLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,8 @@ public class PlayerExecutionHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
|
||||
if (event.side.isServer() && event.phase == TickEvent.Phase.END && event.player instanceof ServerPlayer sp && isExecuting(sp.getUUID())) {
|
||||
if (event.side.isServer() && event.phase == TickEvent.Phase.END && event.player instanceof ServerPlayer sp
|
||||
&& isExecuting(sp.getUUID()) && !Config.COMMON.executorMovementEnabled.get()) {
|
||||
sp.setDeltaMovement(0, 0, 0);
|
||||
sp.setSprinting(false);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ public class Config {
|
||||
public static class Common {
|
||||
public final ForgeConfigSpec.IntValue knockedDuration;
|
||||
public final ForgeConfigSpec.IntValue executionTime;
|
||||
public final ForgeConfigSpec.IntValue mobExecutionTime;
|
||||
public final ForgeConfigSpec.BooleanValue knockedMovementEnabled;
|
||||
public final ForgeConfigSpec.BooleanValue executorMovementEnabled;
|
||||
public final ForgeConfigSpec.IntValue helpUpTime;
|
||||
public final ForgeConfigSpec.DoubleValue helpUpHealAmount;
|
||||
public final ForgeConfigSpec.BooleanValue helpUpEnabled;
|
||||
@@ -25,6 +28,12 @@ public class Config {
|
||||
public final ForgeConfigSpec.ConfigValue<List<? extends String>> mobExecutionAllowlist;
|
||||
public final ForgeConfigSpec.ConfigValue<List<? extends String>> knockedPotionEffects;
|
||||
public final ForgeConfigSpec.BooleanValue removeOtherPotionEffectsWhileKnocked;
|
||||
public final ForgeConfigSpec.BooleanValue npcHelpUpEnabled;
|
||||
public final ForgeConfigSpec.IntValue npcHelpUpTime;
|
||||
public final ForgeConfigSpec.DoubleValue npcHelpUpHealAmount;
|
||||
public final ForgeConfigSpec.DoubleValue npcHelpUpSearchRange;
|
||||
public final ForgeConfigSpec.DoubleValue npcThreatRange;
|
||||
public final ForgeConfigSpec.ConfigValue<List<? extends String>> npcHelpUpAllowlist;
|
||||
|
||||
public Common(ForgeConfigSpec.Builder builder) {
|
||||
builder.push("general");
|
||||
@@ -34,9 +43,21 @@ public class Config {
|
||||
.defineInRange("knockedDuration", 400, 100, 1200);
|
||||
|
||||
executionTime = builder
|
||||
.comment("How long (in ticks) it takes to execute a knocked player. Default: 60 (3 seconds).")
|
||||
.comment("How long (in ticks) it takes another player to execute a knocked player. Default: 60 (3 seconds).")
|
||||
.defineInRange("executionTime", 60, 20, 200);
|
||||
|
||||
mobExecutionTime = builder
|
||||
.comment("How long (in ticks) it takes a mob to execute a knocked player. Default: 60 (3 seconds).")
|
||||
.defineInRange("mobExecutionTime", 60, 0, 1200);
|
||||
|
||||
knockedMovementEnabled = builder
|
||||
.comment("If true, knocked players retain horizontal movement and can jump. Default: false.")
|
||||
.define("knockedMovementEnabled", false);
|
||||
|
||||
executorMovementEnabled = builder
|
||||
.comment("If true, players and mobs may move while executing a knocked player. Default: false.")
|
||||
.define("executorMovementEnabled", false);
|
||||
|
||||
helpUpEnabled = builder
|
||||
.comment("If true, players can help knocked players recover by holding the help-up key. Default: true.")
|
||||
.define("helpUpEnabled", true);
|
||||
@@ -49,6 +70,34 @@ public class Config {
|
||||
.comment("How much health a helped-up player receives after recovery. Default: 4.0 (2 hearts).")
|
||||
.defineInRange("helpUpHealAmount", 4.0D, 1.0D, 20.0D);
|
||||
|
||||
npcHelpUpEnabled = builder
|
||||
.comment("If true, friendly configured NPCs can help knocked players recover. Default: true.")
|
||||
.define("npcHelpUpEnabled", true);
|
||||
|
||||
npcHelpUpTime = builder
|
||||
.comment("How long (in ticks) an NPC must remain beside a player to help them up. Default: 60 (3 seconds).")
|
||||
.defineInRange("npcHelpUpTime", 60, 1, 1200);
|
||||
|
||||
npcHelpUpHealAmount = builder
|
||||
.comment("How much health an NPC-assisted player receives. Default: 4.0 (2 hearts).")
|
||||
.defineInRange("npcHelpUpHealAmount", 4.0D, 1.0D, 20.0D);
|
||||
|
||||
npcHelpUpSearchRange = builder
|
||||
.comment("How far friendly NPCs can notice a knocked player. Default: 16 blocks.")
|
||||
.defineInRange("npcHelpUpSearchRange", 16.0D, 2.0D, 64.0D);
|
||||
|
||||
npcThreatRange = builder
|
||||
.comment("Radius which must be free of living threats before an NPC will help. Default: 12 blocks.")
|
||||
.defineInRange("npcThreatRange", 12.0D, 1.0D, 64.0D);
|
||||
|
||||
npcHelpUpAllowlist = builder
|
||||
.comment("Entity ids which may help knocked players.",
|
||||
"Tamed animals must belong to the player; villagers require positive reputation.",
|
||||
"Other listed mobs, including modded guards, must not be hostile to the player.")
|
||||
.defineListAllowEmpty("npcHelpUpAllowlist",
|
||||
List.of("minecraft:wolf", "minecraft:cat", "minecraft:parrot", "minecraft:villager", "minecraft:iron_golem"),
|
||||
o -> o instanceof String);
|
||||
|
||||
mobExecutionEnabled = builder
|
||||
.comment("If true, mobs can execute knocked players. Default: true.")
|
||||
.define("mobExecutionEnabled", true);
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.fretux.knockedback.effects;
|
||||
|
||||
import net.fretux.knockedback.KnockedManager;
|
||||
import net.fretux.knockedback.CarryManager;
|
||||
import net.fretux.knockedback.config.Config;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
@@ -21,7 +22,9 @@ public class KnockedEffect {
|
||||
player.setSprinting(false);
|
||||
player.hurtMarked = true;
|
||||
player.setNoGravity(false);
|
||||
player.setDeltaMovement(0, player.getDeltaMovement().y(), 0);
|
||||
if (!Config.COMMON.knockedMovementEnabled.get()) {
|
||||
player.setDeltaMovement(0, player.getDeltaMovement().y(), 0);
|
||||
}
|
||||
if (CarryManager.isBeingCarried(player.getUUID())) {
|
||||
if (player.getForcedPose() == Pose.SWIMMING) {
|
||||
player.setForcedPose(null);
|
||||
@@ -60,7 +63,8 @@ public class KnockedEffect {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingJump(LivingEvent.LivingJumpEvent event) {
|
||||
if (event.getEntity() instanceof Player player && KnockedManager.isKnocked(player)) {
|
||||
if (event.getEntity() instanceof Player player && KnockedManager.isKnocked(player)
|
||||
&& !Config.COMMON.knockedMovementEnabled.get()) {
|
||||
event.getEntity().setDeltaMovement(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user