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

package com.hypixel.hytale.builtin.mounts;

import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.component.dependency.OrderPriority;
import com.hypixel.hytale.server.core.modules.entity.teleport.TeleportSystems;
import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport;
import com.hypixel.hytale.component.dependency.SystemGroupDependency;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.system.EcsEvent;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.entity.entities.Player;
import java.time.Instant;
import java.time.temporal.TemporalAmount;
import com.hypixel.hytale.server.core.modules.time.TimeResource;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import java.time.Duration;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageEventSystem;
import com.hypixel.hytale.component.dependency.RootDependency;
import com.hypixel.hytale.builtin.mounts.minecart.MinecartComponent;
import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent;
import com.hypixel.hytale.server.core.modules.entity.component.Interactable;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.protocol.MovementStates;
import java.util.List;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSystems;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import com.hypixel.hytale.server.core.universe.world.World;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerInput;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.entity.AnimationUtils;
import com.hypixel.hytale.protocol.AnimationSlot;
import com.hypixel.hytale.protocol.MountController;
import com.hypixel.hytale.component.system.RefChangeSystem;
import java.util.Iterator;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.mountpoints.BlockMountPoint;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.protocol.BlockMount;
import com.hypixel.hytale.protocol.MountedUpdate;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.protocol.Vector3f;
import com.hypixel.hytale.protocol.ComponentUpdateType;
import com.hypixel.hytale.protocol.ComponentUpdate;
import java.util.Map;
import com.hypixel.hytale.component.ArchetypeChunk;
import javax.annotation.Nullable;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;

public class MountSystems
{
    private static void handleMountedRemoval(final Ref<EntityStore> ref, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final MountedComponent component) {
        final Ref<EntityStore> mountedToEntity = component.getMountedToEntity();
        if (mountedToEntity != null && mountedToEntity.isValid()) {
            final MountedByComponent mountedBy = commandBuffer.getComponent(mountedToEntity, MountedByComponent.getComponentType());
            if (mountedBy != null) {
                mountedBy.removePassenger(ref);
            }
        }
        final Ref<ChunkStore> mountedToBlock = component.getMountedToBlock();
        if (mountedToBlock != null && mountedToBlock.isValid()) {
            final Store<ChunkStore> chunkStore = mountedToBlock.getStore();
            final BlockMountComponent seatComponent = chunkStore.getComponent(mountedToBlock, BlockMountComponent.getComponentType());
            if (seatComponent != null) {
                seatComponent.removeSeatedEntity(ref);
                if (seatComponent.isDead()) {
                    chunkStore.removeComponent(mountedToBlock, BlockMountComponent.getComponentType());
                }
            }
        }
    }
    
