import { InputPermissionCategory, HudVisibility, HudElement, system, TicksPerSecond, world, GameMode, Direction, BlockPermutation, EntityComponentTypes, EquipmentSlot, ItemStack, ItemLockMode, Player, BlockVolume, EasingType, ScriptEventSource } from '@minecraft/server';
import { ActionFormData, uiManager } from '@minecraft/server-ui';

var ActorPropertiesEnum;
(function (ActorPropertiesEnum) {
    ActorPropertiesEnum["ANIM_PITCH"] = "animate:pitch";
    ActorPropertiesEnum["ANIM_YAW"] = "animate:yaw";
    ActorPropertiesEnum["ANIM_ROLL"] = "animate:roll";
    ActorPropertiesEnum["DRONE_ICON"] = "property:icon";
    ActorPropertiesEnum["TYPE"] = "property:type";
    ActorPropertiesEnum["JUMPSCARE"] = "property:jumpscare";
    ActorPropertiesEnum["OPENED"] = "property:opened";
    ActorPropertiesEnum["COLOR"] = "property:colour";
    ActorPropertiesEnum["COLOR_R"] = "colour:r";
    ActorPropertiesEnum["COLOR_G"] = "colour:g";
    ActorPropertiesEnum["DOOR_SLIDE"] = "property:slide";
    ActorPropertiesEnum["DOOR_OPEN"] = "property:open";
    ActorPropertiesEnum["CHAIRLIFT_READY"] = "property:ready";
    ActorPropertiesEnum["CHAIRLIFT_MOVING"] = "property:moving";
    ActorPropertiesEnum["CHAIRLIFT_FORWARD"] = "property:forward";
    ActorPropertiesEnum["CHAIRLIFT_DIRECTION"] = "property:direction";
    ActorPropertiesEnum["FORCE_SLIDE"] = "property:force_slide";
    ActorPropertiesEnum["OUTLINE"] = "property:outline";
    ActorPropertiesEnum["COLLECTED"] = "property:collected";
    ActorPropertiesEnum["BLANK"] = "property:blank";
    ActorPropertiesEnum["LOCKED"] = "property:locked";
    ActorPropertiesEnum["USED"] = "property:used";
    ActorPropertiesEnum["RED"] = "property:red";
    ActorPropertiesEnum["CLIMB"] = "property:climb";
    ActorPropertiesEnum["ROLL"] = "property:roll";
    ActorPropertiesEnum["ANIM"] = "property:anim";
    ActorPropertiesEnum["WALKING"] = "property:walking";
    ActorPropertiesEnum["ALIVE"] = "property:alive";
    ActorPropertiesEnum["FALL"] = "property:fall";
    ActorPropertiesEnum["HIDDEN"] = "property:hidden";
})(ActorPropertiesEnum || (ActorPropertiesEnum = {}));

var DynamicPropertyEnum;
(function (DynamicPropertyEnum) {
    DynamicPropertyEnum["DRONE_OBJECTIVE"] = "droneObjective";
    DynamicPropertyEnum["DRONE_HAS_OBJECTIVE"] = "droneHasObjective";
    DynamicPropertyEnum["DRONE_IN_USE"] = "droneInUse";
    DynamicPropertyEnum["PLANKS_HIT"] = "planksHit";
    DynamicPropertyEnum["BATTERIES_COLLECTED"] = "batteriesCollected";
    DynamicPropertyEnum["RESPAWN_LOCATION"] = "respawnLocation";
    DynamicPropertyEnum["RESPAWN_ROTATION"] = "respawnRotation";
    DynamicPropertyEnum["RESPAWN_FACTING"] = "respawnFacing";
    DynamicPropertyEnum["BLUE_CARD_COLLECTED"] = "blueCardCollected";
    DynamicPropertyEnum["YELLOW_CARD_COLLECTED"] = "yellowCardCollected";
    DynamicPropertyEnum["ORANGE_CARD_COLLECTED"] = "orangeCardCollected";
    DynamicPropertyEnum["PURPLE_CARD_COLLECTED"] = "purpleCardCollected";
    DynamicPropertyEnum["REMOTE_COLLECTED"] = "remoteCollected";
    DynamicPropertyEnum["CHAIRLIFT_RUNNING"] = "chairliftRunning";
    DynamicPropertyEnum["CHAIRLIFT_CHANGED_DIRECTION"] = "chairliftChangedDirection";
    DynamicPropertyEnum["CHAIRLIFT_MOVING"] = "chairliftMoving";
    DynamicPropertyEnum["CLOCK_COUNT"] = "clockCount";
    DynamicPropertyEnum["CLOCK_CAN_CLICK"] = "clockCanClick";
    DynamicPropertyEnum["CLOCK_ACTIVE"] = "clockActive";
    DynamicPropertyEnum["CLOCK_TICK_ID"] = "clockTickId";
    DynamicPropertyEnum["PLANKS_HAMMER_ID"] = "planksHammerId";
    DynamicPropertyEnum["READER_ID"] = "readerId";
    DynamicPropertyEnum["ACTIVE_GAME"] = "activeGame";
    DynamicPropertyEnum["IN_GAME"] = "inGame";
    DynamicPropertyEnum["START_PLAYER_INTERVAL"] = "startPlayerInterval";
    DynamicPropertyEnum["START_PLAYER_TICK"] = "startPlayerTick";
    DynamicPropertyEnum["SAVED_ITEMS"] = "savedItems";
    DynamicPropertyEnum["SAVED_ITEMS_AMOUNT"] = "savedItemsAmount";
    DynamicPropertyEnum["IN_MENU"] = "inMenu";
    DynamicPropertyEnum["HAS_PLAYED"] = "hasPlayed";
    DynamicPropertyEnum["LOBBY_BUTTONS"] = "lobbyButtons";
    DynamicPropertyEnum["COLLECATABLE1"] = "collectable1";
    DynamicPropertyEnum["COLLECATABLE2"] = "collectable2";
    DynamicPropertyEnum["COLLECATABLE3"] = "collectable3";
    DynamicPropertyEnum["COLLECATABLE4"] = "collectable4";
    DynamicPropertyEnum["COLLECATABLE5"] = "collectable5";
    DynamicPropertyEnum["COLLECATABLE6"] = "collectable6";
    DynamicPropertyEnum["FAKE_FLOOR_MOVE"] = "fakeFloorMove";
    DynamicPropertyEnum["LIFT_DIRECTION"] = "liftDirection";
    DynamicPropertyEnum["GAMEEND"] = "gameEnd";
    DynamicPropertyEnum["EGGS_COLLECTED"] = "eggsCollected";
    DynamicPropertyEnum["FALLEN"] = "fallen";
    DynamicPropertyEnum["BROKEN_GLASS"] = "brokenGlass";
    DynamicPropertyEnum["BROKEN_GLASS2"] = "brokenGlass2";
    DynamicPropertyEnum["OPILA_TARGET"] = "opilaTarget";
    DynamicPropertyEnum["OPILA_AI_TYPE"] = "opilaAIType";
    DynamicPropertyEnum["OPILA_COMPLETED"] = "opilaCompleted";
    DynamicPropertyEnum["IN_CHASE"] = "inChase";
    DynamicPropertyEnum["CHASE_COMPLETE"] = "chaseComplete";
    DynamicPropertyEnum["OPILA_DELAY"] = "opilaDelay";
    DynamicPropertyEnum["OPILA_TICK"] = "opilaTick";
    DynamicPropertyEnum["OPILA_CHASE_DIRECTION"] = "opilaChaseDirection";
    DynamicPropertyEnum["OPILA_CHASING_PLAYER"] = "opilaChasingPlayer";
    DynamicPropertyEnum["PLATFORM_STEP"] = "platformStep";
    DynamicPropertyEnum["JUMPSCARE"] = "jumpscare";
    DynamicPropertyEnum["EGGS_PICKED"] = "eggsPicked";
    DynamicPropertyEnum["PROGRESS"] = "progress";
    DynamicPropertyEnum["TAPE_SPAWNED"] = "tapeSpawned";
    DynamicPropertyEnum["REMOVED_ITEM_DATA"] = "removedItemData";
})(DynamicPropertyEnum || (DynamicPropertyEnum = {}));

var SoundEnum;
(function (SoundEnum) {
    SoundEnum["HIT_PLANK"] = "thm.wood_chop";
    SoundEnum["JUMPSCARE_AMBIENCE"] = "thm.jumpscare.ambience";
    SoundEnum["JUMPSCARE_SCARE"] = "thm.jumpscare.scare";
    SoundEnum["SPOTLIGHT"] = "thm.spotlight";
    SoundEnum["KEYCARD"] = "thm.keycard";
    SoundEnum["ERROR"] = "thm.error";
    SoundEnum["GIVE_EGG"] = "thm.give_egg";
    SoundEnum["DOOR_SLIDE"] = "thm.door_slide";
    SoundEnum["POLE_DIRECTION_CHANGE"] = "thm.chairlift.pold_direction_change";
    SoundEnum["COLOUR_CLICK"] = "thm.colour_click";
    SoundEnum["CLOCK_CLICK"] = "thm.clock.click";
    SoundEnum["CLOCK_BACKGROUND"] = "thm.clock.background";
    SoundEnum["CLOCK_CONTINUE"] = "thm.clock.continue";
    SoundEnum["DRONE_GIVE_SIGNAL"] = "thm.drone.give_signal";
    SoundEnum["DRONE_IDLE"] = "thm.drone.idle";
    SoundEnum["DRONE_LOOK"] = "thm.drone.look";
    SoundEnum["DRONE_RECEIVE"] = "thm.drone.recieve";
    SoundEnum["DRONE_THUD"] = "thm.drone.thud";
    SoundEnum["ITEM_PICKUP"] = "thm.item.pickup";
    SoundEnum["COLLECTABLE"] = "thm.collectible";
    SoundEnum["AMBIENCE"] = "thm.ambience";
    SoundEnum["MENU_MUSIC"] = "thm.menu.music";
    SoundEnum["BUTTON_CLICK"] = "thm.button_click";
    SoundEnum["CHAIRLIFT"] = "thm.chairlift";
    SoundEnum["TAPE"] = "thm.tape";
    SoundEnum["TAPE_INSERT"] = "thm.tape_insert";
    SoundEnum["ELEVATOR_STEAM"] = "thm.elevator.steam";
    SoundEnum["ELEVATOR_STOP"] = "thm.elevator.stop";
    SoundEnum["ELEVATOR_ABRUPT_STOP"] = "thm.elevator.abrupt_stop";
    SoundEnum["ELEVATOR_AMBIENCE"] = "thm.elevator.ambience";
    SoundEnum["ELEVATOR_CLANK"] = "thm.elevator.clank";
    SoundEnum["ELEVATOR_COLLISION"] = "thm.elevator.collision";
    SoundEnum["ELEVATOR_DOOR_OPEN"] = "thm.elevator.door_open";
    SoundEnum["ELEVATOR_EMERGENCY"] = "thm.elevator.emergency";
    SoundEnum["ELEVATOR_FLOOR_MOVING"] = "thm.elevator.floor_moving";
    SoundEnum["ELEVATOR_SLAM"] = "thm.elevator.slam";
    SoundEnum["ELEVATOR_SNAP"] = "thm.elevator.snap";
    SoundEnum["CONFUSION"] = "thm.confusion";
    SoundEnum["CONFUSION_MAIN"] = "thm.confusion.main";
    SoundEnum["CONFUSION_OPILA"] = "thm.confusion.opila";
    SoundEnum["JOSH"] = "thm.josh";
    SoundEnum["FEED_BIRD"] = "thm.feed_bird";
    SoundEnum["GLASS_BREAK"] = "thm.glass_break";
    SoundEnum["CORRECT"] = "thm.correct";
    SoundEnum["CHASE"] = "thm.chase";
    SoundEnum["OPILA_FALL"] = "thm.opila_fall";
})(SoundEnum || (SoundEnum = {}));

var CameraShakeTypeEnum;
(function (CameraShakeTypeEnum) {
    CameraShakeTypeEnum["POSITIONAL"] = "positional";
    CameraShakeTypeEnum["ROTATIONAL"] = "rotational";
})(CameraShakeTypeEnum || (CameraShakeTypeEnum = {}));

class VanillaCmdsUtils {
    static cameraShake(player, intensity = 1, duration = 1, type = CameraShakeTypeEnum.POSITIONAL) {
        player.runCommand(`camerashake add "${player.name}" ${intensity} ${duration} ${type}`);
    }
    static setInputPermissions(player, permission, value) {
        const enabled = value ? "enabled" : "disabled";
        let permisionName = permission === InputPermissionCategory.Camera ? "camera" : "movement";
        player.runCommand(`inputpermission set "${player.name}" ${permisionName} ${enabled}`);
    }
}

class PlayerUtils {
    static initPlayer(player) {
        /* player.onScreenDisplay.hideAllExcept(
            [
                HudElement.Crosshair,
                HudElement.Hotbar,
                HudElement.ItemText,
                HudElement.ToolTips,
                HudElement.TouchControls
            ]
        ) */
        player.onScreenDisplay.setHudVisibility(HudVisibility.Hide, [
            HudElement.Armor,
            HudElement.Health,
            HudElement.Hunger,
            HudElement.AirBubbles,
            HudElement.HorseHealth,
            HudElement.PaperDoll,
            HudElement.ProgressBar,
            HudElement.StatusEffects,
        ]);
        system.runTimeout(() => {
            player.sendMessage({ translate: "message.broadcast.music" });
        }, 5 * TicksPerSecond);
    }
    static getAllPlayers() {
        return world.getAllPlayers().filter(p => p.location.y < 100 && p.getGameMode() != GameMode.Spectator);
    }
    static getPlayersNotInGame() {
        return world.getAllPlayers().filter(p => p.location.y > 100);
    }
    static resetCamera(player) {
        let camera = player.camera;
        camera.setCamera("minecraft:first_person");
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Camera, true);
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Movement, true);
    }
    static setInMenu(player, value) {
        player.setDynamicProperty(DynamicPropertyEnum.IN_MENU, value);
    }
    static isInMenu(player) {
        return player.getDynamicProperty(DynamicPropertyEnum.IN_MENU) ?? false;
    }
    static setHasPlayed(player, value) {
        player.setDynamicProperty(DynamicPropertyEnum.HAS_PLAYED, value);
    }
    static hasPlayed(player) {
        return player.getDynamicProperty(DynamicPropertyEnum.HAS_PLAYED) ?? false;
    }
}

const ambientTimeMin = 4 * 60;
const ambientTimeSec = 16.854;
const ambientTime = Math.floor((ambientTimeMin + ambientTimeSec) * TicksPerSecond);
class SoundUtils {
    static ambientIntervals = new Map();
    static ambientSound(player) {
        const playerID = player.id;
        if (this.ambientIntervals.has(playerID)) {
            system.clearRun(this.ambientIntervals.get(playerID));
            player.runCommand(`stopsound @s ${SoundEnum.AMBIENCE}`);
            this.ambientIntervals.delete(playerID);
        }
        this.ambientIntervals.set(playerID, system.runInterval(() => {
            player.playSound(SoundEnum.AMBIENCE, { volume: 1 });
        }, ambientTime));
    }
    static localSound(location, sound) {
        const dimension = world.getDimension("minecraft:overworld");
        dimension.playSound(sound, location, { volume: 2 });
    }
    static globalSound(sound) {
        const players = PlayerUtils.getAllPlayers();
        players.forEach((player) => {
            player.playSound(sound, { volume: 2 });
        });
    }
}

class BlockUtils {
    /**
     * Converts a direction string ("North", "South", "West", or default) into a corresponding 3D vector.
     *
     * @param {string} direction - The direction to convert into a vector ("North", "South", "West").
     * @returns {Object} An object representing the vector with x, y, and z coordinates.
     *
     */
    static getVectorFromDirection(direction) {
        let vector = { x: 1, y: 0, z: 0 };
        switch (direction) {
            case Direction.North:
                vector = { x: 0, y: 0, z: -1 };
                break;
            case Direction.South:
                vector = { x: 0, y: 0, z: 1 };
                break;
            case Direction.West:
                vector = { x: -1, y: 0, z: 0 };
                break;
        }
        return vector;
    }
    static getOppositeDirection(direction) {
        switch (direction) {
            case Direction.North:
                return Direction.South;
            case Direction.South:
                return Direction.North;
            case Direction.West:
                return Direction.East;
            case Direction.East:
                return Direction.West;
            case Direction.Up:
                return Direction.Down;
            case Direction.Down:
                return Direction.Up;
            default:
                throw new Error("Invalid Direction");
        }
    }
    static clearBarrier(dimension, volume) {
        dimension.fillBlocks(volume, BlockPermutation.resolve(`minecraft:air`));
    }
}

var KeycardColourEnum;
(function (KeycardColourEnum) {
    KeycardColourEnum["BLUE"] = "thm:keycard_blue";
    KeycardColourEnum["YELLOW"] = "thm:keycard_yellow";
    KeycardColourEnum["PURPLE"] = "thm:keycard_purple";
    KeycardColourEnum["ORANGE"] = "thm:keycard_orange";
})(KeycardColourEnum || (KeycardColourEnum = {}));

/**
 * Utility class for vector operations on 3D vectors.
 */
