修改爆头判定,延迟补偿
This commit is contained in:
parent
89f3852a42
commit
7707a24a3d
3 changed files with 75 additions and 36 deletions
|
@ -0,0 +1,19 @@
|
|||
package net.mcreator.target.api.entity;
|
||||
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
|
||||
/**
|
||||
* 用于进行一些并非 {@link LivingEntity} 但是可被子弹击中的特殊实体的处理
|
||||
*/
|
||||
public interface ITargetEntity {
|
||||
/**
|
||||
* @param projectile 弹射物实体
|
||||
* @param result 击中实体的位置
|
||||
* @param source 伤害源类型
|
||||
* @param damage 伤害值
|
||||
*/
|
||||
void onProjectileHit(Entity projectile, EntityHitResult result, DamageSource source, float damage);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package net.mcreator.target.entity;
|
||||
|
||||
import net.mcreator.target.TargetMod;
|
||||
import net.mcreator.target.api.entity.ITargetEntity;
|
||||
import net.mcreator.target.headshot.BoundingBoxManager;
|
||||
import net.mcreator.target.headshot.IHeadshotBox;
|
||||
import net.mcreator.target.init.TargetModDamageTypes;
|
||||
|
@ -19,6 +20,7 @@ import net.minecraft.nbt.CompoundTag;
|
|||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
|
@ -39,6 +41,7 @@ import net.minecraftforge.entity.IEntityAdditionalSpawnData;
|
|||
import net.minecraftforge.entity.PartEntity;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
import net.minecraftforge.network.PlayMessages;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
|
@ -147,52 +150,44 @@ public class ProjectileEntity extends Entity implements IEntityAdditionalSpawnDa
|
|||
private EntityResult getHitResult(Entity entity, Vec3 startVec, Vec3 endVec) {
|
||||
double expandHeight = entity instanceof Player && !entity.isCrouching() ? 0.0625 : 0.0;
|
||||
AABB boundingBox = entity.getBoundingBox();
|
||||
|
||||
// 延迟补偿
|
||||
if (entity instanceof ServerPlayer && this.shooter != null) {
|
||||
int ping = (int) Math.floor((((ServerPlayer) this.shooter).latency / 1000.0) * 20.0 + 4.0);
|
||||
boundingBox = BoundingBoxManager.getBoundingBox((Player) entity, ping);
|
||||
Vec3 velocity = new Vec3(entity.getX() - entity.xOld, entity.getY() - entity.yOld, entity.getZ() - entity.zOld);
|
||||
// hitbox 延迟补偿。只有射击者是玩家(且被击中者也是玩家)才进行此类延迟补偿计算
|
||||
if (entity instanceof ServerPlayer player && this.shooter instanceof ServerPlayer serverPlayerOwner) {
|
||||
int ping = Mth.floor((serverPlayerOwner.latency / 1000.0) * 20.0 + 0.5);
|
||||
boundingBox = BoundingBoxManager.getBoundingBox(player, ping);
|
||||
velocity = BoundingBoxManager.getVelocity(player, ping);
|
||||
}
|
||||
// 应用蹲伏导致的 hitbox 变形
|
||||
boundingBox = boundingBox.expandTowards(0, expandHeight, 0);
|
||||
|
||||
if (this.beast) {
|
||||
boundingBox = boundingBox.inflate(3);
|
||||
}
|
||||
|
||||
Vec3 hitPos = boundingBox.clip(startVec, endVec).orElse(null);
|
||||
Vec3 grownHitPos = boundingBox.inflate(0.35, 0.2, 0.35).clip(startVec, endVec).orElse(null);
|
||||
if (hitPos == null && grownHitPos != null) {
|
||||
HitResult result = rayTraceBlocks(this.level(), new ClipContext(startVec, grownHitPos, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this), IGNORE_LEAVES);
|
||||
if (result.getType() == HitResult.Type.BLOCK) {
|
||||
return null;
|
||||
// 根据速度一定程度地扩展 hitbox
|
||||
boundingBox = boundingBox.expandTowards(velocity.x, velocity.y, velocity.z);
|
||||
// 玩家 hitbox 修正,可以通过 Config 调整
|
||||
double playerHitboxOffset = 3;
|
||||
if (entity instanceof ServerPlayer) {
|
||||
if (entity.getVehicle() != null) {
|
||||
boundingBox = boundingBox.move(velocity.multiply(playerHitboxOffset / 2, playerHitboxOffset / 2, playerHitboxOffset / 2));
|
||||
}
|
||||
hitPos = grownHitPos;
|
||||
boundingBox = boundingBox.move(velocity.multiply(playerHitboxOffset, playerHitboxOffset, playerHitboxOffset));
|
||||
}
|
||||
// 给所有实体统一应用的 Hitbox 偏移,其数值为实验得出的定值。
|
||||
if (entity.getVehicle() != null || entity instanceof ITargetEntity) {
|
||||
boundingBox = boundingBox.move(velocity.multiply(-2.5, -2.5, -2.5));
|
||||
}
|
||||
boundingBox = boundingBox.move(velocity.multiply(-5, -5, -5));
|
||||
// 计算射线与实体 boundingBox 的交点
|
||||
Vec3 hitPos = boundingBox.clip(startVec, endVec).orElse(null);
|
||||
|
||||
/* Check for headshot */
|
||||
boolean headshot = false;
|
||||
if (entity instanceof LivingEntity) {
|
||||
IHeadshotBox<LivingEntity> headshotBox = (IHeadshotBox<LivingEntity>) BoundingBoxManager.getHeadshotBoxes(entity.getType());
|
||||
if (headshotBox != null) {
|
||||
AABB box = headshotBox.getHeadshotBox((LivingEntity) entity);
|
||||
if (box != null) {
|
||||
box = box.move(boundingBox.getCenter().x, boundingBox.minY, boundingBox.getCenter().z);
|
||||
Optional<Vec3> headshotHitPos = box.clip(startVec, endVec);
|
||||
if (headshotHitPos.isEmpty()) {
|
||||
box = box.inflate(0.2, 0.2, 0.2);
|
||||
headshotHitPos = box.clip(startVec, endVec);
|
||||
}
|
||||
if (headshotHitPos.isPresent() && (hitPos == null || headshotHitPos.get().distanceTo(hitPos) < 0.55)) {
|
||||
hitPos = headshotHitPos.get();
|
||||
headshot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hitPos == null) {
|
||||
return null;
|
||||
}
|
||||
Vec3 hitBoxPos = hitPos.subtract(entity.position());
|
||||
boolean headshot = false;
|
||||
float eyeHeight = entity.getEyeHeight();
|
||||
if ((eyeHeight - 0.35) < hitBoxPos.y && hitBoxPos.y < (eyeHeight + 0.35)) {
|
||||
headshot = true;
|
||||
}
|
||||
|
||||
return new EntityResult(entity, hitPos, headshot);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,12 @@ public class BoundingBoxManager {
|
|||
private static final Map<EntityType<?>, IHeadshotBox<?>> headshotBoxes = new HashMap<>();
|
||||
private static final WeakHashMap<Player, LinkedList<AABB>> playerBoxes = new WeakHashMap<>();
|
||||
|
||||
private static final WeakHashMap<Player, LinkedList<Vec3>> PLAYER_POSITION = new WeakHashMap<>();
|
||||
// 玩家命中箱缓存表
|
||||
private static final WeakHashMap<Player, LinkedList<AABB>> PLAYER_HITBOXES = new WeakHashMap<>();
|
||||
// 玩家速度缓存表
|
||||
private static final WeakHashMap<Player, LinkedList<Vec3>> PLAYER_VELOCITY = new WeakHashMap<>();
|
||||
|
||||
static {
|
||||
/* Player */
|
||||
registerHeadshotBox(EntityType.PLAYER, (entity) -> {
|
||||
|
@ -118,6 +124,16 @@ public class BoundingBoxManager {
|
|||
playerBoxes.remove(event.getEntity());
|
||||
}
|
||||
|
||||
public static Vec3 getPlayerVelocity(Player entity) {
|
||||
LinkedList<Vec3> positions = PLAYER_POSITION.computeIfAbsent(entity, player -> new LinkedList<>());
|
||||
if (positions.size() > 1) {
|
||||
Vec3 currPos = positions.getFirst();
|
||||
Vec3 prevPos = positions.getLast();
|
||||
return new Vec3(currPos.x - prevPos.x, currPos.y - prevPos.y, currPos.z - prevPos.z);
|
||||
}
|
||||
return new Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
public static AABB getBoundingBox(Player entity, int ping) {
|
||||
if (playerBoxes.containsKey(entity)) {
|
||||
LinkedList<AABB> boxes = playerBoxes.get(entity);
|
||||
|
@ -126,4 +142,13 @@ public class BoundingBoxManager {
|
|||
}
|
||||
return entity.getBoundingBox();
|
||||
}
|
||||
|
||||
public static Vec3 getVelocity(Player entity, int ping) {
|
||||
if (PLAYER_VELOCITY.containsKey(entity)) {
|
||||
LinkedList<Vec3> velocities = PLAYER_VELOCITY.get(entity);
|
||||
int index = Mth.clamp(ping, 0, velocities.size() - 1);
|
||||
return velocities.get(index);
|
||||
}
|
||||
return getPlayerVelocity(entity);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue