164 lines
6.5 KiB
Java
164 lines
6.5 KiB
Java
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.Style;
|
|
import net.minecraft.tags.DamageTypeTags;
|
|
import net.minecraft.util.CommonLinks;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import org.jetbrains.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.<CombatEntry>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 combatEntry = new CombatEntry(source, damage, fallLocation, (float)this.mob.fallDistance);
|
|
this.entries.add(combatEntry);
|
|
this.lastDamageTime = this.mob.tickCount;
|
|
this.takingDamage = true;
|
|
if (!this.inCombat && this.mob.isAlive() && shouldEnterCombat(source)) {
|
|
this.inCombat = true;
|
|
this.combatStartTime = this.mob.tickCount;
|
|
this.combatEndTime = this.combatStartTime;
|
|
this.mob.onEnterCombat();
|
|
}
|
|
}
|
|
|
|
private static boolean shouldEnterCombat(DamageSource source) {
|
|
return source.getEntity() instanceof LivingEntity;
|
|
}
|
|
|
|
private Component getMessageForAssistedFall(Entity entity, Component entityDisplayName, String hasWeaponTranslationKey, String noWeaponTranslationKey) {
|
|
ItemStack itemStack = entity instanceof LivingEntity livingEntity ? livingEntity.getMainHandItem() : ItemStack.EMPTY;
|
|
return !itemStack.isEmpty() && itemStack.has(DataComponents.CUSTOM_NAME)
|
|
? Component.translatable(hasWeaponTranslationKey, this.mob.getDisplayName(), entityDisplayName, itemStack.getDisplayName())
|
|
: Component.translatable(noWeaponTranslationKey, this.mob.getDisplayName(), entityDisplayName);
|
|
}
|
|
|
|
private Component getFallMessage(CombatEntry combatEntry, @Nullable Entity entity) {
|
|
DamageSource damageSource = combatEntry.source();
|
|
if (!damageSource.is(DamageTypeTags.IS_FALL) && !damageSource.is(DamageTypeTags.ALWAYS_MOST_SIGNIFICANT_FALL)) {
|
|
Component component = getDisplayName(entity);
|
|
Entity entity2 = damageSource.getEntity();
|
|
Component component2 = getDisplayName(entity2);
|
|
if (component2 != null && !component2.equals(component)) {
|
|
return this.getMessageForAssistedFall(entity2, component2, "death.fell.assist.item", "death.fell.assist");
|
|
} else {
|
|
return (Component)(component != null
|
|
? this.getMessageForAssistedFall(entity, component, "death.fell.finish.item", "death.fell.finish")
|
|
: Component.translatable("death.fell.killer", this.mob.getDisplayName()));
|
|
}
|
|
} else {
|
|
FallLocation fallLocation = (FallLocation)Objects.requireNonNullElse(combatEntry.fallLocation(), FallLocation.GENERIC);
|
|
return Component.translatable(fallLocation.languageKey(), this.mob.getDisplayName());
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static 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());
|
|
} else {
|
|
CombatEntry combatEntry = (CombatEntry)this.entries.get(this.entries.size() - 1);
|
|
DamageSource damageSource = combatEntry.source();
|
|
CombatEntry combatEntry2 = this.getMostSignificantFall();
|
|
DeathMessageType deathMessageType = damageSource.type().deathMessageType();
|
|
if (deathMessageType == DeathMessageType.FALL_VARIANTS && combatEntry2 != null) {
|
|
return this.getFallMessage(combatEntry2, damageSource.getEntity());
|
|
} else if (deathMessageType == DeathMessageType.INTENTIONAL_GAME_DESIGN) {
|
|
String string = "death.attack." + damageSource.getMsgId();
|
|
Component component = ComponentUtils.wrapInSquareBrackets(Component.translatable(string + ".link")).withStyle(INTENTIONAL_GAME_DESIGN_STYLE);
|
|
return Component.translatable(string + ".message", this.mob.getDisplayName(), component);
|
|
} else {
|
|
return damageSource.getLocalizedDeathMessage(this.mob);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private CombatEntry getMostSignificantFall() {
|
|
CombatEntry combatEntry = null;
|
|
CombatEntry combatEntry2 = null;
|
|
float f = 0.0F;
|
|
float g = 0.0F;
|
|
|
|
for (int i = 0; i < this.entries.size(); i++) {
|
|
CombatEntry combatEntry3 = (CombatEntry)this.entries.get(i);
|
|
CombatEntry combatEntry4 = i > 0 ? (CombatEntry)this.entries.get(i - 1) : null;
|
|
DamageSource damageSource = combatEntry3.source();
|
|
boolean bl = damageSource.is(DamageTypeTags.ALWAYS_MOST_SIGNIFICANT_FALL);
|
|
float h = bl ? Float.MAX_VALUE : combatEntry3.fallDistance();
|
|
if ((damageSource.is(DamageTypeTags.IS_FALL) || bl) && h > 0.0F && (combatEntry == null || h > g)) {
|
|
if (i > 0) {
|
|
combatEntry = combatEntry4;
|
|
} else {
|
|
combatEntry = combatEntry3;
|
|
}
|
|
|
|
g = h;
|
|
}
|
|
|
|
if (combatEntry3.fallLocation() != null && (combatEntry2 == null || combatEntry3.damage() > f)) {
|
|
combatEntry2 = combatEntry3;
|
|
f = combatEntry3.damage();
|
|
}
|
|
}
|
|
|
|
if (g > 5.0F && combatEntry != null) {
|
|
return combatEntry;
|
|
} else {
|
|
return f > 5.0F && combatEntry2 != null ? combatEntry2 : null;
|
|
}
|
|
}
|
|
|
|
public int getCombatDuration() {
|
|
return this.inCombat ? this.mob.tickCount - this.combatStartTime : this.combatEndTime - this.combatStartTime;
|
|
}
|
|
|
|
/**
|
|
* Resets this trackers list of combat entries
|
|
*/
|
|
public void recheckStatus() {
|
|
int i = this.inCombat ? 300 : 100;
|
|
if (this.takingDamage && (!this.mob.isAlive() || this.mob.tickCount - this.lastDamageTime > i)) {
|
|
boolean bl = this.inCombat;
|
|
this.takingDamage = false;
|
|
this.inCombat = false;
|
|
this.combatEndTime = this.mob.tickCount;
|
|
if (bl) {
|
|
this.mob.onLeaveCombat();
|
|
}
|
|
|
|
this.entries.clear();
|
|
}
|
|
}
|
|
}
|