class VectorUtils {
    /**
     * Adds two 3D vectors.
     *
     * @param {Vector3} firstVector3 - The first vector with x, y, and z components.
     * @param {Partial<Vector3>} [secondVector3={}] - The second vector with optional x, y, and z components.
     * @returns {Vector3} A new vector which is the sum of the two input vectors.
     */
    static add(firstVector3, secondVector3 = {}) {
        return {
            x: firstVector3.x + (secondVector3.x ?? 0),
            y: firstVector3.y + (secondVector3.y ?? 0),
            z: firstVector3.z + (secondVector3.z ?? 0)
        };
    }
    static multiply(vector3, secondVector3 = {}) {
        return {
            x: vector3.x * (secondVector3.x ?? 1),
            y: vector3.y * (secondVector3.y ?? 1),
            z: vector3.z * (secondVector3.z ?? 1)
        };
    }
    /**
     * Subtracts the second 3D vector from the first 3D vector.
     *
     * @param {Vector3} firstVector3 - The vector to be subtracted from, with x, y, and z components.
     * @param {Partial<Vector3>} [secondVector3={}] - The vector to subtract, with optional x, y, and z components.
     * @returns {Vector3} A new vector which is the difference of the two input vectors.
     */
    static subtract(firstVector3, secondVector3 = {}) {
        return {
            x: firstVector3.x - (secondVector3.x ?? 0),
            y: firstVector3.y - (secondVector3.y ?? 0),
            z: firstVector3.z - (secondVector3.z ?? 0)
        };
    }
    static normalize(vector) {
        // Calculate the magnitude (length) of the vector
        const magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
        // If the magnitude is 0 (to avoid division by zero), return the original vector
        if (magnitude === 0) {
            return vector;
        }
        // Normalize the vector by dividing each component by the magnitude
        return {
            x: vector.x / magnitude,
            y: vector.y / magnitude,
            z: vector.z / magnitude
        };
    }
    static calculateYawAndPitch(from, to) {
        // Calculate the direction vector
        const direction = this.subtract(to, from);
        // Calculate yaw (rotation around the Y-axis)
        const yaw = Math.atan2(direction.z, direction.x);
        // Calculate pitch (rotation around the X-axis)
        const pitch = Math.atan2(direction.y, Math.sqrt(direction.x * direction.x + direction.z * direction.z));
        // Convert to degrees (optional, if your rotation system uses degrees)
        return {
            yaw: yaw * (180 / Math.PI), // Convert to degrees
            pitch: pitch * (180 / Math.PI) // Convert to degrees
        };
    }
    /**
     * Floors the components of a 3D vector.
     *
     * @param {Vector3} vector3 - The vector with x, y, and z components.
     * @returns {Vector3} A new vector with each component floored to the nearest integer.
     */
    static floor(vector3) {
        return {
            x: Math.floor(vector3.x),
            y: Math.floor(vector3.y),
            z: Math.floor(vector3.z)
        };
    }
    /**
     * Rounds the components of a 3D vector.
     *
     * @param {Vector3} vector3 - The vector with x, y, and z components.
     * @returns {Vector3} A new vector with each component rounded to the nearest integer.
     */
    static round(vector3) {
        return {
            x: Math.round(vector3.x),
            y: Math.round(vector3.y),
            z: Math.round(vector3.z)
        };
    }
    static abs(vector3) {
        return {
            x: Math.abs(vector3.x),
            y: Math.abs(vector3.y),
            z: Math.abs(vector3.z)
        };
    }
    static round2Decimal(vector3) {
        return {
            x: Math.round(vector3.x * 100) / 100,
            y: Math.round(vector3.y * 100) / 100,
            z: Math.round(vector3.z * 100) / 100
        };
    }
    /**
     * Clamps the components of a 3D vector within the given bounds.
     *
     * @param {Vector3} vector3 - The vector to clamp, with x, y, and z components.
     * @param {Partial<Vector3>} [min={}] - The minimum bounds with optional x, y, and z components.
     * @param {Partial<Vector3>} [max={}] - The maximum bounds with optional x, y, and z components.
     * @returns {Vector3} A new vector with each component clamped within the specified bounds.
     */
    static clamp(vector3, min = {}, max = {}) {
        return {
            x: Math.max(min.x ?? 0, Math.min(max.x ?? 1, vector3.x)),
            y: Math.max(min.y ?? 0, Math.min(max.y ?? 1, vector3.y)),
            z: Math.max(min.z ?? 0, Math.min(max.z ?? 1, vector3.z))
        };
    }
    /**
     * Computes the absolute difference between two 3D vectors.
     *
     * @param {Vector3} firstVector3 - The first vector with x, y, and z components.
     * @param {Vector3} secondVector3 - The second vector with x, y, and z components.
     * @returns {Vector3} A new vector representing the absolute difference between the two input vectors.
     */
    static difference(firstVector3, secondVector3) {
        return {
            x: Math.abs(firstVector3.x - secondVector3.x),
            y: Math.abs(firstVector3.y - secondVector3.y),
            z: Math.abs(firstVector3.z - secondVector3.z)
        };
    }
    static equals(firstVector3, secondVector3) {
        return firstVector3.x === secondVector3.x && firstVector3.y === secondVector3.y && firstVector3.z === secondVector3.z;
    }
    static equals2(firstVector2, secondVector2) {
        return firstVector2.x === secondVector2.x && firstVector2.y === secondVector2.y;
    }
    static toString(vector3) {
        return `x:${vector3.x}, y:${vector3.y}, z:${vector3.z}`;
    }
    static toVector3(vector2) {
        return { x: vector2.x, y: 0, z: vector2.y };
    }
    static toVector2(vector3) {
        return { x: vector3.x, y: vector3.z };
    }
}

class NotificationUtils {
    static keyCardNotification(keyCardColour) {
        switch (keyCardColour) {
            case KeycardColourEnum.BLUE:
                this.broadcastNotification('\n1x\n');
                break;
            case KeycardColourEnum.YELLOW:
                this.broadcastNotification('\n1x\n');
                break;
            case KeycardColourEnum.PURPLE:
                this.broadcastNotification('\n1x\n');
                break;
            case KeycardColourEnum.ORANGE:
                this.broadcastNotification('\n1x\n');
                break;
        }
    }
    static remoteNotification() {
        this.broadcastNotification('\n1x\n');
    }
    static batteryNotification(player) {
        this.playerNotification(player, '\n1x\n');
        this.broadcastNotification({ translate: "message.pickup.broadcast.battery", with: [`${player.name}`] }, [player]);
    }
    static remoteInfoNotification(player) {
        this.broadcastNotification({ translate: "message.broadcast.remote.howTo" });
    }
    static tapeNotification(player) {
        this.playerNotification(player, '\n1x\n');
        this.broadcastNotification({ translate: "message.pickup.broadcast.tape", with: [`${player.name}`] }, [player]);
    }
    static hammerNotification(player) {
        this.playerNotification(player, '\n1x\n');
        this.broadcastNotification({ translate: "message.pickup.broadcast.hammer", with: [`${player.name}`] }, [player]);
    }
    static eggNotification(player) {
        let eggCount = world.getDynamicProperty(DynamicPropertyEnum.EGGS_PICKED) ?? 0;
        eggCount++;
        this.playerNotification(player, `\n1x §f(§e${eggCount}§f/§66§f)\n`);
        this.broadcastNotification({ translate: "message.pickup.broadcast.egg", with: [`${player.name}`, `${eggCount}`] }, [player]);
    }
    static chaseNotification() {
        this.broadcastNotification({ translate: "message.broadcast.chase" });
    }
    static playerNotification(player, message) {
        player.sendMessage(message);
        player.playSound(SoundEnum.ITEM_PICKUP);
    }
    static broadcastNotification(message, exclude) {
        const players = PlayerUtils.getAllPlayers();
        for (const player of players) {
            if (exclude && exclude.some(excludedPlayer => excludedPlayer.name === player.name))
                continue;
            this.playerNotification(player, message);
        }
    }
}

class DroneUtils {
    static getDrone() {
        return world.getDimension("minecraft:overworld").getEntities({ type: "thm:drone" })[0];
    }
    static setAngle(droneEntity, option = {}) {
        let pitch = option.pitch ? option.pitch : 0;
        let yaw = option.yaw ? option.yaw : 0;
        let roll = option.roll ? option.roll : 0;
        droneEntity.setProperty(ActorPropertiesEnum.ANIM_PITCH, pitch);
        droneEntity.setProperty(ActorPropertiesEnum.ANIM_YAW, yaw);
        droneEntity.setProperty(ActorPropertiesEnum.ANIM_ROLL, roll);
    }
    static getObjective(droneEntity) {
        const vector = droneEntity.getDynamicProperty(DynamicPropertyEnum.DRONE_OBJECTIVE);
        return vector ? vector : false;
    }
    static setObjective(droneEntity, location) {
        droneEntity.setDynamicProperty(DynamicPropertyEnum.DRONE_OBJECTIVE, location);
    }
    static hasObjective(droneEntity) {
        const hasObjective = droneEntity.getDynamicProperty(DynamicPropertyEnum.DRONE_HAS_OBJECTIVE);
        return hasObjective ? hasObjective : false;
    }
    static setHasObjective(droneEntity, value) {
        droneEntity.setDynamicProperty(DynamicPropertyEnum.DRONE_HAS_OBJECTIVE, value);
    }
    static isInUse(droneEntity) {
        const inObjective = droneEntity.getDynamicProperty(DynamicPropertyEnum.DRONE_IN_USE);
        return inObjective ? inObjective : false;
    }
    static setIsInUse(droneEntity, value) {
        droneEntity.setDynamicProperty(DynamicPropertyEnum.DRONE_IN_USE, value);
    }
}

class ItemUtils {
    static giveItem(item, entity) {
        let inventory = entity.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        if (container?.emptySlotsCount == 0)
            this.dropItem(item, entity);
        else
            container?.addItem(item);
    }
    static giveCollectables(player) {
        const collectable1 = world.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE1);
        const collectable2 = world.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE2);
        const collectable3 = world.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE3);
        const collectable4 = world.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE4);
        const collectable5 = world.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE5);
        const collectable6 = world.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE6);
        if (collectable1) {
            if (!player.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE1)) {
                this.giveItem(this.createItemWithLocks("thm:boarding_pass_1"), player);
                player.setDynamicProperty(DynamicPropertyEnum.COLLECATABLE1, true);
            }
        }
        if (collectable2) {
            if (!player.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE2)) {
                this.giveItem(this.createItemWithLocks("thm:boarding_pass_2"), player);
                player.setDynamicProperty(DynamicPropertyEnum.COLLECATABLE2, true);
            }
        }
        if (collectable3) {
            if (!player.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE3)) {
                this.giveItem(this.createItemWithLocks("thm:letter_1"), player);
                player.setDynamicProperty(DynamicPropertyEnum.COLLECATABLE3, true);
            }
        }
        if (collectable4) {
            if (!player.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE4)) {
                this.giveItem(this.createItemWithLocks("thm:letter_2"), player);
                player.setDynamicProperty(DynamicPropertyEnum.COLLECATABLE4, true);
            }
        }
        if (collectable5) {
            if (!player.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE5)) {
                this.giveItem(this.createItemWithLocks("thm:note"), player);
                player.setDynamicProperty(DynamicPropertyEnum.COLLECATABLE5, true);
            }
        }
        if (collectable6) {
            if (!player.getDynamicProperty(DynamicPropertyEnum.COLLECATABLE6)) {
                this.giveItem(this.createItemWithLocks("thm:stickers"), player);
                player.setDynamicProperty(DynamicPropertyEnum.COLLECATABLE6, true);
            }
        }
    }
    static removeItem(item, player) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        if (!container)
            return;
        const containerSize = container.size;
        for (let i = 0; i < containerSize; i++) {
            const checkItem = container.getItem(i);
            if (checkItem?.typeId === item) {
                container?.setItem(i, undefined);
                break;
            }
        }
    }
    static dropItem(item, entity) {
        entity.dimension.spawnItem(item, entity.location);
    }
    static getMainHandItem(player) {
        const equippable = player.getComponent(EntityComponentTypes.Equippable);
        return equippable.getEquipment(EquipmentSlot.Mainhand);
    }
    static createItemWithLocks(itemTypeId) {
        let item = new ItemStack(itemTypeId);
        item.lockMode = ItemLockMode.inventory;
        item.keepOnDeath = true;
        return item;
    }
    static createKeycardItem(cardColor) {
        let item = new ItemStack(`${cardColor}`);
        item.lockMode = ItemLockMode.slot;
        item.keepOnDeath = true;
        return item;
    }
    static createRemote() {
        let item = new ItemStack(`thm:remote`);
        item.lockMode = ItemLockMode.slot;
        item.keepOnDeath = true;
        return item;
    }
    static setKeycard(player, cardColor) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        let slot = 0;
        switch (cardColor) {
            case KeycardColourEnum.BLUE:
                slot = 5;
                break;
            case KeycardColourEnum.YELLOW:
                slot = 6;
                break;
            case KeycardColourEnum.ORANGE:
                slot = 7;
                break;
            case KeycardColourEnum.PURPLE:
                slot = 8;
                break;
        }
        const item = this.createKeycardItem(cardColor);
        item.setCanPlaceOn(["thm:reader"]);
        container?.setItem(slot, item);
    }
    static setRemote(player) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        const item = this.createRemote();
        container?.setItem(0, item);
    }
    static setBlank(player, slot) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        let item = new ItemStack(`thm:blank`);
        item.lockMode = ItemLockMode.slot;
        item.keepOnDeath = true;
        container?.setItem(slot, item);
    }
}

class ClockController {
    static start() {
        world.setDynamicProperty(DynamicPropertyEnum.CLOCK_ACTIVE, true);
    }
    static setPlayer(player) {
        player.teleport({ x: -35, y: 63, z: 39 });
        player.runCommand("camera @s set minecraft:free pos -19.5 66 38.5 rot 20 135");
        this.giveClock(player, getClockCount());
        const tickID = system.runInterval(() => {
            if (player) {
                try {
                    player.onScreenDisplay.setActionBar({ translate: "clock.actionBar" });
                }
                catch (error) {
                }
            }
        }, 1);
        player.setDynamicProperty(DynamicPropertyEnum.CLOCK_TICK_ID, tickID);
        player.addEffect("invisibility", 1000000, { showParticles: false });
        system.runTimeout(() => {
            if (!player)
                return;
            player.playSound(SoundEnum.CLOCK_BACKGROUND, { volume: 2, pitch: 1 });
        }, 2);
    }
    static advanceTime() {
        let clockCount = getClockCount();
        clockCount++;
        setClockCount(clockCount);
        world
            .getDimension("overworld")
            .playSound(SoundEnum.CLOCK_CLICK, { x: -20, y: 65, z: 38 }, { volume: 2 });
        world.setDynamicProperty(DynamicPropertyEnum.CLOCK_CAN_CLICK, false);
        const players = PlayerUtils.getAllPlayers();
        for (let player of players) {
            this.giveClock(player, clockCount);
            if (clockCount >= 6) {
                const tickID = player.getDynamicProperty(DynamicPropertyEnum.CLOCK_TICK_ID);
                if (tickID)
                    system.clearRun(tickID);
                PlayerController.joinGame(player);
                world.setDynamicProperty(DynamicPropertyEnum.CLOCK_ACTIVE, false);
                player.runCommand(`stopSound @s ${SoundEnum.CLOCK_BACKGROUND}`);
            }
            else {
                let camera = player.camera;
                camera.fade({
                    fadeTime: { fadeInTime: 0.5, fadeOutTime: 0.5, holdTime: 1 },
                    fadeColor: { red: 0, green: 0, blue: 0 },
                });
            }
        }
        if (clockCount >= 6) {
            world.setDynamicProperty(DynamicPropertyEnum.ACTIVE_GAME, true);
            const ingamePlayers = PlayerUtils.getAllPlayers();
            const notInGamePlayers = world.getAllPlayers().filter(p => !ingamePlayers.includes(p));
            let playersToJoin = [...ingamePlayers];
            let remainingSpots = 4 - ingamePlayers.length;
            const notInGamePlayersToJoin = notInGamePlayers.slice(0, remainingSpots);
            playersToJoin = playersToJoin.concat(notInGamePlayersToJoin);
            for (let player of playersToJoin) {
                PlayerController.joinGame(player);
                player.runCommand(`stopSound @s ${SoundEnum.CLOCK_BACKGROUND}`);
                system.runTimeout(() => { PlayerUtils.resetCamera(player); }, 20);
            }
            return;
        }
        system.runTimeout(() => {
            setScene(clockCount);
        }, 5);
        system.runTimeout(() => {
            world.setDynamicProperty(DynamicPropertyEnum.CLOCK_CAN_CLICK, true);
        }, 3 * TicksPerSecond);
    }
    static giveClock(player, clockCount) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        let item = new ItemStack("thm:clock_1", 1);
        switch (clockCount) {
            case 0:
                item = new ItemStack("thm:clock_1", 1);
                break;
            case 1:
                item = new ItemStack("thm:clock_2", 1);
                break;
            case 2:
                item = new ItemStack("thm:clock_3", 1);
                break;
            case 3:
                item = new ItemStack("thm:clock_4", 1);
                break;
            case 4:
                item = new ItemStack("thm:clock_5", 1);
                break;
            case 5:
                item = new ItemStack("thm:clock_6", 1);
                break;
            default:
                inventory.container?.setItem(4, undefined);
                return;
        }
        item.lockMode = ItemLockMode.slot;
        inventory.container?.setItem(4, item);
    }
}
function setScene(clockCount) {
    const dimension = world.getDimension("overworld");
    const firstChairBlock = dimension.getBlock({ x: -21, y: 63, z: 35 });
    const secondChairBlock = dimension.getBlock({ x: -21, y: 63, z: 31 });
    const doors = dimension.getEntities({
        location: { x: -32, y: 63, z: 31 },
        maxDistance: 5,
        type: "thm:door",
        closest: 2,
    });
    switch (clockCount) {
        case 1:
            firstChairBlock?.setPermutation(BlockPermutation.resolve("minecraft:air"));
            secondChairBlock?.setPermutation(BlockPermutation.resolve("thm:desk_chair_side_bw"));
            break;
        case 2:
            const planksHammer = dimension.spawnEntity("thm:planks_hammer", {
                x: -29,
                y: 63.6,
                z: 35.4,
            });
            world.setDynamicProperty(DynamicPropertyEnum.PLANKS_HAMMER_ID, planksHammer.id);
            break;
        case 3:
            const planksHammerId = world.getEntity(world.getDynamicProperty(DynamicPropertyEnum.PLANKS_HAMMER_ID));
            planksHammerId?.remove();
            for (let door of doors) {
                door.setProperty(ActorPropertiesEnum.FORCE_SLIDE, true);
            }
            break;
        case 4:
            firstChairBlock?.setPermutation(BlockPermutation.resolve("thm:desk_chair_bw"));
            secondChairBlock?.setPermutation(BlockPermutation.resolve("minecraft:air"));
            break;
        case 5:
            dimension?.getBlock({ x: -30, y: 63, z: 27 })?.setPermutation(BlockPermutation.resolve("minecraft:air"));
            dimension?.getBlock({ x: -30, y: 63, z: 36 })?.setPermutation(BlockPermutation.resolve("minecraft:air"));
            dimension?.getBlock({ x: -36, y: 67, z: 30 })?.setPermutation(BlockPermutation.resolve("minecraft:air"));
            break;
    }
}
function setClockCount(value) {
    world.setDynamicProperty(DynamicPropertyEnum.CLOCK_COUNT, value);
}
function getClockCount() {
    return world.getDynamicProperty(DynamicPropertyEnum.CLOCK_COUNT) ?? 0;
}

