// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.modules.entity.damage;

import com.hypixel.hytale.protocol.InteractionState;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsSystems;
import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery;
import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerWorldData;
import java.time.Instant;
import com.hypixel.hytale.server.core.asset.type.gameplay.WorldMapConfig;
import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig;
import java.util.UUID;
import java.time.temporal.TemporalUnit;
import java.time.temporal.Temporal;
import java.time.temporal.ChronoUnit;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.server.core.entity.entities.player.pages.PageManager;
import com.hypixel.hytale.server.core.entity.entities.player.pages.CustomUIPage;
import com.hypixel.hytale.server.core.entity.entities.player.pages.RespawnPage;
import java.util.Iterator;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.interface_.KillFeedMessage;
import com.hypixel.hytale.server.core.modules.entity.damage.event.KillFeedEvent;
import java.util.Collection;
import com.hypixel.hytale.server.core.entity.InteractionChain;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.UnarmedInteractions;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.server.core.modules.interaction.Interactions;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.server.core.asset.type.gameplay.DeathConfig;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3d;
import java.util.List;
import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction;
import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.math.util.MathUtil;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.nameplate.Nameplate;
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
import com.hypixel.hytale.server.core.modules.interaction.InteractionModule;
import com.hypixel.hytale.server.core.entity.InteractionManager;
import com.hypixel.hytale.server.core.modules.entitystats.asset.DefaultEntityStatTypes;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.dependency.RootDependency;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.system.RefChangeSystem;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.entity.AnimationUtils;
import com.hypixel.hytale.protocol.AnimationSlot;
import com.hypixel.hytale.server.core.entity.Entity;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;

public class DeathSystems
{
    private static void playDeathAnimation(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent deathComponent, @Nullable final ModelComponent modelComponent, @Nonnull final MovementStatesComponent movementStatesComponent, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (modelComponent == null) {
            return;
        }
        final DamageCause deathCause = deathComponent.getDeathCause();
        if (deathCause == null) {
            return;
        }
        final Model model = modelComponent.getModel();
        final String[] animationIds = Entity.DefaultAnimations.getDeathAnimationIds(movementStatesComponent.getMovementStates(), deathCause);
        final String selectedAnimationId = model.getFirstBoundAnimationId(animationIds);
        AnimationUtils.playAnimation(ref, AnimationSlot.Status, selectedAnimationId, true, componentAccessor);
    }
    
    public abstract static class OnDeathSystem extends RefChangeSystem<EntityStore, DeathComponent>
    {
        @Nonnull
        @Override
        public ComponentType<EntityStore, DeathComponent> componentType() {
            return DeathComponent.getComponentType();
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final DeathComponent oldComponent, @Nonnull final DeathComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
    }
    
    public static class ClearHealth extends OnDeathSystem
    {
        @Nonnull
        private static final ComponentType<EntityStore, EntityStatMap> ENTITY_STAT_MAP_COMPONENT_TYPE;
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return RootDependency.firstSet();
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return ClearHealth.ENTITY_STAT_MAP_COMPONENT_TYPE;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final EntityStatMap entityStatMapComponent = store.getComponent(ref, ClearHealth.ENTITY_STAT_MAP_COMPONENT_TYPE);
            assert entityStatMapComponent != null;
            entityStatMapComponent.setStatValue(DefaultEntityStatTypes.getHealth(), 0.0f);
        }
        
        static {
            ENTITY_STAT_MAP_COMPONENT_TYPE = EntityStatMap.getComponentType();
        }
    }
    
    public static class ClearInteractions extends OnDeathSystem
    {
        @Nonnull
        private static final ComponentType<EntityStore, InteractionManager> INTERACTION_MANAGER_COMPONENT_TYPE;
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return RootDependency.firstSet();
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return ClearInteractions.INTERACTION_MANAGER_COMPONENT_TYPE;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final InteractionManager interactionManagerComponent = commandBuffer.getComponent(ref, ClearInteractions.INTERACTION_MANAGER_COMPONENT_TYPE);
            assert interactionManagerComponent != null;
            interactionManagerComponent.clear();
        }
        
