172 lines
7.7 KiB
Java
172 lines
7.7 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Lists
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.world.damagesource;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.network.chat.ClickEvent;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.ComponentUtils;
|
|
import net.minecraft.network.chat.HoverEvent;
|
|
import net.minecraft.network.chat.MutableComponent;
|
|
import net.minecraft.network.chat.Style;
|
|
import net.minecraft.tags.DamageTypeTags;
|
|
import net.minecraft.util.CommonLinks;
|
|
import net.minecraft.world.damagesource.CombatEntry;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.damagesource.DeathMessageType;
|
|
import net.minecraft.world.damagesource.FallLocation;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public class CombatTracker {
|
|
public static final int RESET_DAMAGE_STATUS_TIME = 100;
|
|
public static final int RESET_COMBAT_STATUS_TIME = 300;
|
|
private static final Style INTENTIONAL_GAME_DESIGN_STYLE = Style.EMPTY.withClickEvent(new ClickEvent.OpenUrl(CommonLinks.INTENTIONAL_GAME_DESIGN_BUG)).withHoverEvent(new HoverEvent.ShowText(Component.literal("MCPE-28723")));
|
|
private final List<CombatEntry> entries = Lists.newArrayList();
|
|
private final LivingEntity mob;
|
|
private int lastDamageTime;
|
|
private int combatStartTime;
|
|
private int combatEndTime;
|
|
private boolean inCombat;
|
|
private boolean takingDamage;
|
|
|
|
public CombatTracker(LivingEntity mob) {
|
|
this.mob = mob;
|
|
}
|
|
|
|
public void recordDamage(DamageSource source, float damage) {
|
|
this.recheckStatus();
|
|
FallLocation fallLocation = FallLocation.getCurrentFallLocation(this.mob);
|
|
CombatEntry entry = new CombatEntry(source, damage, fallLocation, (float)this.mob.fallDistance);
|
|
this.entries.add(entry);
|
|
this.lastDamageTime = this.mob.tickCount;
|
|
this.takingDamage = true;
|
|
if (!this.inCombat && this.mob.isAlive() && CombatTracker.shouldEnterCombat(source)) {
|
|
this.inCombat = true;
|
|
this.combatEndTime = this.combatStartTime = this.mob.tickCount;
|
|
this.mob.onEnterCombat();
|
|
}
|
|
}
|
|
|
|
private static boolean shouldEnterCombat(DamageSource source) {
|
|
return source.getEntity() instanceof LivingEntity;
|
|
}
|
|
|
|
private Component getMessageForAssistedFall(Entity attackerEntity, Component attackerName, String messageWithItem, String messageWithoutItem) {
|
|
ItemStack attackerItem;
|
|
if (attackerEntity instanceof LivingEntity) {
|
|
LivingEntity livingEntity = (LivingEntity)attackerEntity;
|
|
v0 = livingEntity.getMainHandItem();
|
|
} else {
|
|
v0 = attackerItem = ItemStack.EMPTY;
|
|
}
|
|
if (!attackerItem.isEmpty() && attackerItem.has(DataComponents.CUSTOM_NAME)) {
|
|
return Component.translatable(messageWithItem, this.mob.getDisplayName(), attackerName, attackerItem.getDisplayName());
|
|
}
|
|
return Component.translatable(messageWithoutItem, this.mob.getDisplayName(), attackerName);
|
|
}
|
|
|
|
private Component getFallMessage(CombatEntry knockOffEntry, @Nullable Entity killingEntity) {
|
|
DamageSource knockOffSource = knockOffEntry.source();
|
|
if (knockOffSource.is(DamageTypeTags.IS_FALL) || knockOffSource.is(DamageTypeTags.ALWAYS_MOST_SIGNIFICANT_FALL)) {
|
|
FallLocation fallLocation = Objects.requireNonNullElse(knockOffEntry.fallLocation(), FallLocation.GENERIC);
|
|
return Component.translatable(fallLocation.languageKey(), this.mob.getDisplayName());
|
|
}
|
|
Component killerName = CombatTracker.getDisplayName(killingEntity);
|
|
Entity attackerEntity = knockOffSource.getEntity();
|
|
Component attackerName = CombatTracker.getDisplayName(attackerEntity);
|
|
if (attackerName != null && !attackerName.equals(killerName)) {
|
|
return this.getMessageForAssistedFall(attackerEntity, attackerName, "death.fell.assist.item", "death.fell.assist");
|
|
}
|
|
if (killerName != null) {
|
|
return this.getMessageForAssistedFall(killingEntity, killerName, "death.fell.finish.item", "death.fell.finish");
|
|
}
|
|
return Component.translatable("death.fell.killer", this.mob.getDisplayName());
|
|
}
|
|
|
|
private static @Nullable Component getDisplayName(@Nullable Entity entity) {
|
|
return entity == null ? null : entity.getDisplayName();
|
|
}
|
|
|
|
public Component getDeathMessage() {
|
|
if (this.entries.isEmpty()) {
|
|
return Component.translatable("death.attack.generic", this.mob.getDisplayName());
|
|
}
|
|
CombatEntry killingBlow = this.entries.get(this.entries.size() - 1);
|
|
DamageSource killingSource = killingBlow.source();
|
|
CombatEntry knockOffEntry = this.getMostSignificantFall();
|
|
DeathMessageType messageType = killingSource.type().deathMessageType();
|
|
if (messageType == DeathMessageType.FALL_VARIANTS && knockOffEntry != null) {
|
|
return this.getFallMessage(knockOffEntry, killingSource.getEntity());
|
|
}
|
|
if (messageType == DeathMessageType.INTENTIONAL_GAME_DESIGN) {
|
|
String deathMsg = "death.attack." + killingSource.getMsgId();
|
|
MutableComponent link = ComponentUtils.wrapInSquareBrackets(Component.translatable(deathMsg + ".link")).withStyle(INTENTIONAL_GAME_DESIGN_STYLE);
|
|
return Component.translatable(deathMsg + ".message", this.mob.getDisplayName(), link);
|
|
}
|
|
return killingSource.getLocalizedDeathMessage(this.mob);
|
|
}
|
|
|
|
private @Nullable CombatEntry getMostSignificantFall() {
|
|
CombatEntry result = null;
|
|
CombatEntry alternative = null;
|
|
float altDamage = 0.0f;
|
|
float bestFall = 0.0f;
|
|
for (int i = 0; i < this.entries.size(); ++i) {
|
|
float fallDistance;
|
|
CombatEntry entry = this.entries.get(i);
|
|
CombatEntry previous = i > 0 ? this.entries.get(i - 1) : null;
|
|
DamageSource source = entry.source();
|
|
boolean isFakeFall = source.is(DamageTypeTags.ALWAYS_MOST_SIGNIFICANT_FALL);
|
|
float f = fallDistance = isFakeFall ? Float.MAX_VALUE : entry.fallDistance();
|
|
if ((source.is(DamageTypeTags.IS_FALL) || isFakeFall) && fallDistance > 0.0f && (result == null || fallDistance > bestFall)) {
|
|
result = i > 0 ? previous : entry;
|
|
bestFall = fallDistance;
|
|
}
|
|
if (entry.fallLocation() == null || alternative != null && !(entry.damage() > altDamage)) continue;
|
|
alternative = entry;
|
|
altDamage = entry.damage();
|
|
}
|
|
if (bestFall > 5.0f && result != null) {
|
|
return result;
|
|
}
|
|
if (altDamage > 5.0f && alternative != null) {
|
|
return alternative;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public int getCombatDuration() {
|
|
if (this.inCombat) {
|
|
return this.mob.tickCount - this.combatStartTime;
|
|
}
|
|
return this.combatEndTime - this.combatStartTime;
|
|
}
|
|
|
|
public void recheckStatus() {
|
|
int reset;
|
|
int n = reset = this.inCombat ? 300 : 100;
|
|
if (this.takingDamage && (!this.mob.isAlive() || this.mob.tickCount - this.lastDamageTime > reset)) {
|
|
boolean wasInCombat = this.inCombat;
|
|
this.takingDamage = false;
|
|
this.inCombat = false;
|
|
this.combatEndTime = this.mob.tickCount;
|
|
if (wasInCombat) {
|
|
this.mob.onLeaveCombat();
|
|
}
|
|
this.entries.clear();
|
|
}
|
|
}
|
|
}
|
|
|