const CreditsTeamData = [
    {
        name: 'Tomhmagic',
        role: { translate: "credits.role.lead" },
        contribution: [
            { translate: "credits.contribution.lead" },
            { text: "\n" },
            { translate: "credits.contribution.gameplay" },
            { text: "\n" },
            { translate: "credits.contribution.blocks" },
            { text: "\n" },
            { translate: "credits.contribution.textures" },
            { text: "\n" },
            { translate: "credits.contribution.builder" },
        ]
    },
    {
        name: 'Rothruindir',
        role: { translate: "credits.role.builder" },
        contribution: [
            { translate: "credits.contribution.builder" },
        ]
    },
    {
        name: 'SmokeyStack',
        role: { translate: "credits.role.developer" },
        contribution: [
            { translate: "credits.contribution.developer" },
        ]
    },
    {
        name: 'Mr Magic Blox',
        role: { translate: "credits.role.artist" },
        contribution: [
            { translate: "credits.contribution.artist" },
        ]
    },
    {
        name: 'Suirlina',
        role: { translate: "credits.role.modeler" },
        contribution: [
            { translate: "credits.contribution.modeler" },
        ]
    },
    {
        name: 'Maunsten',
        role: { translate: "credits.role.keyArtist" },
        contribution: [
            { translate: "credits.contribution.keyArtist" },
            { text: "\n" },
            { translate: "credits.contribution.rendering" },
        ]
    },
];

async function creditsForm(player) {
    let form = new ActionFormData();
    form.title({ translate: "form.button.credits" });
    const data = CreditsTeamData;
    const body = { rawtext: [] };
    body.rawtext.push({ translate: "credits.gameBy" }, { text: " " }, { translate: "socials.euphoric" }, { text: "\n\n\n" }, { translate: "credits.createdBy" }, { text: " " }, { translate: "socials.tomhmagic" }, { text: "\n\n" });
    data.forEach((item, index) => {
        body.rawtext.push({ text: item.name });
        body.rawtext.push({ text: "\n" });
        body.rawtext.push(item.role);
        body.rawtext.push({ text: "\n" });
        body.rawtext.push({ translate: "credits.role" });
        body.rawtext.push({ text: "\n" });
        item.contribution.forEach(contribution => {
            body.rawtext.push(contribution);
        });
        if (index < data.length - 1) {
            body.rawtext.push({ text: "\n\n" });
        }
    });
    form.body(body);
    form.button({ translate: "form.button.back" });
    await form.show(player);
    return true;
}

async function startForm(player) {
    let form = new ActionFormData();
    form.title({ translate: "form.start.title" });
    player.runCommand(`playsound random.click`);
    PlayerUtils.setInMenu(player, true);
    const started = world.getDynamicProperty(DynamicPropertyEnum.ACTIVE_GAME) ?? false;
    const inClock = world.getDynamicProperty(DynamicPropertyEnum.CLOCK_ACTIVE) ?? false;
    const activeGame = started || inClock;
    const body2 = activeGame ? { translate: "form.start.body.4" } : { translate: "form.start.body.3" };
    const body = {
        rawtext: [
            { translate: "form.start.body.1" },
            { text: "\n\n" },
            { translate: "form.start.body.2" },
            { text: "\n\n" },
            body2,
            { text: "\n\n" },
            { translate: "credits.gameBy" },
            { text: " " },
            { translate: "socials.euphoric" },
            { text: "\n\n" },
            { translate: "socials.euphoric.yt" },
            { text: "\n\n" },
            { translate: "socials.euphoric.x" },
            { text: "\n\n" },
            { translate: "socials.euphoric.web" },
            { text: "\n\n\n" },
            { translate: "credits.createdBy" },
            { text: " " },
            { translate: "socials.tomhmagic" },
            { text: "\n\n" },
            { translate: "socials.tomhmagic.discord" },
            { text: "\n\n" },
            { translate: "socials.tomhmagic.yt" },
            { text: "\n\n" },
            { translate: "socials.tomhmagic.web" },
            { text: "\n\n" },
        ]
    };
    form.body(body);
    form.button(activeGame ? { translate: "form.button.join" } : { translate: "form.button.start" });
    form.button({ translate: "form.button.credits" });
    const result = await form.show(player);
    if (!result)
        return;
    if (result.canceled)
        return;
    const selection = result.selection;
    if (selection === undefined)
        return;
    const endGame = world.getDynamicProperty(DynamicPropertyEnum.GAMEEND) ?? false;
    if (endGame) {
        PlayerController.afterGame(player);
        return;
    }
    if (selection === 0 && activeGame) {
        resetPlayer(player);
        PlayerUtils.setInMenu(player, false);
        system.run(() => {
            if (inClock) {
                ClockController.setPlayer(player);
                PlayerUtils.setInMenu(player, false);
            }
            else {
                PlayerController.joinGame(player);
                PlayerUtils.resetCamera(player);
            }
        });
        return;
    }
    else if (selection === 0 && !activeGame) {
        const clockActive = world.getDynamicProperty(DynamicPropertyEnum.CLOCK_ACTIVE) ?? false;
        if (clockActive) {
            ClockController.setPlayer(player);
            PlayerUtils.setInMenu(player, false);
        }
        else {
            startGame();
        }
    }
    else if (selection === 1) {
        await creditsForm(player);
        startForm(player);
        return;
    }
    return;
}
function resetPlayer(player) {
    const intervalID = player.getDynamicProperty(DynamicPropertyEnum.START_PLAYER_INTERVAL);
    const menuTickID = player.getDynamicProperty(DynamicPropertyEnum.START_PLAYER_TICK);
    if (intervalID)
        system.clearRun(intervalID);
    if (menuTickID)
        system.clearRun(menuTickID);
    player.runCommand(`stopsound @s ${SoundEnum.MENU_MUSIC}`);
}
function startGame() {
    const players = PlayerUtils.getAllPlayers();
    for (const player of players) {
        resetPlayer(player);
        uiManager.closeAllForms(player);
        ClockController.setPlayer(player);
        PlayerUtils.setInMenu(player, false);
    }
    world.setDynamicProperty(DynamicPropertyEnum.CLOCK_ACTIVE, true);
    system.runTimeout(() => { ClockController.start(); }, 1);
}

class ProgressUtils {
    /*
    Base completions i 85%
    1 for each collectable (6 total)
    1 for picking up the tape
    1 for playing the tape
    1 for collecting the 7th egg
    1 for collecting the purple keycard
    5 for opening the purple door
     */
    static getProgress() {
        return world.getDynamicProperty(DynamicPropertyEnum.PROGRESS) ?? 85;
    }
    static addProgress(amount) {
        const progress = this.getProgress();
        const newProgress = progress + amount;
        world.setDynamicProperty(DynamicPropertyEnum.PROGRESS, newProgress);
        return newProgress <= 100 ? newProgress : 100;
    }
}

async function endForm(player) {
    let form = new ActionFormData();
    form.title({ translate: "form.start.title" });
    PlayerUtils.setInMenu(player, true);
    const progress = ProgressUtils.getProgress();
    let colour = "§c";
    if (progress >= 100)
        colour = "§a";
    else if (progress >= 90)
        colour = "§6";
    const body = {
        rawtext: [
            { translate: "form.end.body.1" },
            { text: "\n\n" },
            { translate: "form.end.completed", with: [`${colour}${progress}`] },
            { text: "\n\n" },
            { translate: "form.end.body.2" },
            { text: "\n\n" },
            { translate: "credits.gameBy" },
            { text: " " },
            { translate: "socials.euphoric" },
            { text: "\n\n" },
            { translate: "socials.euphoric.yt" },
            { text: "\n\n" },
            { translate: "socials.euphoric.x" },
            { text: "\n\n" },
            { translate: "socials.euphoric.web" },
            { text: "\n\n\n" },
            { translate: "credits.createdBy" },
            { text: " " },
            { translate: "socials.tomhmagic" },
            { text: "\n\n" },
            { translate: "socials.tomhmagic.discord" },
            { text: "\n\n" },
            { translate: "socials.tomhmagic.yt" },
            { text: "\n\n" },
            { translate: "socials.tomhmagic.web" },
            { text: "\n\n" },
        ]
    };
    form.body(body);
    form.button({ translate: "form.button.credits" });
    const result = await form.show(player);
    if (!result)
        return;
    if (result.canceled)
        return;
    const selection = result.selection;
    if (selection === undefined)
        return;
    if (selection === 0) {
        await creditsForm(player);
        endForm(player);
        return;
    }
    return;
}

class PlayerController {
    static waitingPlayers = [];
    static isPlayerWaiting(player) {
        if (PlayerController.waitingPlayers.length === 0)
            return false;
        return PlayerController.waitingPlayers.includes(player);
    }
    static lobbyPreGame(eventData) {
        const entity = eventData.damagingEntity;
        if (!(entity instanceof Player))
            return;
        const player = entity;
        if (!player)
            return;
        PlayerController.preGame(player);
    }
    static async preGame(player) {
        //if(PlayerController.isPlayerWaiting(player)) return;
        const endGame = world.getDynamicProperty(DynamicPropertyEnum.GAMEEND) ?? false;
        const activeGame = world.getDynamicProperty(DynamicPropertyEnum.ACTIVE_GAME) ?? false;
        const inClock = world.getDynamicProperty(DynamicPropertyEnum.CLOCK_ACTIVE) ?? false;
        const inChase = world.getDynamicProperty(DynamicPropertyEnum.IN_CHASE) ?? false;
        const chaseComplete = world.getDynamicProperty(DynamicPropertyEnum.CHASE_COMPLETE) ?? false;
        const insideArea = new BlockVolume({ x: 0, y: 46, z: 76 }, { x: -6, y: 53, z: 49 });
        if (inChase || (chaseComplete && insideArea.isInside(player.location))) {
            const otherPlayers = PlayerUtils.getAllPlayers().filter(p => p !== player);
            if (otherPlayers.length === 0)
                return;
            const tpPlayer = otherPlayers[Math.floor(Math.random() * otherPlayers.length)];
            PlayerController.joinGame(player, true);
            player.teleport({ x: tpPlayer.location.x, y: tpPlayer.location.y, z: tpPlayer.location.z }, { rotation: { x: 0, y: 0 }, facingLocation: { x: tpPlayer.location.x, y: tpPlayer.location.y + 1, z: tpPlayer.location.z } });
            return;
        }
        if (endGame) {
            PlayerController.afterGame(player);
            return;
        }
        if (!inClock && !activeGame) {
            ClockController.setPlayer(player);
            ClockController.start();
            return;
        }
        if (inClock) {
            ClockController.setPlayer(player);
            return;
        }
        const players = PlayerUtils.getAllPlayers();
        if (players.length >= 4) {
            player.sendMessage({ translate: "menu.start.full" });
            player.playSound(SoundEnum.ERROR);
            return;
        }
        let camera = player.camera;
        camera.fade({
            fadeTime: { fadeInTime: 0.5, fadeOutTime: 0.5, holdTime: 1 },
            fadeColor: { red: 0, green: 0, blue: 0 },
        });
        //PlayerController.waitingPlayers.push(player);
        system.runTimeout(() => {
            if (activeGame) {
                const hasPlayed = PlayerUtils.hasPlayed(player);
                PlayerController.joinGame(player, hasPlayed);
            }
            //PlayerController.waitingPlayers = PlayerController.waitingPlayers.filter(p => p !== player);
        }, TicksPerSecond);
    }
    static async beforeGame(eventData) {
        const player = eventData.player;
        let camera = player.camera;
        const easeTime = 30;
        const leftY = 250;
        const rightY = 190;
        PlayerController.saveItems(player);
        PlayerController.clear(player);
        player.teleport({ x: 4.00, y: 50, z: -2.50 }, { rotation: { x: 0, y: 0 }, facingLocation: { x: 4.00, y: 51, z: 10 } });
        player.setSpawnPoint({ dimension: player.dimension, x: 4.00, y: 50, z: -2.50 });
        startForm(player);
        player.playSound(SoundEnum.MENU_MUSIC, { volume: 2 });
        camera.setCamera("minecraft:free", { location: { x: 5, y: 54, z: 10 }, rotation: { x: 20, y: leftY } });
        camera.setCamera("minecraft:free", { easeOptions: { easeTime: easeTime, easeType: EasingType.InOutQuad }, location: { x: 5, y: 54, z: 10 }, rotation: { x: 20, y: rightY } });
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        let item = new ItemStack("thm:menu", 1);
        item.lockMode = ItemLockMode.slot;
        inventory.container?.setItem(4, item);
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Camera, false);
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Movement, false);
        let left = false;
        const intervalID = system.runInterval(() => {
            if (!player)
                return;
            if (!camera)
                return;
            const rot = left ? rightY : leftY;
            camera.setCamera("minecraft:free", { easeOptions: { easeTime: easeTime, easeType: EasingType.InOutQuad }, location: { x: 5, y: 54, z: 10 }, rotation: { x: 20, y: rot } });
            left = !left;
        }, easeTime * TicksPerSecond);
        const tickID = system.runInterval(() => {
            if (!player)
                return;
            player.onScreenDisplay.setActionBar({ translate: "menu.start.actionBar" });
            player.addEffect("invisibility", 30, { showParticles: false });
        }, 20);
        player.setDynamicProperty(DynamicPropertyEnum.START_PLAYER_INTERVAL, intervalID);
        player.setDynamicProperty(DynamicPropertyEnum.START_PLAYER_TICK, tickID);
    }
    static async afterGame(player) {
        let camera = player.camera;
        const easeTime = 30;
        const leftY = 250;
        const rightY = 190;
        PlayerController.clear(player);
        player.teleport({ x: 4.00, y: 50, z: -2.50 }, { rotation: { x: 0, y: 0 }, facingLocation: { x: 4.00, y: 51, z: 10 } });
        endForm(player);
        camera.setCamera("minecraft:free", { location: { x: 5, y: 54, z: 10 }, rotation: { x: 20, y: leftY } });
        camera.setCamera("minecraft:free", { easeOptions: { easeTime: easeTime, easeType: EasingType.InOutQuad }, location: { x: 5, y: 54, z: 10 }, rotation: { x: 20, y: rightY } });
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        let item = new ItemStack("thm:menu", 1);
        item.lockMode = ItemLockMode.slot;
        inventory.container?.setItem(4, item);
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Camera, false);
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Movement, false);
        let left = false;
        const intervalID = system.runInterval(() => {
            if (!player || !camera) {
                system.clearRun(intervalID);
                return;
            }
            const rot = left ? rightY : leftY;
            camera.setCamera("minecraft:free", { easeOptions: { easeTime: easeTime, easeType: EasingType.InOutQuad }, location: { x: 5, y: 54, z: 10 }, rotation: { x: 20, y: rot } });
            left = !left;
        }, easeTime * TicksPerSecond);
        const tickID = system.runInterval(() => {
            if (!player) {
                system.clearRun(tickID);
            }
            try {
                player.onScreenDisplay.setActionBar({ translate: "menu.start.actionBar" });
                player.addEffect("invisibility", 1000000, { showParticles: false });
            }
            catch (error) {
                system.clearRun(tickID);
                return;
            }
        }, 20);
        player.setDynamicProperty(DynamicPropertyEnum.START_PLAYER_INTERVAL, intervalID);
        player.setDynamicProperty(DynamicPropertyEnum.START_PLAYER_TICK, tickID);
    }
    static joinGame(player, hasPlayed = false) {
        let camera = player.camera;
        const intervalID = player.getDynamicProperty(DynamicPropertyEnum.START_PLAYER_INTERVAL) ?? 0;
        const tickID = player.getDynamicProperty(DynamicPropertyEnum.START_PLAYER_TICK) ?? 0;
        system.clearRun(intervalID);
        system.clearRun(tickID);
        player.setDynamicProperty(DynamicPropertyEnum.START_PLAYER_INTERVAL, undefined);
        player.setDynamicProperty(DynamicPropertyEnum.START_PLAYER_TICK, undefined);
        camera.fade({
            fadeTime: { fadeInTime: 0.5, fadeOutTime: 0.5, holdTime: 1 },
            fadeColor: { red: 0, green: 0, blue: 0 },
        });
        //PlayerController.clear(player);
        PlayerController.setItems(player);
        //PlayerController.loadItems(player);
        const effects = player.getEffects();
        for (const effect of effects) {
            player.removeEffect(effect.typeId);
        }
        PlayerUtils.resetCamera(player);
        if (!hasPlayed)
            player.teleport({ x: 4.00, y: 50, z: -2.50 }, { rotation: { x: 0, y: 0 }, facingLocation: { x: 4.00, y: 51, z: 10 } });
        PlayerUtils.setHasPlayed(player, true);
        /* system.runTimeout(() => {
            SoundUtils.ambientSound(player);
        }, 3 * TicksPerSecond); */
    }
    static setItems(player) {
        const hasRemote = world.getDynamicProperty(DynamicPropertyEnum.REMOTE_COLLECTED) ?? false;
        if (hasRemote)
            ItemUtils.setRemote(player);
        else
            ItemUtils.setBlank(player, 0);
        const hasBlueKeycard = world.getDynamicProperty(DynamicPropertyEnum.BLUE_CARD_COLLECTED) ?? false;
        if (hasBlueKeycard)
            ItemUtils.setKeycard(player, KeycardColourEnum.BLUE);
        else
            ItemUtils.setBlank(player, 5);
        const hasYellowKeycard = world.getDynamicProperty(DynamicPropertyEnum.YELLOW_CARD_COLLECTED) ?? false;
        if (hasYellowKeycard)
            ItemUtils.setKeycard(player, KeycardColourEnum.YELLOW);
        else
            ItemUtils.setBlank(player, 6);
        const hasOrangeKeycard = world.getDynamicProperty(DynamicPropertyEnum.ORANGE_CARD_COLLECTED) ?? false;
        if (hasOrangeKeycard)
            ItemUtils.setKeycard(player, KeycardColourEnum.ORANGE);
        else
            ItemUtils.setBlank(player, 7);
        const hasPurpleKeycard = world.getDynamicProperty(DynamicPropertyEnum.PURPLE_CARD_COLLECTED) ?? false;
        if (hasPurpleKeycard)
            ItemUtils.setKeycard(player, KeycardColourEnum.PURPLE);
        else
            ItemUtils.setBlank(player, 8);
        ItemUtils.giveCollectables(player);
    }
    static saveItems(player) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        if (!container)
            return;
        for (let i = 0; i < 36; i++) {
            const item = container.getItem(i);
            if (!item)
                continue;
            const skipItems = [
                "thm:menu",
                "thm:remote",
                "thm:keycard_blue",
                "thm:keycard_yellow",
                "thm:keycard_orange",
                "thm:keycard_purple",
                "thm:blank",
                "thm:clock_1",
                "thm:clock_2",
                "thm:clock_3",
                "thm:clock_4",
                "thm:clock_5",
                "thm:clock_6"
            ];
            if (skipItems.includes(item.typeId))
                continue;
            player.setDynamicProperty(`${DynamicPropertyEnum.SAVED_ITEMS}${i}`, item.typeId);
            player.setDynamicProperty(`${DynamicPropertyEnum.SAVED_ITEMS_AMOUNT}${i}`, item.amount);
        }
    }
    static loadItems(player) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        if (!container)
            return;
        for (let i = 0; i < 36; i++) {
            const typeId = player.getDynamicProperty(`${DynamicPropertyEnum.SAVED_ITEMS}${i}`) ?? "";
            const amount = player.getDynamicProperty(`${DynamicPropertyEnum.SAVED_ITEMS_AMOUNT}${i}`) ?? 1;
            if (typeId === "")
                continue;
            const item = new ItemStack(typeId, amount);
            container.setItem(i, item);
        }
    }
    static clear(player) {
        const inventory = player.getComponent(EntityComponentTypes.Inventory);
        const container = inventory.container;
        if (!container)
            return;
        for (let i = 0; i < 36; i++) {
            container.clearAll();
        }
    }
}