        static {
            INTERACTION_MANAGER_COMPONENT_TYPE = InteractionModule.get().getInteractionManagerComponent();
        }
    }
    
    public static class ClearEntityEffects extends OnDeathSystem
    {
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return EffectControllerComponent.getComponentType();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final EffectControllerComponent effectControllerComponent = commandBuffer.getComponent(ref, EffectControllerComponent.getComponentType());
            assert effectControllerComponent != null;
            effectControllerComponent.clearEffects(ref, commandBuffer);
        }
    }
    
    public static class PlayerKilledPlayer extends OnDeathSystem
    {
        @Nonnull
        private static final Query<EntityStore> QUERY;
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return PlayerKilledPlayer.QUERY;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Nameplate nameplateComponent = commandBuffer.getComponent(ref, Nameplate.getComponentType());
            final Damage deathInfo = component.getDeathInfo();
            final DamageCause deathCause = component.getDeathCause();
            if (deathCause != DamageCause.PHYSICAL && deathCause != DamageCause.PROJECTILE) {
                return;
            }
            if (deathInfo != null) {
                final Damage.Source source = deathInfo.getSource();
                if (source instanceof final Damage.EntitySource entitySource) {
                    final Ref<EntityStore> sourceRef = entitySource.getRef();
                    if (!sourceRef.isValid()) {
                        return;
                    }
                    final Player attacker = store.getComponent(sourceRef, Player.getComponentType());
                    if (attacker == null) {
                        return;
                    }
                    attacker.sendMessage(Message.translation("server.general.killedEntity").param("entityName", nameplateComponent.getText()));
                }
            }
        }
        
        static {
            QUERY = Archetype.of(Player.getComponentType(), Nameplate.getComponentType());
        }
    }
    
    public static class DropPlayerDeathItems extends OnDeathSystem
    {
        @Nonnull
        private static final Query<EntityStore> QUERY;
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return DropPlayerDeathItems.QUERY;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Player playerComponent = store.getComponent(ref, Player.getComponentType());
            assert playerComponent != null;
            if (playerComponent.getGameMode() == GameMode.Creative) {
                return;
            }
            component.setDisplayDataOnDeathScreen(true);
            final CombinedItemContainer combinedItemContainer = playerComponent.getInventory().getCombinedEverything();
            if (component.getItemsDurabilityLossPercentage() > 0.0) {
                final double durabilityLossRatio = component.getItemsDurabilityLossPercentage() / 100.0;
                boolean hasArmorBroken = false;
                for (short i = 0; i < combinedItemContainer.getCapacity(); ++i) {
                    final ItemStack itemStack = combinedItemContainer.getItemStack(i);
                    if (!ItemStack.isEmpty(itemStack)) {
                        if (!itemStack.isBroken()) {
                            final double durabilityLoss = itemStack.getMaxDurability() * durabilityLossRatio;
                            final ItemStack updatedItemStack = itemStack.withIncreasedDurability(-durabilityLoss);
                            final ItemStackSlotTransaction transaction = combinedItemContainer.replaceItemStackInSlot(i, itemStack, updatedItemStack);
                            if (transaction.getSlotAfter().isBroken() && itemStack.getItem().getArmor() != null) {
                                hasArmorBroken = true;
                            }
                        }
                    }
                }
                if (hasArmorBroken) {
                    playerComponent.getStatModifiersManager().setRecalculate(true);
                }
            }
            List<ItemStack> itemsToDrop = null;
            switch (component.getItemsLossMode()) {
                case ALL: {
                    itemsToDrop = playerComponent.getInventory().dropAllItemStacks();
                    break;
                }
                case CONFIGURED: {
                    final double itemsAmountLossPercentage = component.getItemsAmountLossPercentage();
                    if (itemsAmountLossPercentage > 0.0) {
                        final double itemAmountLossRatio = itemsAmountLossPercentage / 100.0;
                        itemsToDrop = new ObjectArrayList<ItemStack>();
                        for (short j = 0; j < combinedItemContainer.getCapacity(); ++j) {
                            final ItemStack itemStack2 = combinedItemContainer.getItemStack(j);
                            if (!ItemStack.isEmpty(itemStack2)) {
                                if (itemStack2.getItem().dropsOnDeath()) {
                                    final int quantityToLose = Math.max(1, MathUtil.floor(itemStack2.getQuantity() * itemAmountLossRatio));
                                    itemsToDrop.add(itemStack2.withQuantity(quantityToLose));
                                    final int newQuantity = itemStack2.getQuantity() - quantityToLose;
                                    if (newQuantity > 0) {
                                        final ItemStack updatedItemStack2 = itemStack2.withQuantity(newQuantity);
                                        combinedItemContainer.replaceItemStackInSlot(j, itemStack2, updatedItemStack2);
                                    }
                                    else {
                                        combinedItemContainer.removeItemStackFromSlot(j);
                                    }
                                }
                            }
                        }
                    }
                    break;
                }
            }
            if (itemsToDrop != null && !itemsToDrop.isEmpty()) {
                final TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
                assert transformComponent != null;
                final Vector3d position = transformComponent.getPosition();
                final HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType());
                assert headRotationComponent != null;
                final Vector3f headRotation = headRotationComponent.getRotation();
                final Holder<EntityStore>[] drops = ItemComponent.generateItemDrops(store, itemsToDrop, position.clone().add(0.0, 1.0, 0.0), headRotation);
                commandBuffer.addEntities(drops, AddReason.SPAWN);
                component.setItemsLostOnDeath(itemsToDrop);
            }
        }
        
        static {
            QUERY = Archetype.of(Player.getComponentType(), TransformComponent.getComponentType(), HeadRotation.getComponentType());
        }
    }
    
    public static class PlayerDropItemsConfig extends OnDeathSystem
    {
        @Nonnull
        private static final Set<Dependency<EntityStore>> DEPENDENCIES;
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return (Query<EntityStore>)Query.and(Player.getComponentType(), PlayerRef.getComponentType());
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return PlayerDropItemsConfig.DEPENDENCIES;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final DeathConfig deathConfig = store.getExternalData().getWorld().getDeathConfig();
            component.setItemsLossMode(deathConfig.getItemsLossMode());
            component.setItemsAmountLossPercentage(deathConfig.getItemsAmountLossPercentage());
            component.setItemsDurabilityLossPercentage(deathConfig.getItemsDurabilityLossPercentage());
        }
        
        static {
            DEPENDENCIES = Set.of(new SystemDependency(Order.BEFORE, DropPlayerDeathItems.class));
        }
    }
    
    public static class RunDeathInteractions extends OnDeathSystem
    {
        @Nonnull
        private static final ComponentType<EntityStore, Interactions> INTERACTIONS_COMPONENT_TYPE;
        @Nonnull
        private static final ComponentType<EntityStore, InteractionManager> INTERACTION_MANAGER_COMPONENT_TYPE;
        @Nonnull
        private static final Query<EntityStore> QUERY;
        @Nonnull
        private static final Set<Dependency<EntityStore>> DEPENDENCIES;
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return RunDeathInteractions.QUERY;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return RunDeathInteractions.DEPENDENCIES;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final InteractionManager interactionManagerComponent = commandBuffer.getComponent(ref, RunDeathInteractions.INTERACTION_MANAGER_COMPONENT_TYPE);
            assert interactionManagerComponent != null;
            final Interactions interactionsComponent = commandBuffer.getComponent(ref, RunDeathInteractions.INTERACTIONS_COMPONENT_TYPE);
            assert interactionsComponent != null;
            String rootId = interactionsComponent.getInteractionId(InteractionType.Death);
            if (rootId == null) {
                final UnarmedInteractions unarmed = UnarmedInteractions.getAssetMap().getAsset("Empty");
                if (unarmed != null) {
                    rootId = unarmed.getInteractions().get(InteractionType.Death);
                }
            }
            final RootInteraction rootInteraction = (rootId != null) ? RootInteraction.getAssetMap().getAsset(rootId) : null;
            if (rootInteraction == null) {
                return;
            }
            final InteractionContext context = InteractionContext.forInteraction(interactionManagerComponent, ref, InteractionType.Death, commandBuffer);
            final InteractionChain chain = interactionManagerComponent.initChain(InteractionType.Death, context, rootInteraction, false);
            interactionManagerComponent.queueExecuteChain(chain);
            component.setInteractionChain(chain);
        }
        
        static {
            INTERACTIONS_COMPONENT_TYPE = Interactions.getComponentType();
            INTERACTION_MANAGER_COMPONENT_TYPE = InteractionModule.get().getInteractionManagerComponent();
            QUERY = Query.and(RunDeathInteractions.INTERACTIONS_COMPONENT_TYPE, RunDeathInteractions.INTERACTION_MANAGER_COMPONENT_TYPE);
            DEPENDENCIES = Set.of(new SystemDependency(Order.AFTER, ClearEntityEffects.class));
        }
    }
    
    public static class KillFeed extends OnDeathSystem
    {
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return (Query<EntityStore>)Archetype.empty();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Damage deathInfo = component.getDeathInfo();
            if (deathInfo == null) {
                return;
            }
            final World world = commandBuffer.getExternalData().getWorld();
            final ObjectArrayList<PlayerRef> broadcastTargets = new ObjectArrayList<PlayerRef>(world.getPlayerRefs());
            Message killerMessage = null;
            final Damage.Source source = deathInfo.getSource();
            if (source instanceof final Damage.EntitySource entitySource) {
                final Ref<EntityStore> sourceRef = entitySource.getRef();
                if (sourceRef.isValid()) {
                    final KillFeedEvent.KillerMessage killerMessageEvent = new KillFeedEvent.KillerMessage(deathInfo, ref);
                    store.invoke(sourceRef, killerMessageEvent);
                    if (killerMessageEvent.isCancelled()) {
                        return;
                    }
                    killerMessage = killerMessageEvent.getMessage();
                }
            }
            final KillFeedEvent.DecedentMessage decedentMessageEvent = new KillFeedEvent.DecedentMessage(deathInfo);
            store.invoke(ref, decedentMessageEvent);
            if (decedentMessageEvent.isCancelled()) {
                return;
            }
            final Message decedentMessage = decedentMessageEvent.getMessage();
            if (killerMessage == null && decedentMessage == null) {
                return;
            }
            final KillFeedEvent.Display killFeedEvent = new KillFeedEvent.Display(deathInfo, deathInfo.getIfPresentMetaObject(Damage.DEATH_ICON), broadcastTargets);
            store.invoke(ref, killFeedEvent);
            if (killFeedEvent.isCancelled()) {
                return;
            }
            final KillFeedMessage killFeedMessage = new KillFeedMessage((killerMessage != null) ? killerMessage.getFormattedMessage() : null, (decedentMessage != null) ? decedentMessage.getFormattedMessage() : null, killFeedEvent.getIcon());
            for (final PlayerRef targetPlayerRef : killFeedEvent.getBroadcastTargets()) {
                targetPlayerRef.getPacketHandler().write(killFeedMessage);
            }
        }
    }
    
    public static class PlayerDeathScreen extends OnDeathSystem
    {
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return (Query<EntityStore>)Query.and(Player.getComponentType(), TransformComponent.getComponentType());
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Player playerComponent = store.getComponent(ref, Player.getComponentType());
            assert playerComponent != null;
            if (!component.isShowDeathMenu()) {
                return;
            }
            final Damage deathInfo = component.getDeathInfo();
            final Message deathMessage = (deathInfo != null) ? deathInfo.getDeathMessage(ref, commandBuffer) : null;
            component.setDeathMessage(deathMessage);
            final PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
            assert playerRefComponent != null;
            final PageManager pageManager = playerComponent.getPageManager();
            pageManager.openCustomPage(ref, store, new RespawnPage(playerRefComponent, deathMessage, component.displayDataOnDeathScreen(), component.getDeathItemLoss()));
        }
    }
    
    public static class PlayerDeathMarker extends OnDeathSystem
    {
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return Player.getComponentType();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final World world = commandBuffer.getExternalData().getWorld();
            final GameplayConfig gameplayConfig = world.getGameplayConfig();
            final WorldMapConfig worldMapConfigGameplayConfig = gameplayConfig.getWorldMapConfig();
            if (!worldMapConfigGameplayConfig.isDisplayDeathMarker()) {
                return;
            }
            final Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType());
            assert playerComponent != null;
            final TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType());
            assert transformComponent != null;
            final Vector3d position = transformComponent.getPosition();
            final Transform transform = new Transform(position.getX(), position.getY(), position.getZ(), 0.0f, 0.0f, 0.0f);
            final WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType());
            final Instant gameTime = worldTimeResource.getGameTime();
            final int daysSinceWorldStart = (int)WorldTimeResource.ZERO_YEAR.until(gameTime, ChronoUnit.DAYS);
            final String deathMarkerId = "death-marker-" + String.valueOf(UUID.randomUUID());
            final PlayerWorldData perWorldData = playerComponent.getPlayerConfigData().getPerWorldData(world.getName());
            perWorldData.addLastDeath(deathMarkerId, transform, daysSinceWorldStart);
        }
    }
    
    public static class DeathAnimation extends OnDeathSystem
    {
        @Nonnull
        private final Query<EntityStore> query;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public DeathAnimation() {
            this.query = (Query<EntityStore>)Query.and(MovementStatesComponent.getComponentType(), AllLegacyLivingEntityTypesQuery.INSTANCE);
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.BEFORE, EntityStatsSystems.EntityTrackerUpdate.class), new SystemDependency(Order.AFTER, ClearEntityEffects.class));
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final ModelComponent modelComponent = commandBuffer.getComponent(ref, ModelComponent.getComponentType());
            final MovementStatesComponent movementStatesComponent = commandBuffer.getComponent(ref, MovementStatesComponent.getComponentType());
            assert movementStatesComponent != null;
            DeathSystems.playDeathAnimation(ref, component, modelComponent, movementStatesComponent, commandBuffer);
        }
    }
    
    public static class SpawnedDeathAnimation extends RefSystem<EntityStore>
    {
        private static final Query<EntityStore> QUERY;
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return SpawnedDeathAnimation.QUERY;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final DeathComponent deathComponent = commandBuffer.getComponent(ref, DeathComponent.getComponentType());
            assert deathComponent != null;
            final ModelComponent modelComponent = commandBuffer.getComponent(ref, ModelComponent.getComponentType());
            final MovementStatesComponent movementStatesComponent = commandBuffer.getComponent(ref, MovementStatesComponent.getComponentType());
            assert movementStatesComponent != null;
            DeathSystems.playDeathAnimation(ref, deathComponent, modelComponent, movementStatesComponent, commandBuffer);
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        static {
            QUERY = Query.and(AllLegacyLivingEntityTypesQuery.INSTANCE, DeathComponent.getComponentType(), MovementStatesComponent.getComponentType());
        }
    }
    
    public static class CorpseRemoval extends EntityTickingSystem<EntityStore>
    {
        @Nonnull
        private static final ComponentType<EntityStore, DeferredCorpseRemoval> DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE;
        @Nonnull
        private static final Query<EntityStore> QUERY;
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return CorpseRemoval.QUERY;
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final DeathComponent deathComponent = archetypeChunk.getComponent(index, DeathComponent.getComponentType());
            assert deathComponent != null;
            final InteractionChain deathInteractionChain = deathComponent.getInteractionChain();
            if (deathInteractionChain != null && deathInteractionChain.getServerState() == InteractionState.NotFinished) {
                return;
            }
            final DeferredCorpseRemoval corpseRemoval = archetypeChunk.getComponent(index, CorpseRemoval.DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE);
            if (corpseRemoval == null || corpseRemoval.tick(dt)) {
                commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE);
            }
        }
        
        static {
            DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE = DeferredCorpseRemoval.getComponentType();
            QUERY = Query.and(DeathComponent.getComponentType(), Query.not((Query<Object>)Player.getComponentType()));
        }
    }
}