    public static class TrackerUpdate extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> componentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public TrackerUpdate() {
            this.componentType = EntityTrackerSystems.Visible.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.componentType, MountedComponent.getComponentType());
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityTrackerSystems.QUEUE_UPDATE_GROUP;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @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 EntityTrackerSystems.Visible visible = archetypeChunk.getComponent(index, this.componentType);
            final MountedComponent mounted = archetypeChunk.getComponent(index, MountedComponent.getComponentType());
            final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
            if (mounted.consumeNetworkOutdated()) {
                queueUpdatesFor(ref, visible.visibleTo, mounted);
            }
            else if (!visible.newlyVisibleTo.isEmpty()) {
                queueUpdatesFor(ref, visible.newlyVisibleTo, mounted);
            }
        }
        
        private static void queueUpdatesFor(@Nonnull final Ref<EntityStore> ref, @Nonnull final Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo, @Nonnull final MountedComponent component) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.Mounted;
            final Ref<EntityStore> mountedToEntity = component.getMountedToEntity();
            final Ref<ChunkStore> mountedToBlock = component.getMountedToBlock();
            final com.hypixel.hytale.math.vector.Vector3f offset = component.getAttachmentOffset();
            final Vector3f netOffset = new Vector3f(offset.x, offset.y, offset.z);
            MountedUpdate mountedUpdate;
            if (mountedToEntity != null) {
                final int mountedToNetworkId = ref.getStore().getComponent(mountedToEntity, NetworkId.getComponentType()).getId();
                mountedUpdate = new MountedUpdate(mountedToNetworkId, netOffset, component.getControllerType(), null);
            }
            else {
                if (mountedToBlock == null) {
                    throw new UnsupportedOperationException("Couldn't create MountedUpdate packet for MountedComponent");
                }
                final BlockMountComponent blockMountComponent = mountedToBlock.getStore().getComponent(mountedToBlock, BlockMountComponent.getComponentType());
                if (blockMountComponent == null) {
                    return;
                }
                final BlockMountPoint occupiedSeat = blockMountComponent.getSeatBlockBySeatedEntity(ref);
                if (occupiedSeat == null) {
                    return;
                }
                final BlockType blockType = blockMountComponent.getExpectedBlockType();
                final com.hypixel.hytale.math.vector.Vector3f position = occupiedSeat.computeWorldSpacePosition(blockMountComponent.getBlockPos());
                final com.hypixel.hytale.math.vector.Vector3f rotationEuler = occupiedSeat.computeRotationEuler(blockMountComponent.getExpectedRotation());
                final BlockMount blockMount = new BlockMount(blockMountComponent.getType(), new Vector3f(position.x, position.y, position.z), new Vector3f(rotationEuler.x, rotationEuler.y, rotationEuler.z), BlockType.getAssetMap().getIndex(blockType.getId()));
                mountedUpdate = new MountedUpdate(0, netOffset, component.getControllerType(), blockMount);
            }
            update.mounted = mountedUpdate;
            for (final EntityTrackerSystems.EntityViewer viewer : visibleTo.values()) {
                viewer.queueUpdate(ref, update);
            }
        }
    }
    
    public static class TrackerRemove extends RefChangeSystem<EntityStore, MountedComponent>
    {
        @Override
        public Query<EntityStore> getQuery() {
            return EntityTrackerSystems.Visible.getComponentType();
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, MountedComponent> componentType() {
            return MountedComponent.getComponentType();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final MountedComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final MountedComponent oldComponent, @Nonnull final MountedComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final MountedComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            if (component.getControllerType() == MountController.BlockMount) {
                AnimationUtils.stopAnimation(ref, AnimationSlot.Movement, true, commandBuffer);
            }
            final EntityTrackerSystems.Visible visibleComponent = store.getComponent(ref, EntityTrackerSystems.Visible.getComponentType());
            assert visibleComponent != null;
            for (final EntityTrackerSystems.EntityViewer viewer : visibleComponent.visibleTo.values()) {
                viewer.queueRemove(ref, ComponentUpdateType.Mounted);
            }
        }
    }
    
    public static class TrackedMounted extends RefChangeSystem<EntityStore, MountedComponent>
    {
        @Override
        public Query<EntityStore> getQuery() {
            return MountedComponent.getComponentType();
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, MountedComponent> componentType() {
            return MountedComponent.getComponentType();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final MountedComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Ref<EntityStore> target = component.getMountedToEntity();
            if (target == null || !target.isValid()) {
                return;
            }
            final MountedByComponent mountedBy = commandBuffer.ensureAndGetComponent(target, MountedByComponent.getComponentType());
            mountedBy.addPassenger(ref);
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final MountedComponent oldComponent, @Nonnull final MountedComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final MountedComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            MountSystems.handleMountedRemoval(ref, commandBuffer, component);
        }
    }
    
    public static class RemoveMounted extends RefSystem<EntityStore>
    {
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final MountedComponent mounted = commandBuffer.getComponent(ref, MountedComponent.getComponentType());
            commandBuffer.removeComponent(ref, MountedComponent.getComponentType());
            MountSystems.handleMountedRemoval(ref, commandBuffer, mounted);
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return MountedComponent.getComponentType();
        }
    }
    
    public static class PlayerMount extends RefChangeSystem<EntityStore, MountedComponent>
    {
        private final Query<EntityStore> query;
        
        public PlayerMount() {
            this.query = PlayerInput.getComponentType();
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, MountedComponent> componentType() {
            return MountedComponent.getComponentType();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final MountedComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final MountedComponent mounted = commandBuffer.getComponent(ref, MountedComponent.getComponentType());
            assert mounted != null;
            final PlayerInput input = commandBuffer.getComponent(ref, PlayerInput.getComponentType());
            assert input != null;
            final Ref<EntityStore> mountRef = mounted.getMountedToEntity();
            if (mountRef == null || !mountRef.isValid()) {
                return;
            }
            final int mountNetworkId = commandBuffer.getComponent(mountRef, NetworkId.getComponentType()).getId();
            input.setMountId(mountNetworkId);
            input.getMovementUpdateQueue().clear();
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, @Nullable final MountedComponent oldComponent, @Nonnull final MountedComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final MountedComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final PlayerInput input = commandBuffer.getComponent(ref, PlayerInput.getComponentType());
            assert input != null;
            input.setMountId(0);
        }
    }
    
    public static class RemoveMountedBy extends RefSystem<EntityStore>
    {
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final MountedByComponent by = commandBuffer.getComponent(ref, MountedByComponent.getComponentType());
            for (final Ref<EntityStore> p : by.getPassengers()) {
                if (!p.isValid()) {
                    continue;
                }
                final MountedComponent mounted = commandBuffer.getComponent(p, MountedComponent.getComponentType());
                if (mounted == null) {
                    continue;
                }
                final Ref<EntityStore> target = mounted.getMountedToEntity();
                if (target.isValid() && !target.equals(ref)) {
                    continue;
                }
                commandBuffer.removeComponent(p, MountedComponent.getComponentType());
            }
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return MountedByComponent.getComponentType();
        }
    }
    
    public static class RemoveBlockSeat extends RefSystem<ChunkStore>
    {
        @Override
        public void onEntityAdded(@Nonnull final Ref<ChunkStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<ChunkStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<ChunkStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<ChunkStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
            final BlockMountComponent blockSeatComponent = commandBuffer.getComponent(ref, BlockMountComponent.getComponentType());
            assert blockSeatComponent != null;
            final ObjectArrayList<? extends Ref<EntityStore>> dismounting = new ObjectArrayList<Ref<EntityStore>>(blockSeatComponent.getSeatedEntities());
            final World world = ref.getStore().getExternalData().getWorld();
            for (final Ref<EntityStore> seated : dismounting) {
                blockSeatComponent.removeSeatedEntity(seated);
                world.execute(() -> {
                    if (!(!seated.isValid())) {
                        seated.getStore().tryRemoveComponent(seated, MountedComponent.getComponentType());
                    }
                });
            }
        }
        
        @Override
        public Query<ChunkStore> getQuery() {
            return BlockMountComponent.getComponentType();
        }
    }
    
    public static class HandleMountInput extends EntityTickingSystem<EntityStore>
    {
        private final Query<EntityStore> query;
        private final Set<Dependency<EntityStore>> deps;
        
        public HandleMountInput() {
            this.query = (Query<EntityStore>)Query.and(MountedComponent.getComponentType(), PlayerInput.getComponentType());
            this.deps = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.BEFORE, PlayerSystems.ProcessPlayerInput.class));
        }
        
        @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 MountedComponent mounted = archetypeChunk.getComponent(index, MountedComponent.getComponentType());
            assert mounted != null;
            final PlayerInput input = archetypeChunk.getComponent(index, PlayerInput.getComponentType());
            assert input != null;
            final MountController controller = mounted.getControllerType();
            final Ref<EntityStore> targetRef = (controller == MountController.BlockMount) ? archetypeChunk.getReferenceTo(index) : mounted.getMountedToEntity();
            final List<PlayerInput.InputUpdate> queue = input.getMovementUpdateQueue();
            for (int i = 0; i < queue.size(); ++i) {
                final PlayerInput.InputUpdate q = queue.get(i);
                if (controller == MountController.BlockMount && (q instanceof PlayerInput.RelativeMovement || q instanceof PlayerInput.AbsoluteMovement)) {
                    if (mounted.getMountedDurationMs() < 600L) {
                        continue;
                    }
                    final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
                    commandBuffer.removeComponent(ref, MountedComponent.getComponentType());
                }
                if (q instanceof final PlayerInput.SetRiderMovementStates s) {
                    final MovementStates states = s.movementStates();
                    final MovementStatesComponent movementStatesComponent = archetypeChunk.getComponent(index, MovementStatesComponent.getComponentType());
                    if (movementStatesComponent != null) {
                        movementStatesComponent.setMovementStates(states);
                    }
                }
                else if (!(q instanceof PlayerInput.WishMovement)) {
                    if (q instanceof final PlayerInput.RelativeMovement relative) {
                        relative.apply(commandBuffer, archetypeChunk, index);
                        final TransformComponent transform = commandBuffer.getComponent(targetRef, TransformComponent.getComponentType());
                        transform.getPosition().add(relative.getX(), relative.getY(), relative.getZ());
                    }
                    else if (q instanceof final PlayerInput.AbsoluteMovement absolute) {
                        absolute.apply(commandBuffer, archetypeChunk, index);
                        final TransformComponent transform = commandBuffer.getComponent(targetRef, TransformComponent.getComponentType());
                        transform.getPosition().assign(absolute.getX(), absolute.getY(), absolute.getZ());
                    }
                    else if (q instanceof final PlayerInput.SetMovementStates s2) {
                        final MovementStates states = s2.movementStates();
                        final MovementStatesComponent movementStatesComponent = commandBuffer.getComponent(targetRef, MovementStatesComponent.getComponentType());
                        if (movementStatesComponent != null) {
                            movementStatesComponent.setMovementStates(states);
                        }
                    }
                    else if (q instanceof final PlayerInput.SetBody body) {
                        body.apply(commandBuffer, archetypeChunk, index);
                        final TransformComponent transform = commandBuffer.getComponent(targetRef, TransformComponent.getComponentType());
                        transform.getRotation().assign(body.direction().pitch, body.direction().yaw, body.direction().roll);
                    }
                    else if (q instanceof final PlayerInput.SetHead head) {
                        head.apply(commandBuffer, archetypeChunk, index);
                    }
                }
            }
            queue.clear();
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.deps;
        }
    }
    
    public static class EnsureMinecartComponents extends HolderSystem<EntityStore>
    {
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            holder.ensureComponent(Interactable.getComponentType());
            holder.putComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
            holder.ensureComponent(PrefabCopyableComponent.getComponentType());
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return MinecartComponent.getComponentType();
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return RootDependency.firstSet();
        }
    }
    
    public static class OnMinecartHit extends DamageEventSystem
    {
        private static final Duration HIT_RESET_TIME;
        private static final int NUMBER_OF_HITS = 3;
        @Nonnull
        private static final Query<EntityStore> QUERY;
        @Nonnull
        private static final Set<Dependency<EntityStore>> DEPENDENCIES;
        
        @Override
        public Query<EntityStore> getQuery() {
            return OnMinecartHit.QUERY;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return OnMinecartHit.DEPENDENCIES;
        }
        
        @Override
        public void handle(final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final Damage damage) {
            final MinecartComponent minecartComponent = archetypeChunk.getComponent(index, MinecartComponent.getComponentType());
            assert minecartComponent != null;
            final Instant currentTime = commandBuffer.getResource(TimeResource.getResourceType()).getNow();
            if (minecartComponent.getLastHit() != null && currentTime.isAfter(minecartComponent.getLastHit().plus((TemporalAmount)OnMinecartHit.HIT_RESET_TIME))) {
                minecartComponent.setLastHit(null);
                minecartComponent.setNumberOfHits(0);
            }
            if (damage.getAmount() <= 0.0f) {
                return;
            }
            minecartComponent.setNumberOfHits(minecartComponent.getNumberOfHits() + 1);
            minecartComponent.setLastHit(currentTime);
            if (minecartComponent.getNumberOfHits() == 3) {
                commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE);
                boolean shouldDropItem = true;
                final Damage.Source source2 = damage.getSource();
                if (source2 instanceof final Damage.EntitySource source) {
                    final Player playerComponent = source.getRef().isValid() ? commandBuffer.getComponent(source.getRef(), Player.getComponentType()) : null;
                    if (playerComponent != null) {
                        shouldDropItem = (playerComponent.getGameMode() != GameMode.Creative);
                    }
                }
                if (shouldDropItem && minecartComponent.getSourceItem() != null) {
                    final TransformComponent transform = archetypeChunk.getComponent(index, TransformComponent.getComponentType());
                    assert transform != null;
                    final Holder<EntityStore> drop = ItemComponent.generateItemDrop(commandBuffer, new ItemStack(minecartComponent.getSourceItem()), transform.getPosition(), transform.getRotation(), 0.0f, 1.0f, 0.0f);
                    if (drop != null) {
                        commandBuffer.addEntity(drop, AddReason.SPAWN);
                    }
                }
            }
        }
        
        static {
            HIT_RESET_TIME = Duration.ofSeconds(10L);
            QUERY = Archetype.of(MinecartComponent.getComponentType(), TransformComponent.getComponentType());
            DEPENDENCIES = Set.of(new SystemGroupDependency(Order.AFTER, DamageModule.get().getGatherDamageGroup()), new SystemGroupDependency(Order.AFTER, DamageModule.get().getFilterDamageGroup()), new SystemGroupDependency(Order.BEFORE, DamageModule.get().getInspectDamageGroup()));
        }
    }
    
    public static class TeleportMountedEntity extends RefChangeSystem<EntityStore, Teleport>
    {
        private static final Set<Dependency<EntityStore>> DEPENDENCIES;
        
        @Override
        public Query<EntityStore> getQuery() {
            return MountedComponent.getComponentType();
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, Teleport> componentType() {
            return Teleport.getComponentType();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final Teleport component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            commandBuffer.removeComponent(ref, MountedComponent.getComponentType());
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, @Nullable final Teleport oldComponent, @Nonnull final Teleport newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final Teleport component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return TeleportMountedEntity.DEPENDENCIES;
        }
        
        static {
            DEPENDENCIES = Set.of(new SystemDependency(Order.BEFORE, TeleportSystems.MoveSystem.class, OrderPriority.CLOSEST), new SystemDependency(Order.BEFORE, TeleportSystems.PlayerMoveSystem.class, OrderPriority.CLOSEST));
        }
    }
    
    public static class MountedEntityDeath extends RefChangeSystem<EntityStore, DeathComponent>
    {
        @Override
        public Query<EntityStore> getQuery() {
            return MountedComponent.getComponentType();
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, DeathComponent> componentType() {
            return DeathComponent.getComponentType();
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            commandBuffer.removeComponent(ref, MountedComponent.getComponentType());
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, @Nullable 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) {
        }
    }
}