class GameController {
    static pickUpRemote() {
        //Triggered from picking up the remote
        //Logic
        //Update data that remote is collected
        //Give all players remote
        //Notification finding 2 batteries
        system.runTimeout(() => {
            NotificationUtils.broadcastNotification({ translate: "message.remote.batteries" });
        }, 20);
        const dimension = world.getDimension("minecraft:overworld");
        dimension.spawnEntity("thm:battery", { x: -8.5, y: 51, z: 5.5 });
        dimension.spawnEntity("thm:battery", { x: -16.5, y: 51, z: -1 });
    }
    static completeBattery() {
        //Triggered from collecting the last battery
        //Logic
        //Activate drone
        const dimension = world.getDimension("minecraft:overworld");
        const target = dimension.spawnEntity("thm:drone_target", { x: 1, y: 52, z: 8 });
        target.setProperty(ActorPropertiesEnum.BLANK, true);
        const drone = DroneUtils.getDrone();
        drone.triggerEvent("tomhmagic:drone_on");
    }
    static completeColourLight() {
        //Triggered from completing the colour light puzzle
        //Logic
        //Open door for orange keycard
        const dimension = world.getDimension("minecraft:overworld");
        const door = dimension.getEntities({ type: "thm:door", tags: ["ballpit"] })[0];
        door?.setProperty("property:open", true);
        const block = dimension.getBlock({ x: -2, y: 48, z: 77 });
        block?.setPermutation(BlockPermutation.resolve("air"));
    }
    static startLift() {
        const barrierArea = new BlockVolume({ x: 8, y: 50, z: 55 }, { x: 8, y: 51, z: 62 });
        const dimension = world.getDimension("minecraft:overworld");
        const playersInTheWay = dimension.getPlayers({ location: { x: 8, y: 50, z: 55 }, volume: { x: 8, y: 4, z: 5 } });
        for (const player of playersInTheWay) {
            player.teleport({ x: 4, y: 50, z: 56 });
        }
        dimension.fillBlocks(barrierArea, "minecraft:barrier");
        const fillArea = new BlockVolume({ x: 9, y: 49, z: 62 }, { x: 13, y: 51, z: 55 });
        dimension.fillBlocks(fillArea, "minecraft:air");
        const floor = dimension.spawnEntity("thm:fake_floor", { x: 9.5, y: 50, z: 55.5 });
        dimension.playSound(SoundEnum.ELEVATOR_FLOOR_MOVING, { x: 9, y: 50, z: 58 }, { volume: 2 });
        system.run(() => {
            dimension.playSound(SoundEnum.ELEVATOR_AMBIENCE, { x: 9, y: 48, z: 58 }, { volume: 2 });
        });
        system.runTimeout(() => {
            floor.setDynamicProperty(DynamicPropertyEnum.FAKE_FLOOR_MOVE, true);
        }, 2 * TicksPerSecond);
    }
    static callLift() {
        const dimension = world.getDimension("minecraft:overworld");
        const lift = dimension.getEntities({ type: "thm:lift" })[0];
        if (!lift)
            return;
        dimension.playSound(SoundEnum.ELEVATOR_STOP, { x: 9, y: 48, z: 58 }, { volume: 2 });
        lift.setDynamicProperty(DynamicPropertyEnum.LIFT_DIRECTION, 0.001);
    }
    static openLift() {
        const barrierArea = new BlockVolume({ x: 8, y: 50, z: 55 }, { x: 8, y: 51, z: 62 });
        const dimension = world.getDimension("minecraft:overworld");
        const lift = dimension.getEntities({ type: "thm:lift" })[0];
        if (!lift)
            return;
        dimension.playSound(SoundEnum.ELEVATOR_STEAM, { x: 9, y: 50, z: 58 }, { volume: 2 });
        dimension.playSound(SoundEnum.ELEVATOR_STOP, { x: 9, y: 50, z: 58 }, { volume: 2 });
        system.runTimeout(() => {
            const fillArea = new BlockVolume({ x: 9, y: 49, z: 62 }, { x: 13, y: 49, z: 55 });
            dimension.fillBlocks(fillArea, "minecraft:barrier");
            lift.setProperty(ActorPropertiesEnum.OPENED, true);
            dimension.playSound(SoundEnum.ELEVATOR_DOOR_OPEN, { x: 9, y: 50, z: 58 }, { volume: 2 });
        }, 2 * TicksPerSecond);
        system.runTimeout(() => {
            dimension.fillBlocks(barrierArea, "minecraft:air");
        }, 3 * TicksPerSecond);
    }
    static sendLift() {
        const dimension = world.getDimension("minecraft:overworld");
        const lift = dimension.getEntities({ type: "thm:lift" })[0];
        if (!lift)
            return;
        dimension.playSound(SoundEnum.ELEVATOR_STOP, { x: 9, y: 48, z: 58 }, { volume: 2 });
        dimension.playSound(SoundEnum.ELEVATOR_STEAM, { x: 9, y: 48, z: 58 }, { volume: 2 });
        lift.setDynamicProperty(DynamicPropertyEnum.LIFT_DIRECTION, -0.001);
        const fillArea = new BlockVolume({ x: 10, y: 49, z: 62 }, { x: 12, y: 49, z: 55 });
        dimension.fillBlocks(fillArea, "minecraft:air");
        const allPlayers = PlayerUtils.getAllPlayers();
        const lift_seats = dimension.getEntities({ type: "thm:lift_seats" })[0];
        for (const player of allPlayers) {
            const ridable = lift_seats.getComponent(EntityComponentTypes.Rideable);
            ridable.addRider(player);
            VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Movement, false);
        }
    }
    static finalSoundPos = { x: 11, y: 25, z: 59 };
    static finalLift() {
        //Jumbo Josh
        const dimension = world.getDimension("minecraft:overworld");
        const lift_seats = dimension.getEntities({ type: "thm:lift_seats" })[0];
        if (!lift_seats)
            return;
        const players = PlayerUtils.getAllPlayers();
        for (const player of players) {
            const ridable = lift_seats.getComponent(EntityComponentTypes.Rideable);
            ridable.ejectRider(player);
            VanillaCmdsUtils.cameraShake(player);
            VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Movement, true);
        }
        dimension.playSound(SoundEnum.ELEVATOR_ABRUPT_STOP, this.finalSoundPos, { volume: 2 });
        const josh = dimension.getEntities({ type: "thm:jumbo_josh" })[0];
        if (!josh)
            return;
        josh.setProperty(ActorPropertiesEnum.HIDDEN, false);
        system.runTimeout(() => {
            dimension.playSound(SoundEnum.JOSH, this.finalSoundPos, { volume: 2 });
        }, 6 * TicksPerSecond);
        let interval;
        const alarmTick = system.runTimeout(() => {
            interval = this.alarm();
        }, 12 * TicksPerSecond);
        system.runTimeout(() => {
            josh.setProperty(ActorPropertiesEnum.CLIMB, true);
        }, 14 * TicksPerSecond);
        system.runTimeout(() => {
            dimension.playSound(SoundEnum.ELEVATOR_SLAM, this.finalSoundPos, { volume: 2 });
            const players = PlayerUtils.getAllPlayers();
            for (const player of players) {
                VanillaCmdsUtils.cameraShake(player);
            }
        }, (14 + 2.5) * TicksPerSecond);
        system.runTimeout(() => {
            dimension.playSound(SoundEnum.ELEVATOR_SLAM, this.finalSoundPos, { volume: 2 });
            const players = PlayerUtils.getAllPlayers();
            for (const player of players) {
                VanillaCmdsUtils.cameraShake(player);
            }
        }, (14 + 4) * TicksPerSecond);
        system.runTimeout(() => {
            dimension.playSound(SoundEnum.ELEVATOR_SNAP, this.finalSoundPos, { volume: 2 });
            const players = PlayerUtils.getAllPlayers();
            for (const player of players) {
                VanillaCmdsUtils.cameraShake(player);
            }
        }, (14 + 7) * TicksPerSecond);
        system.runTimeout(() => {
            const lift = dimension.getEntities({ type: "thm:lift" })[0];
            if (!lift)
                return;
            lift.setProperty(ActorPropertiesEnum.FALL, true);
        }, (14 + 7.5) * TicksPerSecond);
        system.runTimeout(() => {
            josh.remove();
            system.clearRun(interval);
            system.clearRun(alarmTick);
            const players = PlayerUtils.getAllPlayers();
            for (const player of players) {
                let camera = player.camera;
                camera.fade({ fadeTime: { fadeInTime: 0.5, fadeOutTime: 0.5, holdTime: 2 }, fadeColor: { red: 0, green: 0, blue: 0 } });
            }
        }, (14 + 8) * TicksPerSecond);
        system.runTimeout(() => {
            const players = PlayerUtils.getAllPlayers();
            for (const player of players) {
                PlayerController.afterGame(player);
            }
            world.setDynamicProperty(DynamicPropertyEnum.GAMEEND, true);
        }, (14 + 9) * TicksPerSecond);
    }
    static alarm() {
        const dimension = world.getDimension("minecraft:overworld");
        const lift = dimension.getEntities({ type: "thm:lift" })[0];
        const josh = dimension.getEntities({ type: "thm:jumbo_josh" })[0];
        const soundTime = Math.floor(0.769 * TicksPerSecond);
        if (!josh || !lift)
            return 0;
        const lightVolume = new BlockVolume({ x: 11, y: lift.location.y + 3, z: 58 }, { x: 11, y: lift.location.y + 3, z: 59 });
        const interval = system.runInterval(() => {
            if (lift !== undefined) {
                try {
                    lift.setProperty(ActorPropertiesEnum.RED, true);
                }
                catch (error) { }
            }
            if (josh !== undefined) {
                try {
                    josh.setProperty(ActorPropertiesEnum.RED, true);
                }
                catch (error) { }
            }
            dimension.playSound(SoundEnum.ELEVATOR_EMERGENCY, this.finalSoundPos, { volume: 2 });
            dimension.fillBlocks(lightVolume, "minecraft:light_block_10");
            system.runTimeout(() => {
                dimension.fillBlocks(lightVolume, "minecraft:light_block_15");
                if (lift !== undefined) {
                    try {
                        lift.setProperty(ActorPropertiesEnum.RED, false);
                    }
                    catch (error) { }
                }
                if (josh !== undefined) {
                    try {
                        josh.setProperty(ActorPropertiesEnum.RED, false);
                    }
                    catch (error) {
                    }
                }
            }, soundTime / 2);
        }, soundTime);
        return interval;
    }
    static openDoorFromKeycard(keycardColour) {
        let barrierVolume;
        let colour = 0;
        switch (keycardColour) {
            case KeycardColourEnum.BLUE:
                barrierVolume = new BlockVolume({ x: 13, y: 51, z: 8 }, { x: 13, y: 52, z: 9 });
                colour = 1;
                break;
            case KeycardColourEnum.YELLOW:
                barrierVolume = new BlockVolume({ x: 4, y: 51, z: 12 }, { x: 3, y: 52, z: 12 });
                colour = 2;
                break;
            case KeycardColourEnum.ORANGE:
                barrierVolume = new BlockVolume({ x: 4, y: 51, z: 54 }, { x: 3, y: 52, z: 54 });
                colour = 4;
                break;
            case KeycardColourEnum.PURPLE:
                barrierVolume = new BlockVolume({ x: 6, y: 51, z: 46 }, { x: 6, y: 52, z: 47 });
                colour = 3;
                ProgressUtils.addProgress(5);
                break;
        }
        const dimension = world.getDimension("minecraft:overworld");
        const doors = dimension.getEntities({
            type: "thm:door",
            propertyOptions: [{
                    propertyId: ActorPropertiesEnum.COLOR,
                    value: { equals: colour }
                }]
        });
        for (const door of doors) {
            door.setProperty(ActorPropertiesEnum.DOOR_SLIDE, true);
        }
        dimension.playSound(SoundEnum.DOOR_SLIDE, doors[0].location, { volume: 2 });
        system.runTimeout(() => {
            BlockUtils.clearBarrier(dimension, barrierVolume);
        }, 40);
    }
    static saveAllPlayersPosition() {
        const players = PlayerUtils.getAllPlayers();
        for (const player of players) {
            this.savePositionForRespawn(player);
        }
    }
    static savePositionForRespawn(player) {
        const location = player.location;
        const rotation = player.getRotation();
        player.setDynamicProperty(DynamicPropertyEnum.RESPAWN_LOCATION, location);
        player.setDynamicProperty(DynamicPropertyEnum.RESPAWN_ROTATION, {
            x: rotation.x,
            y: rotation.y,
            z: 0,
        });
        player.setDynamicProperty(DynamicPropertyEnum.RESPAWN_FACTING, player.getViewDirection());
    }
    static respawnAllPlayers() {
        const players = PlayerUtils.getAllPlayers();
        for (const player of players) {
            this.respawn(player);
        }
    }
    static respawn(player) {
        let camera = player.camera;
        camera.fade({ fadeTime: { fadeInTime: 0.5, fadeOutTime: 0.5, holdTime: 2 }, fadeColor: { red: 0, green: 0, blue: 0 } });
        system.runTimeout(() => {
            let respawnLocation = player.getDynamicProperty(DynamicPropertyEnum.RESPAWN_LOCATION);
            let respawnRotation = player.getDynamicProperty(DynamicPropertyEnum.RESPAWN_ROTATION);
            let respawnFacing = player.getDynamicProperty(DynamicPropertyEnum.RESPAWN_FACTING);
            if (!respawnLocation) {
                player.teleport({ x: 4.00, y: 50, z: -2.50 }, { rotation: { x: 0, y: 0 }, facingLocation: { x: 4.00, y: 51, z: 10 } });
            }
            else {
                respawnRotation = respawnRotation || { x: 0, y: 0, z: 0 };
                respawnFacing = respawnFacing || { x: 0, y: 0, z: 0 };
                player.teleport(respawnLocation, { rotation: respawnRotation, facingLocation: VectorUtils.add(respawnLocation, respawnFacing) });
            }
            VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Camera, true);
            VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Movement, true);
        }, 10);
    }
    static turnOnLights() {
        const lightBlocks = [
            [
                { x: -5, y: 51, z: 39 },
                { x: -5, y: 50, z: 31 },
                { x: -5, y: 50, z: 23 },
                { x: -5, y: 58, z: 23 },
                { x: -5, y: 58, z: 31 },
                { x: -5, y: 58, z: 39 },
            ],
            [
                { x: -11, y: 50, z: 39 },
                { x: -11, y: 51, z: 31 },
                { x: -11, y: 51, z: 23 },
                { x: -11, y: 58, z: 23 },
                { x: -11, y: 58, z: 31 },
                { x: -11, y: 58, z: 39 },
            ],
            [
                { x: -17, y: 51, z: 39 },
                { x: -17, y: 50, z: 31 },
                { x: -17, y: 50, z: 23 },
                { x: -17, y: 58, z: 23 },
                { x: -17, y: 58, z: 31 },
                { x: -17, y: 58, z: 39 },
            ],
            [
                { x: -23, y: 50, z: 39 },
                { x: -23, y: 50, z: 31 },
                { x: -23, y: 50, z: 23 },
                { x: -23, y: 58, z: 23 },
                { x: -23, y: 58, z: 31 },
                { x: -23, y: 58, z: 39 },
            ],
            [
                { x: -29, y: 50, z: 39 },
                { x: -29, y: 52, z: 32 },
                { x: -30, y: 54, z: 23 },
                { x: -29, y: 58, z: 23 },
                { x: -29, y: 58, z: 31 },
                { x: -29, y: 58, z: 39 },
            ],
            [
                { x: -35, y: 51, z: 40 },
                { x: -39, y: 51, z: 44 },
                { x: -35, y: 50, z: 31 },
                { x: -35, y: 50, z: 23 },
                { x: -35, y: 58, z: 23 },
                { x: -35, y: 58, z: 31 },
                { x: -35, y: 58, z: 39 },
            ],
        ];
        const darknessLocs = [
            { x: -8.5, y: 50, z: 31.5 },
            { x: -14.5, y: 50, z: 31.5 },
            { x: -20.5, y: 50, z: 31.5 },
            { x: -25.5, y: 50, z: 31.5 },
            { x: -32.5, y: 50, z: 31.5 },
        ];
        const dimension = world.getDimension("minecraft:overworld");
        let step = 0;
        const darknessEntity = dimension.getEntities({ type: "thm:darkness" })[0];
        if (!darknessEntity)
            return;
        dimension.playSound(SoundEnum.CONFUSION_MAIN, { x: 1, y: 50, z: 36 }, { volume: 2 });
        const timer = system.runInterval(() => {
            let lightgroup = lightBlocks[step];
            for (const light of lightgroup) {
                const block = dimension.getBlock(light);
                const lightLevel = light.y === 58 ? 12 : 15;
                block?.setPermutation(BlockPermutation.resolve(`minecraft:light_block_${lightLevel}`));
                dimension.playSound(SoundEnum.SPOTLIGHT, { x: 1, y: 50, z: 36 }, { volume: 2 });
                if (step < 5)
                    darknessEntity.teleport(darknessLocs[step], { dimension: darknessEntity.dimension });
            }
            let lightStep = 6 * step;
            const firstLight = dimension.getBlock({ x: -5 - lightStep, y: 59, z: 39 });
            const secondLight = dimension.getBlock({ x: -5 - lightStep, y: 59, z: 31 });
            const thirdLight = dimension.getBlock({ x: -5 - lightStep, y: 59, z: 23 });
            if (firstLight)
                firstLight.setPermutation(firstLight.permutation.withState("thm:on", true));
            if (secondLight)
                secondLight.setPermutation(secondLight.permutation.withState("thm:on", true));
            if (thirdLight)
                thirdLight.setPermutation(thirdLight.permutation.withState("thm:on", true));
            step++;
            if (step >= 6) {
                dimension.playSound(SoundEnum.SPOTLIGHT, { x: 1, y: 50, z: 36 }, { volume: 2 });
                const barrierVolume = new BlockVolume({ x: 0, y: 50, z: 35 }, { x: 0, y: 53, z: 37 });
                const concreteVolume = new BlockVolume({ x: -36, y: 52, z: 48 }, { x: -37, y: 50, z: 48 });
                const carpetVolume = new BlockVolume({ x: -36, y: 50, z: 48 }, { x: -37, y: 50, z: 48 });
                BlockUtils.clearBarrier(dimension, barrierVolume);
                BlockUtils.clearBarrier(dimension, concreteVolume);
                dimension.fillBlocks(carpetVolume, "minecraft:purple_carpet");
                darknessEntity.remove();
                system.clearRun(timer);
            }
        }, 20);
    }
}

class JumpScareController {
    static inJumpScare = false;
    static trigger(players) {
        // Prep Opila            
        let opilaEntity = world.getDimension('overworld').getEntities({ type: 'thm:opila_alive' })[0];
        if (!opilaEntity)
            return;
        const riding = opilaEntity.getComponent(EntityComponentTypes.Riding);
        if (riding !== undefined)
            return;
        opilaEntity.setProperty(ActorPropertiesEnum.JUMPSCARE, true);
        opilaEntity.triggerEvent('thm:stop_walking');
        opilaEntity.setProperty(ActorPropertiesEnum.WALKING, false);
        world.setDynamicProperty(DynamicPropertyEnum.OPILA_TICK, false);
        let opilaTarget = world.getDimension('overworld').getEntities({ type: 'thm:opila_target' })[0];
        if (opilaTarget) {
            opilaTarget.remove();
        }
        if (this.inJumpScare)
            return;
        for (const player of players) {
            player.runCommand(`stopsound @s ${SoundEnum.CHASE}`);
            const jumpascare = player.getDynamicProperty(DynamicPropertyEnum.JUMPSCARE) ?? false;
            if (jumpascare)
                return;
            this.showJumpScare(player, opilaEntity);
        }
        this.inJumpScare = true;
        system.runTimeout(() => {
            OpilaController.failed();
            this.inJumpScare = false;
        }, 50);
    }
    static showJumpScare(player, opilaEntity) {
        if (PlayerUtils.isInMenu(player))
            return;
        // Camera movement
        const opilaLocation = opilaEntity.location;
        const opilaViewDirection = opilaEntity.getViewDirection();
        player.setDynamicProperty(DynamicPropertyEnum.JUMPSCARE, true);
        const distanceForward = 3;
        const distanceUp = 2;
        const length = Math.sqrt(opilaViewDirection.x ** 2 + opilaViewDirection.z ** 2);
        const forwardX = length === 0 ? 0 : opilaViewDirection.x / length;
        const forwardZ = length === 0 ? 0 : opilaViewDirection.z / length;
        const firstCameraLocation = {
            x: opilaLocation.x + forwardX * distanceForward,
            y: opilaLocation.y + distanceUp,
            z: opilaLocation.z + forwardZ * distanceForward
        };
        const secondeCameraLocation = { x: opilaLocation.x, y: opilaLocation.y + distanceUp, z: opilaLocation.z };
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Camera, false);
        VanillaCmdsUtils.setInputPermissions(player, InputPermissionCategory.Movement, false);
        let camera = player.camera;
        //camera.setCamera("minecraft:free", {location:firstCameraLocation, facingEntity:opilaEntity});
        player.runCommand(`camera @s set minecraft:free pos ${firstCameraLocation.x} ${firstCameraLocation.y} ${firstCameraLocation.z} facing ${opilaLocation.x} ${opilaLocation.y + 2} ${opilaLocation.z} `);
        player.addEffect("invisibility", 30, { showParticles: false });
        system.runTimeout(() => {
            // Sounds and camera shake
            player.playSound(SoundEnum.JUMPSCARE_SCARE);
            player.playSound(SoundEnum.JUMPSCARE_AMBIENCE);
            VanillaCmdsUtils.cameraShake(player, 0.01, 1, CameraShakeTypeEnum.POSITIONAL);
        }, 1);
        system.runTimeout(() => {
            camera.setCamera("minecraft:free", { easeOptions: { easeTime: 5, easeType: EasingType.Linear }, location: secondeCameraLocation });
        }, 10);
        // Reset camera and respawn player
        system.runTimeout(() => {
            player.runCommand(`stopsound @s ${SoundEnum.JUMPSCARE_SCARE}`);
            player.runCommand(`stopsound @s ${SoundEnum.JUMPSCARE_AMBIENCE}`);
            GameController.respawn(player);
            player.setDynamicProperty(DynamicPropertyEnum.JUMPSCARE, false);
            system.runTimeout(() => {
                PlayerUtils.resetCamera(player);
            }, 20);
        }, 30);
    }
}

var OpilaAIType;
(function (OpilaAIType) {
    OpilaAIType[OpilaAIType["static"] = 0] = "static";
    OpilaAIType[OpilaAIType["creep"] = 1] = "creep";
    OpilaAIType[OpilaAIType["chase"] = 2] = "chase";
})(OpilaAIType || (OpilaAIType = {}));
var ChaseDirection;
(function (ChaseDirection) {
    ChaseDirection[ChaseDirection["north"] = 0] = "north";
    ChaseDirection[ChaseDirection["east"] = 1] = "east";
    ChaseDirection[ChaseDirection["south"] = 2] = "south";
    ChaseDirection[ChaseDirection["west"] = 3] = "west";
})(ChaseDirection || (ChaseDirection = {}));
const creepMovement = 0.15;
const chaseSteps = [
    { location: { x: -4, y: 47, z: 64 }, delay: 3 * TicksPerSecond },
    { location: { x: -1, y: 47, z: 64 }, delay: 1.5 * TicksPerSecond },
    { location: { x: -1, y: 47, z: 71 }, delay: 1.5 * TicksPerSecond },
    { location: { x: -4, y: 47, z: 75 }, delay: 1.5 * TicksPerSecond },
    { location: { x: -9, y: 49, z: 75 }, delay: 1.5 * TicksPerSecond },
];
class OpilaController {
    static AI_TYPE = OpilaAIType.static;
    static TICK_ID;
    static getOpila() {
        const dimension = world.getDimension("minecraft:overworld");
        return dimension.getEntities({ type: "thm:opila_alive" })[0];
    }
    static initOpila() {
        //Triggered from picking up orange keycard
        //Logic
        //Spawn opillo on chairlift
        //Spawn tape recorder in cafe
        const dimension = world.getDimension("minecraft:overworld");
        const tapeSpawned = world.getDynamicProperty(DynamicPropertyEnum.TAPE_SPAWNED) ?? false;
        //Stuff to do only once
        if (!tapeSpawned) {
            dimension.spawnEntity("thm:for_you", { x: -12.5, y: 51, z: 2.85 });
            dimension.spawnEntity("thm:tape_recorder", { x: -12.5, y: 51, z: 5.8 });
            dimension.spawnEntity("thm:tape_pickup", { x: -11.5, y: 51, z: 5.8 });
            const sittingOpila = world.getDimension("minecraft:overworld").getEntities({ type: "thm:opila" })[0];
            sittingOpila?.remove();
            const hiddenEgg = world.getDimension("minecraft:overworld").getEntities({ type: "thm:egg" })[0];
            hiddenEgg?.remove();
            world.setDynamicProperty(DynamicPropertyEnum.TAPE_SPAWNED, true);
        }
        world.setDynamicProperty(DynamicPropertyEnum.IN_CHASE, true);
        ChairliftController.hardReset();
        const insideArea = new BlockVolume({ x: 0, y: 46, z: 76 }, { x: -6, y: 53, z: 49 });
        const outOfAreaPlayers = PlayerUtils.getAllPlayers().filter(p => !insideArea.isInside(p.location));
        for (const player of outOfAreaPlayers) {
            player.teleport({ x: -2, y: 47, z: 73 });
        }
        system.runTimeout(() => {
            GameController.saveAllPlayersPosition();
            const chairlift = dimension.getEntities({ type: "thm:chairlift", location: { x: -32, y: 47, z: 72 }, maxDistance: 5 })[0];
            if (!chairlift)
                throw new Error("Chairlift not found");
            const newOpila = dimension.spawnEntity("thm:opila_alive", { x: -38, y: 57, z: 71 });
            const ridable = chairlift.getComponent(EntityComponentTypes.Rideable);
            if (!ridable)
                throw new Error("Rideable component not found");
            ridable.addRider(newOpila);
            //dimension.runCommand('ride @e[type=thm:opila_alive] start_riding @e[type=thm:chairlift, x=-32, y=47, z=72, r=5]');
            ChairliftController.startChairlifts();
            SoundUtils.globalSound(SoundEnum.CONFUSION_OPILA);
        }, TicksPerSecond);
    }
    static failed() {
        const opila = this.getOpila();
        const dimension = world.getDimension("minecraft:overworld");
        if (!opila)
            throw new Error("Opila not found");
        dimension.runCommand("summon thm:opila_alive -37 57 73 -90");
        opila.remove();
        const wallBlocks = new BlockVolume({ x: -12, y: 48, z: 77 }, { x: -30, y: 48, z: 77 });
        dimension.fillBlocks(wallBlocks, "minecraft:diorite_wall");
        const floorArea = new BlockVolume({ x: -12, y: 48, z: 56 }, { x: -30, y: 48, z: 76 });
        dimension.fillBlocks(floorArea, "minecraft:air");
        const button = dimension.getEntities({ type: "thm:button", tags: ["ballpit"] })[0];
        button.setProperty(ActorPropertiesEnum.LOCKED, true);
        button.setProperty(ActorPropertiesEnum.USED, false);
        const drone = DroneUtils.getDrone();
        const droneTargets = dimension.getEntities({ type: "thm:drone_target" });
        droneTargets.forEach(t => t.remove());
        DroneUtils.setIsInUse(drone, false);
        drone.teleport({ x: -3, y: 48, z: 67 });
        system.runTimeout(() => {
            this.initOpila();
        }, 2 * TicksPerSecond);
    }
    static startCreep() {
        const opila = this.getOpila();
        if (!opila)
            throw new Error("Opila not found");
        this.setAIType(opila, OpilaAIType.creep);
        opila?.setDynamicProperty(DynamicPropertyEnum.OPILA_TARGET, chaseSteps[0].location);
        world.setDynamicProperty(DynamicPropertyEnum.OPILA_TICK, true);
        opila?.setProperty(ActorPropertiesEnum.WALKING, true);
        this.backCheck(opila.location);
    }
    static backCheckInterval;
    static backCheck(location) {
        if (!location)
            return;
        const interval = system.runInterval(() => {
            const flooredZ = Math.floor(location.z);
            const blockVolume = new BlockVolume({ x: 0, y: 46, z: flooredZ >= 74 ? 77 : flooredZ + 1 }, { x: -8, y: 52, z: flooredZ - 5 });
            const players = PlayerUtils.getAllPlayers().filter(p => blockVolume.isInside(p.location));
            if (players.length > 0) {
                JumpScareController.trigger(PlayerUtils.getAllPlayers());
                system.clearRun(interval);
            }
        }, 1);
        this.backCheckInterval = interval;
    }
    static startChase() {
        const opila = this.getOpila();
        const dimension = world.getDimension("minecraft:overworld");
        if (!opila)
            throw new Error("Opila not found");
        this.setAIType(opila, OpilaAIType.chase);
        NotificationUtils.chaseNotification();
        //world.setDynamicProperty(DynamicPropertyEnum.OPILA_TICK, true);
        //this.setChaseDirection(opila, ChaseDirection.north);
        opila?.setProperty(ActorPropertiesEnum.WALKING, false);
        opila?.setProperty(ActorPropertiesEnum.ALIVE, true);
        this.setChasingPlayer(opila, true);
        opila?.triggerEvent("thm:start_walking");
        dimension.spawnEntity("thm:opila_target", { x: -37, y: 48, z: 75 });
        const wallBlocks = new BlockVolume({ x: -12, y: 48, z: 77 }, { x: -30, y: 48, z: 77 });
        dimension.fillBlocks(wallBlocks, "minecraft:brick_wall");
        world.structureManager.place("platform", dimension, { x: -30, y: 48, z: 56 });
        SoundUtils.globalSound(SoundEnum.CHASE);
    }
    static stop() {
        if (!this.AI_TYPE)
            return;
        world.setDynamicProperty(DynamicPropertyEnum.OPILA_COMPLETED, true);
        system.clearRun(this.TICK_ID);
        world.setDynamicProperty(DynamicPropertyEnum.OPILA_TICK, false);
        world.setDynamicProperty(DynamicPropertyEnum.IN_CHASE, false);
    }
    static tick(tickID) {
        const completed = world.getDynamicProperty(DynamicPropertyEnum.OPILA_COMPLETED) ?? false;
        if (completed) {
            system.clearRun(tickID);
            return;
        }
        const opila = this.getOpila();
        if (!opila)
            return;
        if (this.TICK_ID === undefined)
            this.TICK_ID = tickID;
        if (!this.AI_TYPE)
            this.AI_TYPE = this.getAIType(opila);
        if (this.AI_TYPE === OpilaAIType.static)
            return;
        if (this.AI_TYPE === OpilaAIType.creep) {
            let target = opila.getDynamicProperty(DynamicPropertyEnum.OPILA_TARGET);
            if (!target) {
                this.setAIType(opila, OpilaAIType.static);
                return;
            }
            const index = chaseSteps.findIndex(s => VectorUtils.equals(s.location, target));
            let delay = opila.getDynamicProperty(DynamicPropertyEnum.OPILA_DELAY) ?? 0;
            const threshold = 0.5;
            if (Math.abs(opila.location.x - target.x) <= threshold &&
                Math.abs(opila.location.y - target.y) <= threshold &&
                Math.abs(opila.location.z - target.z) <= threshold) {
                // Reached target
                opila?.setProperty(ActorPropertiesEnum.WALKING, false);
                if (index === chaseSteps.length - 1 && delay === 0) {
                    // Finished creep
                    world.setDynamicProperty(DynamicPropertyEnum.OPILA_TICK, false);
                    return;
                }
                else {
                    delay = chaseSteps[index + 1].delay;
                    opila.setDynamicProperty(DynamicPropertyEnum.OPILA_DELAY, delay);
                    opila.setDynamicProperty(DynamicPropertyEnum.OPILA_TARGET, chaseSteps[index + 1].location);
                    return;
                }
            }
            if (delay > 0) {
                opila.setDynamicProperty(DynamicPropertyEnum.OPILA_DELAY, delay - 1);
                return;
            }
            opila?.setProperty(ActorPropertiesEnum.WALKING, true);
            const location = opila.location;
            let direction = VectorUtils.subtract(target, location);
            // Ensure we don't overshoot the target
            if (Math.abs(direction.x) <= creepMovement)
                direction.x = 0;
            else if (direction.x > 0)
                direction.x = creepMovement;
            else if (direction.x < 0)
                direction.x = -creepMovement;
            if (Math.abs(direction.y) <= creepMovement)
                direction.y = 0;
            else if (direction.y > 0)
                direction.y = creepMovement;
            else if (direction.y < 0)
                direction.y = -creepMovement;
            if (Math.abs(direction.z) <= creepMovement)
                direction.z = 0;
            else if (direction.z > 0)
                direction.z = creepMovement;
            else if (direction.z < 0)
                direction.z = -creepMovement;
            // Teleport to the new position
            opila.teleport(VectorUtils.add(location, direction), { facingLocation: VectorUtils.add(target, { x: 0, y: 1, z: 0 }) });
        }
    }
    static chasePlatform(tickID) {
        const completed = world.getDynamicProperty(DynamicPropertyEnum.OPILA_COMPLETED) ?? false;
        if (completed) {
            system.clearRun(tickID);
            return;
        }
        if (this.AI_TYPE !== OpilaAIType.chase)
            return;
        let step = world.getDynamicProperty(DynamicPropertyEnum.PLATFORM_STEP) ?? 77;
        if (step === 56)
            return;
        const structureManager = world.structureManager;
        const dimension = world.getDimension("minecraft:overworld");
        step--;
        structureManager.place("platform2", dimension, { x: -30, y: 48, z: step });
        world.setDynamicProperty(DynamicPropertyEnum.PLATFORM_STEP, step);
        //world.sendMessage(`Step: ${step}`);
        if (step === 55) {
            world.setDynamicProperty(DynamicPropertyEnum.OPILA_TICK, false);
        }
    }
    static calculateYaw(from, to) {
        // Calculate the direction vector
        const direction = VectorUtils.subtract(to, from);
        // Calculate yaw (rotation around the Y-axis)
        const yaw = Math.atan2(direction.z, direction.x);
        // Convert to degrees (optional, if your rotation system uses degrees)
        return yaw * (180 / Math.PI); // Convert to degrees
    }
    static completeOpillo() {
        const dimension = world.getDimension("minecraft:overworld");
        const playersOnPlatform = dimension.getPlayers({ location: { x: -32, y: 44, z: 49 }, volume: { x: 35, y: 10, z: 26 } });
        world.setDynamicProperty(DynamicPropertyEnum.OPILA_TICK, false);
        world.setDynamicProperty(DynamicPropertyEnum.CHASE_COMPLETE, true);
        world.setDynamicProperty(DynamicPropertyEnum.IN_CHASE, false);
        for (const player of playersOnPlatform) {
            player.teleport({ x: -37, y: 47, z: 73 });
            player.runCommand(`stopsound @s ${SoundEnum.CHASE}`);
        }
        if (this.backCheckInterval)
            system.clearRun(this.backCheckInterval);
        SoundUtils.localSound({ x: -18, y: 50, z: 74 }, SoundEnum.OPILA_FALL);
        const plaformArea = new BlockVolume({ x: -30, y: 48, z: 76 }, { x: -12, y: 48, z: 56 });
        dimension.fillBlocks(plaformArea, "minecraft:air");
        const wallArea = new BlockVolume({ x: -12, y: 48, z: 77 }, { x: -31, y: 48, z: 77 });
        dimension.fillBlocks(wallArea, "minecraft:diorite_wall");
        const chairlifts = dimension.getEntities({ type: "thm:chairlift" });
        chairlifts.forEach((entity) => {
            entity.triggerEvent("thm:up");
        });
        const opila = this.getOpila();
        system.runTimeout(() => {
            opila?.remove();
        }, 2 * TicksPerSecond);
    }
    static getAIType(entity) {
        this.AI_TYPE = (entity.getDynamicProperty(DynamicPropertyEnum.OPILA_AI_TYPE) ?? 0);
        return this.AI_TYPE;
    }
    static setAIType(entity, type) {
        entity.setDynamicProperty(DynamicPropertyEnum.OPILA_AI_TYPE, type);
        this.AI_TYPE = type;
    }
    static setChaseDirection(entity, direction) {
        entity.setDynamicProperty(DynamicPropertyEnum.OPILA_CHASE_DIRECTION, direction);
    }
    static getChaseDirection(entity) {
        return (entity.getDynamicProperty(DynamicPropertyEnum.OPILA_CHASE_DIRECTION) ?? 0);
    }
    static setChasingPlayer(entity, value) {
        entity.setDynamicProperty(DynamicPropertyEnum.OPILA_CHASING_PLAYER, value);
    }
    static isChasingPlayer(entity) {
        return entity.getDynamicProperty(DynamicPropertyEnum.OPILA_CHASING_PLAYER) ?? false;
    }
}

let RUNNING = false;
let COMPLETED = 0;
const SPEED = 0.05;
class ChairliftController {
    static hardReset() {
        const dimension = world.getDimension('overworld');
        const chairlift = dimension.getEntities({ type: "thm:chairlift", location: { x: -32, y: 47, z: 72 }, maxDistance: 5 })[0];
        if (chairlift)
            return;
        this.setRunning(false);
        this.setCompleted(0);
        world.setDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_MOVING, false);
        const chairlifts = world
            .getDimension('overworld')
            .getEntities({ type: 'thm:chairlift' });
        chairlifts[0].teleport({ x: -8.5, y: chairlifts[0].location.y, z: 50.5 });
        chairlifts[1].teleport({ x: -32.5, y: chairlifts[1].location.y, z: 72.5 });
    }
    ;
    static startChairlifts() {
        const moving = world.getDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_MOVING) ?? false;
        if (moving)
            return;
        this.setRunning(true);
        this.setCompleted(0);
        const chairlifts = world
            .getDimension('overworld')
            .getEntities({ type: 'thm:chairlift' });
        for (const chairlift of chairlifts) {
            this.setMoving(chairlift, true);
        }
    }
    static getRunning() {
        return RUNNING;
    }
    static setRunning(value) {
        world.setDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_RUNNING, value);
        RUNNING = value;
    }
    static setCompleted(value) {
        COMPLETED = value;
    }
    static ready(entity) {
        entity.setProperty(ActorPropertiesEnum.CHAIRLIFT_MOVING, true);
        world.setDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_MOVING, true);
    }
    static setMoving(entity, value) {
        entity.setProperty(ActorPropertiesEnum.CHAIRLIFT_MOVING, value);
    }
    static run(entity) {
        const moving = entity.getProperty(ActorPropertiesEnum.CHAIRLIFT_MOVING);
        if (!moving)
            return;
        const forward = entity.getProperty(ActorPropertiesEnum.CHAIRLIFT_FORWARD);
        const direction = entity.getProperty(ActorPropertiesEnum.CHAIRLIFT_DIRECTION);
        const block = entity.dimension.getBlock({ x: entity.location.x, y: 57, z: entity.location.z });
        if (!block)
            return;
        const started = getStarted(block, forward);
        if (started) {
            entity.dimension.playSound(SoundEnum.ELEVATOR_STEAM, entity.location);
        }
        move(entity, direction);
        const changedDirection = entity.getDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_CHANGED_DIRECTION) ?? false;
        if (block.permutation.matches("minecraft:iron_block") && !changedDirection) {
            entity.dimension.playSound(SoundEnum.POLE_DIRECTION_CHANGE, entity.location);
            entity.teleport({ x: Math.floor(block.location.x) + 0.5, y: entity.location.y, z: Math.floor(block.location.z) + 0.5 });
            const newDirection = changeDirection(entity, direction);
            entity.setProperty(ActorPropertiesEnum.CHAIRLIFT_DIRECTION, newDirection);
            entity.setDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_CHANGED_DIRECTION, true);
        }
        if (block.permutation.matches("minecraft:emerald_block") && !started) {
            entity.dimension.playSound(SoundEnum.ELEVATOR_STEAM, entity.location);
            this.setMoving(entity, false);
            entity.teleport({ x: Math.floor(block.location.x) + 0.5, y: entity.location.y, z: Math.floor(block.location.z) + 0.5 });
            entity.setProperty(ActorPropertiesEnum.CHAIRLIFT_FORWARD, !forward);
            const newDirection = changeDirection(entity, direction);
            entity.setProperty(ActorPropertiesEnum.CHAIRLIFT_DIRECTION, newDirection);
            entity.setDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_CHANGED_DIRECTION, false);
            COMPLETED++;
            const riderComponent = entity.getComponent(EntityComponentTypes.Rideable);
            riderComponent.getRiders().forEach(rider => {
                if (rider.typeId === "thm:opila_alive") {
                    riderComponent.ejectRider(rider);
                    const button = rider.dimension.getEntities({ type: "thm:button", tags: ["ballpit"] })[0];
                    button.setProperty(ActorPropertiesEnum.LOCKED, false);
                    OpilaController.startCreep();
                }
            });
        }
        if (COMPLETED >= 2) {
            this.setRunning(false);
            world.setDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_MOVING, false);
        }
    }
}
function getStarted(block, forward) {
    let started = false;
    if (forward && (block.location.x == -32 && block.location.z == 72)) {
        started = true;
    }
    else if (!forward && (block.location.x == -8 && block.location.z == 50)) {
        started = true;
    }
    return started;
}
function move(entity, direction) {
    switch (direction) {
        case "north":
            entity.applyImpulse({ x: 0, y: 0, z: -SPEED });
            break;
        case "south":
            entity.applyImpulse({ x: 0, y: 0, z: SPEED });
            break;
        case "west":
            entity.applyImpulse({ x: -SPEED, y: 0, z: 0 });
            break;
        case "east":
            entity.applyImpulse({ x: SPEED, y: 0, z: 0 });
            break;
    }
}
function changeDirection(entity, direction) {
    switch (direction) {
        case "east":
            return "north";
        case "west":
            return "south";
        case "north":
            return "west";
        case "south":
            return "east";
        default:
            throw new Error("Invalid direction");
    }
}

class OutlineController {
    static lookedAtEntities = new Map();
    static run(player) {
        const hitTest = player.getEntitiesFromViewDirection({ maxDistance: 5, families: ["outline"] });
        if (!hitTest || hitTest.length == 0)
            return;
        const entity = hitTest[0].entity;
        if (!entity)
            return;
        const currentJob = this.lookedAtEntities.get(entity.id);
        if (currentJob) {
            system.clearRun(currentJob);
            this.lookedAtEntities.delete(entity.id);
        }
        let doOutline = true;
        let text = [{ translate: "text.pickup" }];
        if (entity.typeId === "thm:chairlift") {
            const ready = entity.getProperty(ActorPropertiesEnum.CHAIRLIFT_READY) || false;
            if (!ready)
                text = [];
            if (ready)
                text = [{ translate: "text.chairlift" }];
            doOutline = false;
        }
        if (entity.typeId === "thm:menu_entity")
            text = [{ translate: "text.menu" }];
        if (entity.typeId === "thm:colour_light") {
            const colour = entity.getProperty(ActorPropertiesEnum.COLOR) || 0;
            text = [
                { translate: "text.colour_light" },
                { text: "\n" },
                { translate: "text.current_colour" },
                { text: " " },
            ];
            const locked = entity.getProperty(ActorPropertiesEnum.LOCKED) || false;
            if (locked) {
                text = [];
                doOutline = false;
            }
            if (!locked) {
                switch (colour) {
                    case 0:
                        text.push({ translate: "colour.red" });
                        break;
                    case 1:
                        text.push({ translate: "colour.yellow" });
                        break;
                    case 2:
                        text.push({ translate: "colour.pink" });
                        break;
                    case 3:
                        text.push({ translate: "colour.blue" });
                        break;
                    case 4:
                        text.push({ translate: "colour.green" });
                        break;
                    case 5:
                        text.push({ translate: "colour.orange" });
                        break;
                    case 6:
                        text.push({ translate: "colour.white" });
                        break;
                }
            }
        }
        player.setPropertyOverrideForEntity(entity, ActorPropertiesEnum.OUTLINE, doOutline);
        //entity.setProperty(ActorPropertiesEnum.OUTLINE, doOutline);
        if (text.length > 0)
            player.onScreenDisplay.setActionBar(text);
        const job = system.runTimeout(() => {
            if (entity !== undefined && player !== undefined) {
                try {
                    player.setPropertyOverrideForEntity(entity, ActorPropertiesEnum.OUTLINE, false);
                    //entity.setProperty(ActorPropertiesEnum.OUTLINE, false);
                }
                catch (error) {
                }
                this.lookedAtEntities.delete(entity.id);
            }
        }, 5);
        this.lookedAtEntities.set(entity.id, job);
    }
    static removeEntity(entity) {
        const currentJob = this.lookedAtEntities.get(entity.id);
        if (currentJob) {
            system.clearRun(currentJob);
            this.lookedAtEntities.delete(entity.id);
        }
    }
}

const KeycardLink = new Map([
    [
        "12,51,7", () => GameController.openDoorFromKeycard(KeycardColourEnum.BLUE)
    ],
    [
        "1,51,35", () => GameController.turnOnLights()
    ],
    [
        "-28,52,23", () => {
            const button = world.getDimension("minecraft:overworld").getEntities({ type: "thm:button", tags: ["playroom"] })[0];
            button.setProperty(ActorPropertiesEnum.LOCKED, false);
        }
    ],
    [
        "17,51,34", () => {
            const button = world.getDimension("minecraft:overworld").getEntities({ type: "thm:button", tags: ["classroom"] })[0];
            button.setProperty(ActorPropertiesEnum.LOCKED, false);
        }
    ],
    [
        "5,51,45", () => GameController.openDoorFromKeycard(KeycardColourEnum.PURPLE)
    ],
    [
        "5,51,53", () => GameController.openDoorFromKeycard(KeycardColourEnum.ORANGE)
    ],
    [
        "3,50,60", () => {
            const button = world.getDimension("minecraft:overworld").getEntities({ type: "thm:button", tags: ["office"] })[0];
            button.setProperty(ActorPropertiesEnum.LOCKED, false);
        }
    ],
    [
        "2,51,56", () => GameController.startLift()
    ],
    [
        "-38,51,49", () => {
            const dimension = world.getDimension("minecraft:overworld");
            const reader = dimension.getBlock({ x: -35, y: 48, z: 76 });
            reader?.setPermutation(reader.permutation.withState("thm:colour", "thm:keycard_yellow"));
            const chairlifts = dimension.getEntities({ type: "thm:chairlift" });
            chairlifts.forEach((entity) => {
                entity.triggerEvent("thm:down");
            });
            dimension.playSound(SoundEnum.CHAIRLIFT, { x: -33, y: 47, z: 72 }, { volume: 2 });
        }
    ],
    [
        "-35,48,76", () => {
            chairlifts({ x: -35, y: 48, z: 76 });
        }
    ],
    [
        "-5,48,49", () => {
            chairlifts({ x: -5, y: 48, z: 49 });
        }
    ],
    [
        "-37,48,76", () => OpilaController.completeOpillo()
    ]
]);
function chairlifts(location) {
    const dimension = world.getDimension("minecraft:overworld");
    const chairlifts = dimension.getEntities({ type: "thm:chairlift" });
    let chairLiftUp = false;
    chairlifts.forEach((entity) => {
        chairLiftUp = entity.getProperty(ActorPropertiesEnum.ANIM) ?? false;
    });
    const inChase = world.getDynamicProperty(DynamicPropertyEnum.IN_CHASE) ?? false;
    if (!inChase && chairLiftUp)
        ChairliftController.startChairlifts();
    const block = dimension.getBlock(location);
    system.runTimeout(() => {
        block?.setPermutation(block.permutation.withState("thm:open", false));
    }, TicksPerSecond);
}
/* Keycard links
0 - Blue key card reader - blue doors
1 -
*/

const ReaderComponent = {
    onUseOn(event) {
        const { block, source: player, itemStack } = event;
        const dimension = block.dimension;
        if (!block.hasTag("reader"))
            return;
        const readerColour = block.permutation.getState("thm:colour");
        if (readerColour === undefined)
            return;
        const open = block.permutation.getState("thm:open");
        const error = block.permutation.getState("thm:error");
        if (open || error)
            return;
        if (readerColour === itemStack.typeId) {
            const readerFunction = KeycardLink.get(`${block.location.x},${block.location.y},${block.location.z}`);
            if (readerFunction)
                readerFunction();
            block.setPermutation(block.permutation.withState("thm:open", true));
            dimension.playSound(SoundEnum.KEYCARD, block.location, { volume: 2 });
        }
        else {
            block.setPermutation(block.permutation.withState("thm:error", true));
            dimension.playSound(SoundEnum.ERROR, block.location, { volume: 2 });
            system.runTimeout(() => {
                block.setPermutation(block.permutation.withState("thm:error", false));
            }, 20);
        }
    }
};

system.beforeEvents.startup.subscribe(({ blockComponentRegistry, itemComponentRegistry }) => {
    itemComponentRegistry.registerCustomComponent('thm:reader', ReaderComponent);
});
world.afterEvents.worldLoad.subscribe(() => {
    const chairliftRunning = world.getDynamicProperty(DynamicPropertyEnum.CHAIRLIFT_RUNNING) ?? false;
    ChairliftController.setRunning(chairliftRunning);
});

class HitEntityController {
    static hitColourLight(hitEventData) {
        // How many correct colour is needed
        const CORRECT_COLOURS = 6;
        let correct_colours_so_far = 0;
        // Increment colour
        const entity = hitEventData.hitEntity;
        const locked = entity.getProperty(ActorPropertiesEnum.LOCKED);
        if (locked)
            return;
        const current_colour = entity.getProperty(ActorPropertiesEnum.COLOR);
        if (current_colour === undefined)
            return;
        const new_colour = (current_colour + 1) % 7;
        entity.setProperty(ActorPropertiesEnum.COLOR, new_colour);
        entity.dimension.playSound(SoundEnum.COLOUR_CLICK, entity.location, { volume: 2 });
        // Grab all colour lights
        const colour_lights = entity.dimension.getEntities({
            location: entity.location,
            closest: 6,
            type: 'thm:colour_light'
        });
        // Check if colour lights match the correct colour
        colour_lights.forEach((colour_light) => {
            const colour = colour_light.id === entity.id ? new_colour : colour_light.getProperty(ActorPropertiesEnum.COLOR);
            const colour_correct = colour_light.getProperty('property:colour_correct');
            if (colour === colour_correct) {
                correct_colours_so_far++;
            }
        });
        if (CORRECT_COLOURS === correct_colours_so_far) {
            for (const colour_light of colour_lights) {
                colour_light.setProperty(ActorPropertiesEnum.LOCKED, true);
            }
            system.runTimeout(() => {
                for (const colour_light of colour_lights) {
                    colour_light.setProperty(ActorPropertiesEnum.COLOR, 4);
                    entity.dimension.playSound(SoundEnum.CORRECT, entity.location, { volume: 2 });
                    GameController.completeColourLight();
                }
            }, 0.5 * TicksPerSecond);
        }
    }
    static hitPlank(hitEventData) {
        const entity = hitEventData.hitEntity;
        const damagingEntity = hitEventData.damagingEntity;
        if (!(damagingEntity instanceof Player))
            return;
        const player = damagingEntity;
        const item = ItemUtils.getMainHandItem(player);
        if (!item)
            return;
        if (item.typeId !== 'thm:hammer')
            return;
        const fallen = world.getDynamicProperty(DynamicPropertyEnum.FALLEN);
        if (fallen)
            return;
        entity.triggerEvent('thm:fall');
        let plankHit = world.getDynamicProperty(DynamicPropertyEnum.PLANKS_HIT) ?? 0;
        plankHit++;
        world.setDynamicProperty(DynamicPropertyEnum.PLANKS_HIT, plankHit);
        entity.setDynamicProperty(DynamicPropertyEnum.FALLEN, true);
        player.dimension.playSound(SoundEnum.HIT_PLANK, player.location, { volume: 2 });
        if (plankHit !== 2)
            return;
        const barrierBlocks = new BlockVolume({ x: -36, y: 51, z: 47 }, { x: -37, y: 52, z: 47 });
        player.dimension.fillBlocks(barrierBlocks, 'minecraft:air');
    }
}

const HIT_ENTITIES = {
    "thm:colour_light": { function: HitEntityController.hitColourLight },
    "thm:plank": { function: HitEntityController.hitPlank },
    "thm:menu_entity": { function: PlayerController.lobbyPreGame },
};
function isHitEntity(typeID) {
    return typeID in HIT_ENTITIES;
}

world.afterEvents.entityHitEntity.subscribe((event) => {
    if (!(event.damagingEntity instanceof Player))
        return;
    const typeId = event.hitEntity.typeId;
    if (isHitEntity(typeId))
        HIT_ENTITIES[typeId].function(event);
});

world.afterEvents.playerSpawn.subscribe(({ player, initialSpawn }) => {
    if (!initialSpawn)
        return;
    PlayerUtils.initPlayer(player);
    if (player.location.y < 100)
        PlayerController.preGame(player);
    const removedItemData = player.getDynamicProperty("removedItemData");
    if (removedItemData) {
        const items = JSON.parse(removedItemData);
        for (const item of items) {
            player.runCommand(`clear @s ${item.typeId} 0 ${item.amount}`);
        }
        player.setDynamicProperty(DynamicPropertyEnum.REMOVED_ITEM_DATA, undefined);
    }
    /* if(PlayerUtils.hasPlayed(player)){
        system.runTimeout(() => {
            SoundUtils.ambientSound(player);
        }, 10 * TicksPerSecond);
    } */
});

const distance = 15;
class RaycastUtils {
    static getRaycastLocation(player) {
        const entityHit = player.getEntitiesFromViewDirection({ maxDistance: distance });
        const naughtCornerVol = new BlockVolume({ x: 18.8, y: 50, z: 34 }, { x: 22, y: 55, z: 38 });
        const brokenGlass = world.getDynamicProperty(DynamicPropertyEnum.BROKEN_GLASS) ?? false;
        new BlockVolume({ x: 6, y: 50, z: 22 }, { x: 23, y: 54, z: 39 });
        const checkVolume = new BlockVolume({ x: 7, y: 50, z: 38 }, { x: 15, y: 54, z: 27 });
        const drone = DroneUtils.getDrone();
        let glassMethod = false;
        if (entityHit) {
            for (const hit of entityHit) {
                if (naughtCornerVol.isInside(hit.entity.location) && !brokenGlass) {
                    player.dimension.spawnEntity("thm:drone_target", hit.entity.location);
                    glassMethod = true;
                }
                if (hit.entity.typeId === "thm:button" && !glassMethod)
                    return hit.entity.location;
            }
        }
        const hit = player.getBlockFromViewDirection({ maxDistance: distance });
        if (hit?.block.typeId === "minecraft:yellow_stained_glass_pane" && !glassMethod) {
            player.dimension.spawnEntity("thm:drone_target_red", hit.block.location);
            SoundUtils.globalSound(SoundEnum.DRONE_GIVE_SIGNAL);
            return { x: 0, y: 0, z: 0 };
        }
        //world.sendMessage(`${hit?.block.typeId}`);
        const result = hit ? getHitLocation(hit) : customRaycast(player);
        if (naughtCornerVol.isInside(result) && !brokenGlass) {
            const glassTarget = player.dimension.spawnEntity("thm:drone_target", result);
            glassTarget.setProperty(ActorPropertiesEnum.BLANK, true);
            glassMethod = true;
        }
        if (glassMethod) {
            if (checkVolume.isInside(drone.location)) {
                return { x: 17.5, y: 52.5, z: 36.5 };
            }
            else {
                return { x: 20.5, y: 52.5, z: 34.5 };
            }
        }
        return result;
    }
}
function getHitLocation(hit) {
    const location = hit.block.location;
    const face = hit.face;
    //world.sendMessage(`Hit Location: x:${location.x} y:${location.y} z:${location.z}, Face: ${face}`);
    const offset = BlockUtils.getVectorFromDirection(face);
    return VectorUtils.add(location, offset);
}
function customRaycast(player) {
    const viewDirection = player.getViewDirection();
    const headLocation = player.getHeadLocation();
    //world.sendMessage(`Custom Raycast`)
    const targetLocation = {
        x: headLocation.x + viewDirection.x * distance,
        y: headLocation.y + viewDirection.y * distance,
        z: headLocation.z + viewDirection.z * distance
    };
    return targetLocation;
}

class ButtonController {
    static hitButton(entity) {
        const tag = entity.getTags()[0];
        switch (tag) {
            case "lobby":
                this.lobbyButton(entity);
                break;
            case "playroom":
                this.playRoomButton(entity);
                break;
            case "classroom":
                this.classroomButton(entity);
                break;
            case "ballpit":
                this.ballpitButton(entity);
                break;
            case "office":
                this.officeButton(entity);
                break;
        }
    }
    static lobbyButton(entity) {
        let buttonsHit = world.getDynamicProperty(DynamicPropertyEnum.LOBBY_BUTTONS) || 0;
        buttonsHit++;
        world.setDynamicProperty(DynamicPropertyEnum.LOBBY_BUTTONS, buttonsHit);
        if (buttonsHit === 2)
            GameController.openDoorFromKeycard(KeycardColourEnum.YELLOW);
    }
    static playRoomButton(entity) {
        const dimension = entity.dimension;
        const egg = dimension.spawnEntity("thm:egg", { x: -20, y: 50, z: 21.5 });
        egg?.setProperty(ActorPropertiesEnum.ROLL, true);
        system.runTimeout(() => {
            egg?.setProperty(ActorPropertiesEnum.ROLL, false);
        }, 2 * TicksPerSecond);
    }
    static classroomButton(entity) {
        const dimension = entity.dimension;
        const door = dimension.getEntities({ type: "thm:door", tags: ["classroom"] })[0];
        door.setProperty("property:open", true);
        const block = dimension.getBlock({ x: 21, y: 51, z: 22 });
        block?.setPermutation(BlockPermutation.resolve("air"));
    }
    static ballpitButton(entity) {
        OpilaController.startChase();
    }
    static officeButton(entity) {
        const reader = entity.dimension.getBlock({ x: 2, y: 51, z: 56 });
        reader?.setPermutation(reader.permutation.withState("thm:colour", "thm:keycard_orange"));
    }
}

const tpLocations = {
    "minecraft:diamond_block": { x: 2, y: 51, z: 6 }, //lobby
    "minecraft:gold_block": { x: 11, y: 51, z: 35 }, //classroom
    "minecraft:emerald_block": { x: -6, y: 51, z: 37 }, //playground
    "minecraft:lapis_block": { x: -36, y: 51, z: 52 }, //ball pit 1
    "minecraft:coal_block": { x: -3, y: 48, z: 67 }, //ball pit 2
    "minecraft:iron_block": { x: 9, y: 51, z: 57 }, //office
};
class RemoteUtils {
    static currentInterval;
    static currentTimeout;
    static useRemote(player) {
        const battires = world.getDynamicProperty(DynamicPropertyEnum.BATTERIES_COLLECTED);
        if (battires !== 3)
            return;
        const isSneaking = player.isSneaking;
        const drone = DroneUtils.getDrone();
        if (!drone)
            return;
        if (DroneUtils.isInUse(drone))
            return;
        SoundUtils.globalSound(SoundEnum.DRONE_GIVE_SIGNAL);
        SoundUtils.globalSound(SoundEnum.DRONE_RECEIVE);
        if (isSneaking) {
            this.tpDrone(player, drone);
            return;
        }
        this.droneObjective(player, drone);
    }
    static reachTarget(location, triggerButton = true) {
        const battires = world.getDynamicProperty(DynamicPropertyEnum.BATTERIES_COLLECTED);
        if (battires !== 3) {
            world.setDynamicProperty(DynamicPropertyEnum.BATTERIES_COLLECTED, 3);
            return;
        }
        const drone = DroneUtils.getDrone();
        if (!drone)
            return;
        this.setDroneColour(drone, 0, 255);
        DroneUtils.setIsInUse(drone, false);
        const targets = drone.dimension.getEntities({ type: "thm:drone_target" });
        let isGlass = false;
        let isGlass2 = false;
        for (const target of targets) {
            if (target.hasTag("glass"))
                isGlass = true;
            if (target.hasTag("glass2"))
                isGlass2 = true;
            target.remove();
        }
        const raycast = drone.getBlockFromViewDirection({ maxDistance: 1 });
        const brokenGlass = world.getDynamicProperty(DynamicPropertyEnum.BROKEN_GLASS) ?? false;
        const brokenGlass2 = world.getDynamicProperty(DynamicPropertyEnum.BROKEN_GLASS2) ?? false;
        if (isGlass && !brokenGlass) {
            drone.dimension.runCommand('fill 18 54 35 18 50 38 air destroy');
            world.setDynamicProperty(DynamicPropertyEnum.BROKEN_GLASS, true);
        }
        if (isGlass2 && !brokenGlass2) {
            drone.dimension.runCommand('fill 19 50 34 21 54 34 air destroy');
            world.setDynamicProperty(DynamicPropertyEnum.BROKEN_GLASS, true);
        }
        if (raycast) {
            SoundUtils.globalSound(SoundEnum.DRONE_THUD);
        }
        const button = drone.dimension.getEntities({ location: location, type: "thm:button", maxDistance: 1 })[0];
        if (button && triggerButton) {
            const locked = button.getProperty(ActorPropertiesEnum.LOCKED);
            const used = button.getProperty(ActorPropertiesEnum.USED);
            if (!locked && !used) {
                button.setProperty(ActorPropertiesEnum.USED, true);
                SoundUtils.globalSound(SoundEnum.BUTTON_CLICK);
                ButtonController.hitButton(button);
            }
            else if (locked) {
                SoundUtils.globalSound(SoundEnum.DRONE_THUD);
            }
        }
    }
    static droneObjective(player, drone) {
        const naughtyCornerVolume = new BlockVolume({ x: 18, y: 50, z: 34 }, { x: 22, y: 54, z: 38 });
        const location = RaycastUtils.getRaycastLocation(player);
        const inObjective = DroneUtils.hasObjective(drone);
        SoundUtils.globalSound(SoundEnum.DRONE_LOOK);
        if (naughtyCornerVolume.isInside(location)) ;
        if (inObjective)
            return;
        if (VectorUtils.equals(location, { x: 0, y: 0, z: 0 }))
            return;
        DroneUtils.setIsInUse(drone, true);
        const target = player.dimension.spawnEntity("thm:drone_target", location);
        this.sameRoom(player, drone);
        if (VectorUtils.equals(location, { x: 17.5, y: 52.5, z: 36.5 })) {
            target.setProperty(ActorPropertiesEnum.BLANK, true);
            target.addTag("glass");
        }
        if (VectorUtils.equals(location, { x: 20.5, y: 52.5, z: 34.5 })) {
            target.setProperty(ActorPropertiesEnum.BLANK, true);
            target.addTag("glass2");
        }
        drone.setProperty(ActorPropertiesEnum.COLOR_R, 255);
        drone.setProperty(ActorPropertiesEnum.COLOR_G, 0);
        this.setDroneColour(drone, 255, 255);
        const delay = 10;
        system.runTimeout(() => {
            const drone = DroneUtils.getDrone();
            if (!drone)
                return;
            this.setDroneColour(drone, 0, 255);
            DroneUtils.setIsInUse(drone, false);
            const targets = drone.dimension.getEntities({ type: "thm:drone_target" });
            for (const target of targets) {
                target.remove();
            }
        }, delay * TicksPerSecond);
    }
    static sameRoom(player, drone) {
        const droneCheckLoc = { x: drone.location.x, y: 0, z: drone.location.z };
        const droneBlock = drone.dimension.getBlock(droneCheckLoc);
        const playerheckLoc = { x: player.location.x, y: 0, z: player.location.z };
        const playerBlock = player.dimension.getBlock(playerheckLoc);
        let tpLoc = undefined;
        if (!droneBlock || !playerBlock)
            return;
        tpLoc = tpLocations[playerBlock.typeId];
        if (playerBlock.typeId === droneBlock.typeId)
            return;
        drone.teleport(tpLoc);
    }
    static tpDrone(player, drone) {
        const checkLoc = { x: player.location.x, y: 0, z: player.location.z };
        const block = player.dimension.getBlock(checkLoc);
        if (DroneUtils.isInUse(drone))
            return;
        let tpLoc = undefined;
        if (block)
            tpLoc = tpLocations[block.typeId];
        if (tpLoc === undefined) {
            return;
        }
        drone.teleport(tpLoc);
        drone.setProperty(ActorPropertiesEnum.DRONE_ICON, true);
        DroneUtils.setIsInUse(drone, true);
        const delay = 3;
        system.runTimeout(() => {
            this.resetObjective(drone);
        }, delay * TicksPerSecond);
    }
    static resetObjective(drone) {
        DroneUtils.setIsInUse(drone, false);
        DroneUtils.setObjective(drone, undefined);
        drone.setProperty(ActorPropertiesEnum.DRONE_ICON, false);
        drone.setProperty(ActorPropertiesEnum.COLOR_R, 0);
        drone.setProperty(ActorPropertiesEnum.COLOR_G, 255);
        if (this.currentTimeout) {
            system.clearRun(this.currentTimeout);
            this.currentTimeout = undefined;
        }
        if (this.currentInterval) {
            system.clearRun(this.currentInterval);
            this.currentInterval = undefined;
        }
    }
    static setDroneColour(drone, targetR, targetG) {
        let colourR = drone.getProperty(ActorPropertiesEnum.COLOR_R) ?? 0;
        let colourG = drone.getProperty(ActorPropertiesEnum.COLOR_G) ?? 0;
        const incrementR = targetR > colourR ? 10 : -10;
        const incrementG = targetG > colourG ? 10 : -10;
        if (this.currentInterval) {
            system.clearRun(this.currentInterval);
            this.currentInterval = undefined;
        }
        const colourInterval = system.runInterval(() => {
            if (colourR !== targetR) {
                colourR = Math.min(255, Math.max(0, colourR + incrementR));
                drone.setProperty(ActorPropertiesEnum.COLOR_R, colourR);
            }
            if (colourG !== targetG) {
                colourG = Math.min(255, Math.max(0, colourG + incrementG));
                drone.setProperty(ActorPropertiesEnum.COLOR_G, colourG);
            }
            if (colourR === targetR && colourG === targetG) {
                system.clearRun(colourInterval);
                this.currentInterval = undefined;
            }
        }, 1);
        this.currentInterval = colourInterval;
    }
}

world.afterEvents.dataDrivenEntityTrigger.subscribe((event) => {
    const entity = event.entity;
    if (entity === undefined)
        return;
    const location = entity.location;
    const ID = event.eventId;
    if (entity.typeId === 'thm:drone_target')
        RemoteUtils.reachTarget(location);
    if (entity.typeId === 'thm:opila_alive' && ID === 'thm:jumpscare')
        JumpScareController.trigger(PlayerUtils.getAllPlayers());
    const chaseComplete = world.getDynamicProperty(DynamicPropertyEnum.CHASE_COMPLETE) ?? false;
    if (entity.typeId === 'thm:opila_target' && !chaseComplete)
        JumpScareController.trigger(PlayerUtils.getAllPlayers());
});

class InteractEntityController {
    static feedBirt(eventData) {
        const entity = eventData.target;
        const player = eventData.player;
        const equippable = player.getComponent(EntityComponentTypes.Equippable);
        const mainHandItem = equippable.getEquipment(EquipmentSlot.Mainhand);
        if (!mainHandItem)
            return;
        if (mainHandItem.typeId !== 'thm:eggs')
            return;
        if (mainHandItem.amount > 1) {
            mainHandItem.amount--;
            equippable.setEquipment(EquipmentSlot.Mainhand, mainHandItem);
        }
        else {
            equippable.setEquipment(EquipmentSlot.Mainhand, undefined);
        }
        entity.dimension.playSound(SoundEnum.FEED_BIRD, entity.location, { volume: 2 });
        let eggCount = world.getDynamicProperty(DynamicPropertyEnum.EGGS_COLLECTED) ?? 0;
        eggCount++;
        world.setDynamicProperty(DynamicPropertyEnum.EGGS_COLLECTED, eggCount);
        if (eggCount === 6) {
            const keycard = entity.dimension.spawnEntity("thm:keycard", { x: -35.4, y: 51.6, z: 41.5 });
            keycard.setProperty(ActorPropertiesEnum.COLOR, 1);
        }
        if (eggCount === 7) {
            const keycard = entity.dimension.spawnEntity("thm:keycard", { x: -35.4, y: 51.6, z: 41.5 });
            keycard.setProperty(ActorPropertiesEnum.COLOR, 3);
            ProgressUtils.addProgress(1);
        }
    }
    static liftReader(eventData) {
        const entity = eventData.target;
        const player = eventData.player;
        const colour = entity.getProperty(ActorPropertiesEnum.COLOR);
        if (colour === 1)
            return;
        const equippable = player.getComponent(EntityComponentTypes.Equippable);
        const mainHandItem = equippable.getEquipment(EquipmentSlot.Mainhand);
        if (!mainHandItem)
            return;
        if (!mainHandItem.typeId.includes("thm:keycard"))
            return;
        if (mainHandItem.typeId !== "thm:keycard_orange") {
            entity.setProperty(ActorPropertiesEnum.COLOR, 2);
            entity.dimension.playSound(SoundEnum.ERROR, entity.location, { volume: 2 });
            system.runTimeout(() => { entity.setProperty(ActorPropertiesEnum.COLOR, 0); }, 2 * TicksPerSecond);
            return;
        }
        entity.dimension.playSound(SoundEnum.KEYCARD, entity.location, { volume: 2 });
        entity.setProperty(ActorPropertiesEnum.COLOR, 1);
        system.runTimeout(() => {
            entity.dimension.playSound(SoundEnum.ELEVATOR_DOOR_OPEN, entity.location, { volume: 2 });
            const lift = entity.dimension.getEntities({ type: "thm:lift" })[0];
            lift.setProperty(ActorPropertiesEnum.OPENED, false);
        }, 2 * TicksPerSecond);
        system.runTimeout(() => {
            GameController.sendLift();
        }, 3 * TicksPerSecond);
    }
    static playTape(eventData) {
        const entity = eventData.target;
        const player = eventData.player;
        const mainHandItem = ItemUtils.getMainHandItem(player);
        if (!mainHandItem)
            return;
        if (mainHandItem.typeId !== "thm:tape")
            return;
        ItemUtils.removeItem("thm:tape", player);
        entity.dimension.playSound(SoundEnum.TAPE_INSERT, entity.location, { volume: 2 });
        ProgressUtils.addProgress(1);
        system.runTimeout(() => {
            entity.dimension.playSound(SoundEnum.TAPE, entity.location, { volume: 2 });
        }, 2 * TicksPerSecond);
    }
    static collectTape(eventData) {
        const entity = eventData.target;
        const player = eventData.player;
        const item = new ItemStack(`thm:tape`);
        ItemUtils.giveItem(item, player);
        NotificationUtils.tapeNotification(player);
        OutlineController.removeEntity(entity);
        entity.triggerEvent('thm:collected');
        ProgressUtils.addProgress(1);
    }
    static collectBattery(eventData) {
        const entity = eventData.target;
        let batteriesCollected = world.getDynamicProperty(DynamicPropertyEnum.BATTERIES_COLLECTED) ?? 0;
        batteriesCollected++;
        world.setDynamicProperty(DynamicPropertyEnum.BATTERIES_COLLECTED, batteriesCollected);
        entity.triggerEvent('thm:collected');
        NotificationUtils.batteryNotification(eventData.player);
        OutlineController.removeEntity(entity);
        if (batteriesCollected !== 2)
            return;
        GameController.completeBattery();
        NotificationUtils.remoteInfoNotification(eventData.player);
    }
    static collectRemote(eventData) {
        const entity = eventData.target;
        world.setDynamicProperty(DynamicPropertyEnum.REMOTE_COLLECTED, true);
        const players = PlayerUtils.getAllPlayers();
        NotificationUtils.remoteNotification();
        players.forEach((player) => {
            ItemUtils.setRemote(player);
        });
        OutlineController.removeEntity(entity);
        GameController.pickUpRemote();
        entity.triggerEvent('thm:collected');
    }
    static collectKeycard(eventData) {
        const entity = eventData.target;
        const keycardColourIndex = entity.getProperty(ActorPropertiesEnum.COLOR);
        OutlineController.removeEntity(entity);
        entity.triggerEvent('thm:collected');
        let keycardColour = null;
        switch (keycardColourIndex) {
            case 0:
                world.setDynamicProperty(DynamicPropertyEnum.BLUE_CARD_COLLECTED, true);
                keycardColour = KeycardColourEnum.BLUE;
                break;
            case 1:
                world.setDynamicProperty(DynamicPropertyEnum.YELLOW_CARD_COLLECTED, true);
                keycardColour = KeycardColourEnum.YELLOW;
                break;
            case 2:
                world.setDynamicProperty(DynamicPropertyEnum.ORANGE_CARD_COLLECTED, true);
                keycardColour = KeycardColourEnum.ORANGE;
                OpilaController.initOpila();
                break;
            case 3:
                world.setDynamicProperty(DynamicPropertyEnum.PURPLE_CARD_COLLECTED, true);
                keycardColour = KeycardColourEnum.PURPLE;
                ProgressUtils.addProgress(1);
                break;
            default:
                throw new Error('Invalid keycard colour');
        }
        const players = PlayerUtils.getAllPlayers();
        NotificationUtils.keyCardNotification(keycardColour);
        players.forEach((player) => {
            ItemUtils.setKeycard(player, keycardColour);
        });
    }
    static collectCollectable(eventData) {
        const entity = eventData.target;
        const location = entity.location;
        const type = entity.getProperty(ActorPropertiesEnum.TYPE);
        entity.triggerEvent('thm:collected');
        ProgressUtils.addProgress(1);
        entity.dimension.playSound(SoundEnum.COLLECTABLE, location, { volume: 2 });
        InteractEntityController.itemCollectEffects(location);
        OutlineController.removeEntity(entity);
        let dp = "";
        switch (type) {
            case "boarding_pass_1":
                dp = DynamicPropertyEnum.COLLECATABLE1;
                break;
            case "boarding_pass_2":
                dp = DynamicPropertyEnum.COLLECATABLE2;
                break;
            case "letter_1":
                dp = DynamicPropertyEnum.COLLECATABLE3;
                break;
            case "letter_2":
                dp = DynamicPropertyEnum.COLLECATABLE4;
                break;
            case "note":
                dp = DynamicPropertyEnum.COLLECATABLE5;
                break;
            case "stickers":
                dp = DynamicPropertyEnum.COLLECATABLE6;
                break;
        }
        world.setDynamicProperty(dp, true);
        const players = PlayerUtils.getAllPlayers();
        players.forEach((player) => {
            ItemUtils.giveCollectables(player);
        });
    }
    static collectHammer(eventData) {
        const entity = eventData.target;
        const player = eventData.player;
        const item = new ItemStack(`thm:hammer`);
        ItemUtils.giveItem(item, player);
        NotificationUtils.hammerNotification(player);
        OutlineController.removeEntity(entity);
        entity.triggerEvent('thm:collected');
    }
    static collectEgg(eventData) {
        const player = eventData.player;
        const entity = eventData.target;
        const rolling = entity.getProperty(ActorPropertiesEnum.ROLL);
        if (rolling)
            return;
        const item = new ItemStack(`thm:eggs`);
        ItemUtils.giveItem(item, player);
        NotificationUtils.eggNotification(player);
        OutlineController.removeEntity(entity);
        entity.triggerEvent('thm:collected');
        let eggCount = world.getDynamicProperty(DynamicPropertyEnum.EGGS_PICKED) ?? 0;
        eggCount++;
        world.setDynamicProperty(DynamicPropertyEnum.EGGS_PICKED, eggCount);
    }
    static itemCollectEffects(location) {
        const dimension = world.getDimension("minecraft:overworld");
        dimension.playSound(SoundEnum.ITEM_PICKUP, location, { volume: 2 });
    }
}

const INTERACT_ENTITIES = {
    'thm:opila': { function: InteractEntityController.feedBirt },
    "thm:battery": { function: InteractEntityController.collectBattery },
    "thm:keycard": { function: InteractEntityController.collectKeycard },
    "thm:egg": { function: InteractEntityController.collectEgg },
    "thm:collectable": { function: InteractEntityController.collectCollectable },
    "thm:hammer_pickup": { function: InteractEntityController.collectHammer },
    "thm:remote_pickup": { function: InteractEntityController.collectRemote },
    "thm:tape_recorder": { function: InteractEntityController.playTape },
    "thm:tape_pickup": { function: InteractEntityController.collectTape },
    "thm:keycard_reader_entity": { function: InteractEntityController.liftReader },
};
function isInteractEntity(typeID) {
    return INTERACT_ENTITIES.hasOwnProperty(typeID);
}

world.afterEvents.playerInteractWithEntity.subscribe((event) => {
    const typeId = event.target.typeId;
    const collected = event.target.getProperty(ActorPropertiesEnum.COLLECTED);
    if (collected)
        return;
    if (!isInteractEntity(typeId))
        return;
    INTERACT_ENTITIES[typeId].function(event);
});

world.beforeEvents.playerLeave.subscribe((event) => {
    const player = event.player;
    const tickID = player.getDynamicProperty(DynamicPropertyEnum.CLOCK_TICK_ID);
    if (tickID)
        system.clearRun(tickID);
    const inventory = player.getComponent(EntityComponentTypes.Inventory);
    if (!inventory)
        return;
    const container = inventory.container;
    if (!container)
        return;
    const size = container.size;
    const itemsToRemove = [
        "thm:hammer",
        "thm:tape",
        "thm:eggs"
    ];
    const itemsToAdd = [];
    const removedItems = [];
    for (let i = 0; i < size; i++) {
        const item = container.getItem(i);
        if (!item)
            continue;
        if (!itemsToRemove.includes(item.typeId))
            continue;
        itemsToAdd.push(item);
        removedItems.push({ typeId: item.typeId, amount: item.amount });
    }
    if (removedItems.length === 0)
        return;
    player.setDynamicProperty(DynamicPropertyEnum.REMOVED_ITEM_DATA, JSON.stringify(removedItems));
    system.run(() => {
        for (const item of itemsToAdd) {
            giveItem(item, player);
        }
    });
});
function getRandomPlayer(player) {
    const otherPlayers = world.getAllPlayers().filter(p => p !== player);
    if (otherPlayers.length === 0)
        return undefined;
    return otherPlayers[Math.floor(Math.random() * otherPlayers.length)];
}
function giveItem(item, player) {
    const randomPlayer = getRandomPlayer(player);
    if (!randomPlayer)
        return;
    try {
        ItemUtils.giveItem(item, randomPlayer);
        randomPlayer.sendMessage({ translate: `message.itemTransfer.${item.typeId}` });
    }
    catch (error) {
        giveItem(item, player);
    }
}

world.afterEvents.itemUse.subscribe((event) => {
    const itemStack = event.itemStack;
    const player = event.source;
    const entityHit = player.getEntitiesFromViewDirection({ families: ['outline'], maxDistance: 6 })[0];
    if (entityHit)
        return;
    if (itemStack.typeId === 'thm:remote')
        RemoteUtils.useRemote(player);
    if (itemStack.typeId === 'thm:menu') {
        endForm(player);
    }
    if (itemStack.typeId.startsWith(`thm:clock`)) {
        const canClickClock = world.getDynamicProperty(DynamicPropertyEnum.CLOCK_CAN_CLICK) ?? true;
        if (canClickClock)
            ClockController.advanceTime();
        return;
    }
    debugItems(player, itemStack);
});
function debugItems(player, itemStack) {
    if (itemStack.typeId !== 'minecraft:stick')
        return;
    const itemName = itemStack.nameTag;
    switch (itemName) {
        case 'jumpscare':
            const players = PlayerUtils.getAllPlayers();
            JumpScareController.trigger(players);
            break;
        case 'lights':
            GameController.turnOnLights();
            break;
        case 'chairlift':
            ChairliftController.setRunning(true);
            ChairliftController.setCompleted(0);
            const chairlifts = world
                .getDimension('overworld')
                .getEntities({ type: 'thm:chairlift' });
            for (const chairlift of chairlifts) {
                ChairliftController.setMoving(chairlift, true);
            }
            break;
    }
}

const decals = {
    "josh": { width: 7, height: 5 },
    "balloons": { width: 6, height: 5 },
    "josh_flipped": { width: 7, height: 5 },
    "balloons_flipped": { width: 6, height: 5 },
    "cafe": { width: 2, height: 2 },
    "cafe_flipped": { width: 2, height: 2 },
    "boots": { width: 2, height: 2 },
    "boots_flipped": { width: 2, height: 2 },
    "flynn": { width: 7, height: 5 },
    "wall_art": { width: 8, height: 4 },
    "wall_art_flipped": { width: 8, height: 4 },
    "whiteboard": { width: 6, height: 3 },
    "whiteboard2": { width: 6, height: 3 },
    "playground1": { width: 40, height: 5 },
    "playground2": { width: 40, height: 5 },
    "playground3": { width: 9, height: 5 },
    "playground4": { width: 9, height: 5 },
    "playground5": { width: 31, height: 5 },
    "banbaleena": { width: 8, height: 5 },
    "banban": { width: 8, height: 5 },
    "fiddles": { width: 9, height: 5 },
    "opila": { width: 7, height: 5 },
    "fiddles_bw": { width: 9, height: 5 },
    "whiteboard_bw": { width: 6, height: 3 },
    "colour_game": { width: 12, height: 5 },
    "naughty": { width: 2, height: 2 },
    "feet": { width: 1, height: 4 },
    "jump": { width: 3, height: 2 }
};
system.afterEvents.scriptEventReceive.subscribe((event) => {
    const { id, initiator, message, sourceBlock, sourceEntity, sourceType, } = event;
    let sender = sourceType == ScriptEventSource.Entity ? sourceEntity : null;
    const entity = sender;
    if (!(entity instanceof Player))
        return;
    const player = entity;
    const [command, decalName] = id.split(':');
    if (command !== 'placeDecal')
        return;
    const decal = decals[decalName];
    if (decal === undefined) {
        player.sendMessage('§cDecal not found.');
        return;
    }
    const block = player.getBlockFromViewDirection({ includeLiquidBlocks: true, includePassableBlocks: true, maxDistance: 6 });
    if (block === undefined) {
        player.sendMessage('§cYou are too far away from the block.');
        return;
    }
    const { width, height } = decal;
    const direction = block.face;
    let startLocation = block.block.location;
    const dimension = block.block.dimension;
    for (let hightIndex = 0; hightIndex < height; hightIndex++) {
        let steplocation = { x: startLocation.x, y: startLocation.y - hightIndex, z: startLocation.z };
        for (let widthIndex = 0; widthIndex < width; widthIndex++) {
            steplocation = getLocationFromDirection(startLocation, direction, widthIndex, hightIndex);
            const replaceBlock = dimension.getBlock(steplocation);
            const placeDirection = getPlaceDirection(direction);
            replaceBlock?.setPermutation(BlockPermutation.resolve(`thm:decal_${decalName}_${hightIndex + 1}${widthIndex + 1}`).withState("minecraft:cardinal_direction", placeDirection));
        }
    }
});
function getLocationFromDirection(location, direction, widthIndex, hightIndex) {
    direction = direction.toLowerCase();
    switch (direction) {
        case 'north':
            return { x: location.x - widthIndex, y: location.y - hightIndex, z: location.z };
        case 'south':
            return { x: location.x + widthIndex, y: location.y - hightIndex, z: location.z };
        case 'west':
            return { x: location.x, y: location.y - hightIndex, z: location.z + widthIndex };
        case 'east':
            return { x: location.x, y: location.y - hightIndex, z: location.z - widthIndex };
        default:
            throw new Error('Invalid direction');
    }
}
function getPlaceDirection(direction) {
    direction = direction.toLowerCase();
    switch (direction) {
        case 'north':
            return 'west';
        case 'south':
            return 'east';
        case 'west':
            return 'south';
        case 'east':
            return 'north';
        default:
            throw new Error('Invalid direction');
    }
}

system.afterEvents.scriptEventReceive.subscribe((event) => {
    const { id, initiator, message, sourceBlock, sourceEntity, sourceType, } = event;
    let sender = sourceType == ScriptEventSource.Entity ? sourceEntity : null;
    const entity = sender;
    if (!(entity instanceof Player))
        return;
    const player = entity;
    if (id !== 'thm:reset')
        return;
    const DPs = world.getDynamicPropertyIds();
    for (const dp of DPs) {
        world.setDynamicProperty(dp, undefined);
    }
    player.sendMessage(`Reset all ${DPs.length} dynamic properties`);
});

system.afterEvents.scriptEventReceive.subscribe((event) => {
    const { id, initiator, message, sourceBlock, sourceEntity, sourceType, } = event;
    let sender = sourceType == ScriptEventSource.Entity ? sourceEntity : null;
    const entity = sender;
    if (!(entity instanceof Player))
        return;
    const player = entity;
    if (id !== 'thm:setReader')
        return;
    const readerID = parseInt(message);
    const entities = player.dimension.getEntities({ type: 'thm:reader', location: player.location, closest: 1, maxDistance: 10 });
    if (entities === undefined) {
        player.sendMessage('§cYou are too far away from the entity.');
        return;
    }
    if (entities.length === 0) {
        player.sendMessage('§cNo entities found');
        return;
    }
    if (entities[0].typeId !== "thm:reader") {
        player.sendMessage('§cCannot find reader entity');
        return;
    }
    const reader = entities[0];
    reader.setDynamicProperty(DynamicPropertyEnum.READER_ID, readerID);
    player.sendMessage('§aSet ID to ' + readerID);
});

system.afterEvents.scriptEventReceive.subscribe((event) => {
    const { id, initiator, message, sourceBlock, sourceEntity, sourceType, } = event;
    let sender = sourceType == ScriptEventSource.Entity ? sourceEntity : null;
    const entity = sender;
    if (!(entity instanceof Player))
        return;
    const player = entity;
    if (id === 'thm:delete') {
        world.setDynamicProperty(message, undefined);
        player.sendMessage(`Dynamic Property ${message} deleted`);
    }
    if (id !== 'thm:test')
        return;
    switch (message) {
        case 'lift':
            GameController.startLift();
            break;
        case 'creep':
            OpilaController.initOpila();
            break;
        case 'chase':
            OpilaController.startChase();
            break;
        case 'setmaxplayers':
            const value = parseInt(message);
            player.runCommand(`setmaxplayers ${value}`);
            break;
        case 'dps':
            const dpIDs = world.getDynamicPropertyIds();
            for (const dp of dpIDs) {
                const value = world.getDynamicProperty(dp);
                player.sendMessage(`${dp}: ${value}`);
            }
            break;
        case 'egg':
            const dimension = world.getDimension("minecraft:overworld");
            const egg = dimension.spawnEntity("thm:egg", { x: -20, y: 50, z: 21.5 });
            egg?.setProperty(ActorPropertiesEnum.ROLL, true);
            system.runTimeout(() => {
                egg?.setProperty(ActorPropertiesEnum.ROLL, false);
            }, 2 * TicksPerSecond);
            break;
    }
});

// Glass notification
const glassNotificationInterval = system.runInterval(() => {
    const triggerVolume = new BlockVolume({ x: 7, y: 49, z: 35 }, { x: 7, y: 53, z: 37 });
    const players = PlayerUtils.getAllPlayers();
    for (const player of players) {
        if (triggerVolume.isInside(player.location)) {
            NotificationUtils.broadcastNotification({ translate: "message.broadcast.drone.glass" });
            system.clearRun(glassNotificationInterval);
            return;
        }
    }
}, 1);
// Drone idle sound
system.runInterval(() => {
    const drone = DroneUtils.getDrone();
    if (drone === undefined)
        return;
    if (drone.getProperty("property:power") === "off")
        return;
    drone.dimension.playSound(SoundEnum.DRONE_IDLE, drone.location, { volume: 0.2 });
}, TicksPerSecond);
// More ambient sounds
const min = 5 * 60;
const max = 15 * 60;
system.runInterval(() => {
    let randomInterval = Math.floor(Math.random() * (max - min + 1)) + min;
    system.runTimeout(() => {
        const players = PlayerUtils.getAllPlayers();
        const gameend = world.getDynamicProperty(DynamicPropertyEnum.GAMEEND) ?? false;
        if (gameend)
            return;
        for (const player of players) {
            player.playSound(SoundEnum.CONFUSION, { volume: 2 });
        }
    }, randomInterval * TicksPerSecond);
}, 60 * TicksPerSecond);
// Handle Ballpit respawn
system.runInterval(() => {
    const ballpitVolume = new BlockVolume({ x: 0, y: -2, z: 76 }, { x: -39, y: 40, z: 49 });
    const players = world.getAllPlayers();
    for (const player of players) {
        if (!ballpitVolume.isInside(player.location))
            continue;
        const inChase = world.getDynamicProperty(DynamicPropertyEnum.IN_CHASE) ?? false;
        if (!inChase) {
            player.teleport({ x: -36, y: 47, z: 72 });
        }
        else {
            NotificationUtils.broadcastNotification({ translate: "message.ballpit.respawn", with: [`${player.name}`] });
            GameController.respawnAllPlayers();
            OpilaController.failed();
        }
    }
}, 1 * TicksPerSecond);
// Handle Opila
const opilaTick = system.runInterval(() => {
    const doTick = world.getDynamicProperty(DynamicPropertyEnum.OPILA_TICK) ?? false;
    if (doTick) {
        OpilaController.tick(opilaTick);
    }
}, 1);
//Handle Chairlifts
system.runInterval(() => {
    const chairliftRunning = ChairliftController.getRunning();
    if (chairliftRunning) {
        const chairlifts = world.getDimension("overworld").getEntities({ type: "thm:chairlift" });
        for (const chairlift of chairlifts) {
            ChairliftController.run(chairlift);
        }
    }
}, 1);
// Handle Outlines
system.runInterval(() => {
    const players = world.getAllPlayers();
    for (const player of players) {
        OutlineController.run(player);
    }
}, 1);
// Handle Fake Floor
const floorTick = system.runInterval(() => {
    const floor = world.getDimension("overworld").getEntities({ type: "thm:fake_floor" })[0];
    if (floor === undefined)
        return;
    floor.applyImpulse({ x: 0, y: -0.001, z: 0 });
    if (floor.location.y < 42) {
        floor.remove();
        system.clearRun(floorTick);
        GameController.callLift();
    }
}, 1);
// Handle Lift
system.runInterval(() => {
    const dimension = world.getDimension("overworld");
    const lift = dimension.getEntities({ type: "thm:lift" })[0];
    if (lift === undefined)
        return;
    let direction = lift.getDynamicProperty(DynamicPropertyEnum.LIFT_DIRECTION);
    if (direction === undefined)
        return;
    if (direction === 0)
        return;
    const lightVolume = new BlockVolume({ x: 11, y: lift.location.y + 3, z: 58 }, { x: 11, y: lift.location.y + 3, z: 59 });
    const lightVolumeAbove = new BlockVolume({ x: 11, y: lift.location.y + 4, z: 58 }, { x: 11, y: lift.location.y + 4, z: 59 });
    const lightVolumeBelow = new BlockVolume({ x: 11, y: lift.location.y + 2, z: 58 }, { x: 11, y: lift.location.y + 2, z: 59 });
    dimension.fillBlocks(lightVolume, "minecraft:light_block_15");
    dimension.fillBlocks(lightVolumeAbove, "minecraft:air");
    dimension.fillBlocks(lightVolumeBelow, "minecraft:air");
    if (lift.location.y >= 50 && direction > 0) {
        lift.setDynamicProperty(DynamicPropertyEnum.LIFT_DIRECTION, 0);
        direction = 0;
        lift.teleport({ x: lift.location.x, y: 50, z: lift.location.z });
        lift.clearVelocity();
        system.runTimeout(() => {
            GameController.openLift();
        }, TicksPerSecond);
    }
    if (lift.location.y <= 25 && direction < 0) {
        lift.setDynamicProperty(DynamicPropertyEnum.LIFT_DIRECTION, 0);
        direction = 0;
        lift.teleport({ x: lift.location.x, y: 25, z: lift.location.z });
        lift.clearVelocity();
        system.runTimeout(() => {
            GameController.finalLift();
        }, 1);
    }
    if (direction === undefined)
        return;
    if (direction === 0)
        return;
    lift.applyImpulse({ x: 0, y: direction, z: 0 });
}, 1);
