import { Direction, system, world } from "@minecraft/server";
import * as mcui from "@minecraft/server-ui";
import PlayerHelper from "./PlayerHelper";
import { structure_data, getStructureDataForStructureId, getEntranceInstructionsForType, getEntranceNameForType } from "./StructureData";
import EntityManager from "./EntityManager";
import FSWorldHelper from "./fs/FSWorldHelper";
import FSDynamicProperty from "./fs/libs/FSDynamicProperty";
import FSUtil from "./fs/FSUtil";
import FSPlayerPerms from "./fs/libs/perms/FSPlayerPerms";
import SecurityDoor from "./entity/SecurityDoor";
import FSLocalizedString from "./fs/FSLocalizedString";
import FSInventoryManager from "./fs/libs/FSInventoryManager";
import { BUNKER_COST_CHIPS, CORRIDOR_COST_CHIPS, FULL_UNDO_SUPPORT_ENABLED, HUB_CORRIDOR_COST_CHIPS, RAYCAST_MAX_DISTANCE, REFUND_PERCENTAGE_ON_UNDO, SHOULD_REFUND_CHIPS_ON_UNDO } from "./fs/FSConstants";
import FSActionFormData from "./fs/forms/ui/FSActionFormData";
import FSActionFormResponse from "./fs/forms/resps/FSActionFormResp";
import FormManager from "./FormManager";
import FSPlayer from "./fs/FSPlayer";
class StructureData {
    constructor(structure) {
        this.structure = structure;
        this.original_structure = structure;
        this.location_0 = { x: 0, y: 0, z: 0 };
        this.location_1 = { x: 0, y: 0, z: 0 };
        this.size = { x: 0, y: 0, z: 0 };
        this.size_offset = { x: 0, y: 0, z: 0 };
        this.door_loc_0 = { x: 0, y: 0, z: 0 };
        this.door_loc_1 = { x: 0, y: 0, z: 0 };
        this.door_spawn_loc = { x: 0, y: 0, z: 0 };
        this.door_rotation = "0";
        this.rotation = "0_degrees";
    }
}
const raycast_max_distance = RAYCAST_MAX_DISTANCE;
export default class StructureManager {
    constructor() {
    }
    static sharedInstance() {
        if (!StructureManager.instance) {
            StructureManager.instance = new StructureManager();
        }
        return StructureManager.instance;
    }
    init() { }
    calculateStructureData(location, rotation, structure) {
        let loc_0 = location;
        let loc_1 = location;
        let rot = "0_degrees";
        let size = { x: 32, y: 16, z: 32 };
        let door_rotation = "0";
        let door_loc_0 = { x: 0, y: 0, z: 0 };
        let door_loc_1 = { x: 0, y: 0, z: 0 };
        let door_spawn_loc = location;
        let east_south_offset = -16;
        let x_z_offset = 8;
        if (this.isBuilding(structure)) {
            size = { x: 16, y: 12, z: 16 };
        }
        if (this.isEntrance(structure)) {
            size = { x: 16, y: 9, z: 16 };
        }
        if (this.isCorridorFirst(structure)) {
            size = { x: 16, y: 10, z: 48 };
            east_south_offset = -size.z;
        }
        if (this.isCorridorNormal(structure)) {
            size = { x: 16, y: 10, z: 32 };
            east_south_offset = -size.z;
        }
        if (this.isRoom(structure)) {
            size = { x: 32, y: 16, z: 32 };
            east_south_offset = -size.z;
            x_z_offset = size.x * 0.5;
        }
        if (this.isHub(structure)) {
            size = { x: 48, y: 16, z: 48 };
        }
        let size_offset = { x: size.x - 1, y: size.y - 1, z: size.z - 1 };
        if (this.isRoom(structure) || this.isCorridor(structure)) {
            location = { x: location.x, y: location.y - 1, z: location.z };
        }
        let offset_north_east = 0.0001;
        let offset_south_west = 0.9999;
        offset_north_east = 0;
        offset_south_west = 0;
        switch (rotation) {
            case Direction.North: {
                loc_0 = { x: location.x - x_z_offset, y: location.y - 1, z: location.z + 1 };
                loc_1 = { x: loc_0.x + size_offset.x, y: loc_0.y + size_offset.y, z: loc_0.z + size_offset.z };
                door_loc_0 = { x: location.x + 1, y: location.y + 1, z: location.z + 0 };
                door_loc_1 = { x: location.x - 2, y: location.y + 4, z: location.z + 0 };
                door_spawn_loc = { x: location.x - offset_north_east, y: location.y + 1, z: location.z + 1 };
                rot = "0_degrees";
                door_rotation = "180";
                break;
            }
            case Direction.East: {
                loc_0 = { x: location.x + east_south_offset, y: location.y - 1, z: location.z - x_z_offset };
                loc_1 = { x: loc_0.x + size_offset.z, y: loc_0.y + size_offset.y, z: loc_0.z + size_offset.x };
                door_loc_0 = { x: location.x + 0, y: location.y + 1, z: location.z + 1 };
                door_loc_1 = { x: location.x + 0, y: location.y + 4, z: location.z - 2 };
                door_spawn_loc = { x: location.x - 1, y: location.y + 1, z: location.z - offset_north_east };
                rot = "90_degrees";
                door_rotation = "270";
                break;
            }
            case Direction.South: {
                loc_0 = { x: location.x - (x_z_offset - 1), y: location.y - 1, z: location.z + east_south_offset };
                loc_1 = { x: loc_0.x + size_offset.x, y: loc_0.y + size_offset.y, z: loc_0.z + size_offset.z };
                door_loc_0 = { x: location.x - 1, y: location.y + 1, z: location.z + 0 };
                door_loc_1 = { x: location.x + 2, y: location.y + 4, z: location.z + 0 };
                door_spawn_loc = { x: location.x + offset_south_west, y: location.y + 1, z: location.z - 1 };
                rot = "180_degrees";
                door_rotation = "0";
                break;
            }
            case Direction.West: {
                loc_0 = { x: location.x + 1, y: location.y - 1, z: location.z - (x_z_offset - 1) };
                loc_1 = { x: loc_0.x + size_offset.z, y: loc_0.y + size_offset.y, z: loc_0.z + size_offset.x };
                door_loc_0 = { x: location.x + 0, y: location.y + 1, z: location.z - 1 };
                door_loc_1 = { x: location.x + 0, y: location.y + 4, z: location.z + 2 };
                door_spawn_loc = { x: location.x + 1, y: location.y + 1, z: location.z + offset_south_west };
                rot = "270_degrees";
                door_rotation = "90";
                break;
            }
            case Direction.Down: {
                loc_0 = { x: location.x, y: location.y - 1, z: location.z };
                loc_1 = { x: loc_0.x + size_offset.x, y: loc_0.y + size_offset.y, z: loc_0.z + size_offset.z };
                rot = "0_degrees";
                break;
            }
            case Direction.Up: {
                loc_0 = { x: location.x, y: location.y + 1, z: location.z };
                loc_1 = { x: loc_0.x + size_offset.x, y: loc_0.y + size_offset.y, z: loc_0.z + size_offset.z };
                rot = "0_degrees";
                break;
            }
        }
        let original_structure = structure;
        if (structure === "__hub_0") {
            structure = "hub_0";
            loc_0 = location;
            loc_1 = { x: loc_0.x + size_offset.x, y: loc_0.y + size_offset.y, z: loc_0.z + size_offset.z };
        }
        if (this.isHub(structure)) {
            rot = "0_degrees";
        }
        let data = new StructureData(structure);
        data.original_structure = original_structure;
        data.location_0 = loc_0;
        data.location_1 = loc_1;
        data.size = size;
        data.size_offset = size_offset;
        data.rotation = rot;
        data.door_loc_0 = door_loc_0;
        data.door_loc_1 = door_loc_1;
        data.door_spawn_loc = door_spawn_loc;
        data.door_rotation = door_rotation;
        return data;
    }
    boxCoordsString(location_0, location_1) {
        return "x=" + location_0.x.toFixed(2) + ",y=" + location_0.y.toFixed(2) + ",z=" + location_0.z.toFixed(2) + ",dx=" + (location_1.x - location_0.x).toFixed(2) + ",dy=" + (location_1.y - location_0.y).toFixed(2) + ",dz=" + (location_1.z - location_0.z).toFixed(2);
    }
    addStructure(player, location, rotation, structure, display_name, bunker_id = "", structure_cost) {
        if (!FSPlayerPerms.sharedInstance().isAdmin(player)) {
            PlayerHelper.outputAdminAccessRequired(player);
            return;
        }
        let structure_data = this.calculateStructureData(location, rotation, structure);
        if (player.hasTag("sbunk_building")) {
            player.sendMessage({ translate: FSLocalizedString.localize("chat.building.already_building", "You are already building a structure!", "Building already message") });
            return;
        }
        world.playSound("fs.sbunk.construction", location);
        if (this.isRoom(structure) || this.isCorridor(structure)) {
            player.addTag("sbunk_building");
            if (structure_data.door_rotation === "0" || structure_data.door_rotation === "180") {
                let points = FSWorldHelper.pointsBetween(structure_data.door_loc_0.x, structure_data.door_loc_1.x);
                points.forEach(point => {
                    let particle_location = { x: point, y: structure_data.door_loc_0.y, z: structure_data.door_loc_0.z };
                    player.dimension.spawnParticle("fs_sbunk:sbunk_building_structure", particle_location);
                });
            }
            else {
                let points = FSWorldHelper.pointsBetween(structure_data.door_loc_0.z, structure_data.door_loc_1.z);
                points.forEach(point => {
                    let particle_location = { x: structure_data.door_loc_0.x, y: structure_data.door_loc_0.y, z: point };
                    player.dimension.spawnParticle("fs_sbunk:sbunk_building_structure", particle_location);
                });
            }
            system.runTimeout(() => {
                this.addStructureDelayed(player, location, rotation, structure, display_name, bunker_id, structure_cost);
                player.removeTag("sbunk_building");
                system.runTimeout(() => {
                    let eqo = {
                        type: "57d_sbunk:security_door",
                        closest: 1,
                        location: location,
                        maxDistance: 6
                    };
                    const entities = player.dimension.getEntities(eqo);
                    if (entities.length > 0) {
                        SecurityDoor.onInteract(entities[0]); 
                    }
                }, 30);
            }, 30);
        }
        else {
            this.addStructureDelayed(player, location, rotation, structure, display_name, bunker_id, structure_cost);
        }
    }
    addStructureDelayed(player, location, rotation, structure, display_name, bunker_id = "", structure_cost) {
        if (!FSPlayerPerms.sharedInstance().isAdmin(player)) {
            PlayerHelper.outputAdminAccessRequired(player);
            return;
        }
        let structure_data = this.calculateStructureData(location, rotation, structure);
        StructureManager.PopCustomEntitiesToItems(player.dimension, structure_data.location_0, structure_data.location_1);
        system.runTimeout(() => {
            StructureManager.KillCustomEntitiesWithLocs(player.dimension, structure_data.location_0, structure_data.location_1);
            StructureManager.TeleportItemsWithLocs(player, player.dimension, structure_data.location_0, structure_data.location_1);
            StructureManager.TeleportEntitiesWithLocs(player, player.dimension, structure_data.location_0, structure_data.location_1);
        }, 1);
        system.runTimeout(() => {
            const undo_id = this.addUndo(player, structure_data, bunker_id, structure_cost);
            if (this.isCorridorFirst(structure)) {
                FSDynamicProperty.setBoolean(player, "has_built_corridor_0", true);
            }
            else if (this.isCorridorNormal(structure)) {
                FSDynamicProperty.setBoolean(player, "has_built_corridor_1", true);
            }
            else if (this.isRoom(structure)) {
                FSDynamicProperty.setBoolean(player, "has_built_room", true);
            }
            let command_structure = "/structure load 57d_sbunk:" + structure_data.structure + " " + structure_data.location_0.x + " " + structure_data.location_0.y + " " + structure_data.location_0.z + " " + structure_data.rotation + " none true true false";
            let loc_0 = structure_data.location_0;
            let loc_1 = structure_data.location_1;
            let size = structure_data.size_offset;
            let half_height = Math.floor(size.y * 0.5);
            const loc_0_x = loc_0.x.toFixed(2);
            const loc_0_y = loc_0.y.toFixed(2);
            const loc_0_z = loc_0.z.toFixed(2);
            const loc_1_x = loc_1.x.toFixed(2);
            const loc_1_y = loc_1.y.toFixed(2);
            const loc_1_z = loc_1.z.toFixed(2);
            const loc_0_h = (loc_0.y + half_height - 1).toFixed(2);
            const loc_1_h = (loc_1.y - half_height + 1).toFixed(2);
            const cmd_fill_0 = `/fill ${loc_0_x} ${loc_0_y} ${loc_0_z} ${loc_1_x} ${loc_1_h} ${loc_1_z} bedrock`;
            const cmd_fill_1 = `/fill ${loc_0_x} ${loc_0_h} ${loc_0_z} ${loc_1_x} ${loc_1_y} ${loc_1_z} bedrock`;
            player.runCommandAsync(cmd_fill_0).then((commandResult) => {
                if (commandResult.successCount === 0) {
                    player.sendMessage({ translate: FSLocalizedString.localize("chat.structure.failed_fill.v1.line_1", "§cError: §rUnable to perform that action at this time.", "Failed to fill void 0 message") });
                    player.sendMessage({ translate: FSLocalizedString.localize("chat.structure.failed_fill.v1.line_2", "§aPossible reasons: §rYou are trying to build into an invalid location such as the void.", "Possible reasons message") });
                    player.playSound("fs.sbunk.chat_ping");
                    StructureManager.Undo(player);
                }
                else {
                    player.runCommandAsync(cmd_fill_1).then((commandResult) => {
                        if (commandResult.successCount === 0) {
                            player.sendMessage({ translate: FSLocalizedString.localize("chat.structure.failed_fill.v2.line_1", "§cError: §rUnable to perform that action at this time.", "Failed to fill void 0 message") });
                            player.sendMessage({ translate: FSLocalizedString.localize("chat.structure.failed_fill.v2.line_2", "§aPossible reasons: §rYou are trying to build into an invalid location such as the void.", "Possible reasons message") });
                            player.playSound("fs.sbunk.chat_ping");
                            StructureManager.Undo(player);
                        }
                        else {
                            StructureManager.KillCustomEntitiesWithLocs(player.dimension, structure_data.location_0, structure_data.location_1);
                            player.runCommandAsync(command_structure).then((commandResult) => {
                                if (commandResult.successCount === 0) {
                                    player.sendMessage({ translate: FSLocalizedString.localize("chat.structure.failed_fill.v3.line_1", "§cError: §rUnable to perform that action at this time.", "Failed to fill void 0 message") });
                                    player.sendMessage({ translate: FSLocalizedString.localize("chat.structure.failed_fill.v3.line_2", "§aPossible reasons: §rYou are trying to build into an invalid location such as the void.", "Possible reasons message") });
                                    player.playSound("fs.sbunk.chat_ping");
                                    StructureManager.Undo(player);
                                }
                                else {
                                    system.runTimeout(() => {
                                        StructureManager.KillItemsWithLocs(player.dimension, structure_data.location_0, structure_data.location_1);
                                        const box_coords = StructureManager.sharedInstance().boxCoordsString(structure_data.location_0, structure_data.location_1);
                                        let cmd_despawn = `/tag @e[${box_coords}] add ${undo_id}`;
                                        player.runCommand(cmd_despawn);
                                    }, 1);
                                    let open_door = this.isRoom(structure_data.structure) || this.isCorridor(structure_data.structure);
                                    if (open_door) {
                                        FSWorldHelper.fillBlocks(player.dimension, structure_data.door_loc_0, structure_data.door_loc_1, "minecraft:air");
                                        if (this.isRoom(structure_data.structure) && structure_data.door_rotation) {
                                            let spawn_event = "fs:set_normal_hitbox";
                                            if (structure_data.door_rotation === "90" || structure_data.door_rotation === "270") {
                                                spawn_event = "fs:set_rotated_hitbox";
                                            }
                                            display_name = display_name + "\nRoom ID: " + undo_id; 
                                            const command = `/summon 57d_sbunk:security_door ${structure_data.door_spawn_loc.x} ${structure_data.door_spawn_loc.y} ${structure_data.door_spawn_loc.z} ${structure_data.door_rotation} 0 ${spawn_event} "${display_name}"`;
                                            player.dimension.runCommand(command);
                                        }
                                    }
                                    if (this.isHub(structure_data.structure)) {
                                        const msg = {
                                            rawtext: [
                                                { text: "§g[!] " },
                                                {
                                                    translate: FSLocalizedString.localize("chat.structure.new_floor_added", "A new floor has been added!", "New floor added message")
                                                }
                                            ]
                                        };
                                        player.sendMessage(msg);
                                        player.playSound("fs.sbunk.chat_ping");
                                    }
                                    this.replaceStructureEntityPlaceholders(player);
                                    const shouldAllowOpElementsInCreative = FSPlayer.isCreative(player) && FSDynamicProperty.getBoolean(world, "op_chests_in_creative");
                                    const shouldAllowOpElementsInSurvival = !FSPlayer.isCreative(player) && FSDynamicProperty.getBoolean(world, "op_chests_in_survival");
                                    if (!shouldAllowOpElementsInCreative && !shouldAllowOpElementsInSurvival) {
                                        this.removeOverpoweredElements(player, structure_data);
                                    }
                                }
                            });
                        }
                    });
                }
            });
        }, 2);
    }
    removeOverpoweredElements(player, structure_data) {
        let loc_0 = structure_data.location_0;
        let loc_1 = structure_data.location_1;
        const loc_0_x = loc_0.x.toFixed(2);
        const loc_0_y = loc_0.y.toFixed(2);
        const loc_0_z = loc_0.z.toFixed(2);
        const loc_1_x = loc_1.x.toFixed(2);
        const loc_1_y = loc_1.y.toFixed(2);
        const loc_1_z = loc_1.z.toFixed(2);
        const coords = `${loc_0_x} ${loc_0_y} ${loc_0_z} ${loc_1_x} ${loc_1_y} ${loc_1_z}`;
        const command_remove_chests_north = `/fill ${coords} 57d_sbunk:placeholder_chest_north [] replace chest ["minecraft:cardinal_direction":"north"]`;
        const command_remove_chests_east = `/fill ${coords} 57d_sbunk:placeholder_chest_east [] replace chest ["minecraft:cardinal_direction":"east"]`;
        const command_remove_chests_south = `/fill ${coords} 57d_sbunk:placeholder_chest_south [] replace chest ["minecraft:cardinal_direction":"south"]`;
        const command_remove_chests_west = `/fill ${coords} 57d_sbunk:placeholder_chest_west [] replace chest ["minecraft:cardinal_direction":"west"]`;
        const command_add_chests_north = `/fill ${coords} chest ["minecraft:cardinal_direction":"north"] replace 57d_sbunk:placeholder_chest_north []`;
        const command_add_chests_east = `/fill ${coords} chest ["minecraft:cardinal_direction":"east"] replace 57d_sbunk:placeholder_chest_east []`;
        const command_add_chests_south = `/fill ${coords} chest ["minecraft:cardinal_direction":"south"] replace 57d_sbunk:placeholder_chest_south []`;
        const command_add_chests_west = `/fill ${coords} chest ["minecraft:cardinal_direction":"west"] replace 57d_sbunk:placeholder_chest_west []`;
        player.runCommand(command_remove_chests_north);
        player.runCommand(command_remove_chests_east);
        player.runCommand(command_remove_chests_south);
        player.runCommand(command_remove_chests_west);
        player.runCommand(command_add_chests_north);
        player.runCommand(command_add_chests_east);
        player.runCommand(command_add_chests_south);
        player.runCommand(command_add_chests_west);
        const command_remove_ender_chests_north = `/fill ${coords} 57d_sbunk:placeholder_ender_chest_north [] replace ender_chest ["minecraft:cardinal_direction":"north"]`;
        const command_remove_ender_chests_east = `/fill ${coords} 57d_sbunk:placeholder_ender_chest_east [] replace ender_chest ["minecraft:cardinal_direction":"east"]`;
        const command_remove_ender_chests_south = `/fill ${coords} 57d_sbunk:placeholder_ender_chest_south [] replace ender_chest ["minecraft:cardinal_direction":"south"]`;
        const command_remove_ender_chests_west = `/fill ${coords} 57d_sbunk:placeholder_ender_chest_west [] replace ender_chest ["minecraft:cardinal_direction":"west"]`;
        const command_add_ender_chests_north = `/fill ${coords} ender_chest ["minecraft:cardinal_direction":"north"] replace 57d_sbunk:placeholder_ender_chest_north []`;
        const command_add_ender_chests_east = `/fill ${coords} ender_chest ["minecraft:cardinal_direction":"east"] replace 57d_sbunk:placeholder_ender_chest_east []`;
        const command_add_ender_chests_south = `/fill ${coords} ender_chest ["minecraft:cardinal_direction":"south"] replace 57d_sbunk:placeholder_ender_chest_south []`;
        const command_add_ender_chests_west = `/fill ${coords} ender_chest ["minecraft:cardinal_direction":"west"] replace 57d_sbunk:placeholder_ender_chest_west []`;
        player.runCommand(command_remove_ender_chests_north);
        player.runCommand(command_remove_ender_chests_east);
        player.runCommand(command_remove_ender_chests_south);
        player.runCommand(command_remove_ender_chests_west);
        player.runCommand(command_add_ender_chests_north);
        player.runCommand(command_add_ender_chests_east);
        player.runCommand(command_add_ender_chests_south);
        player.runCommand(command_add_ender_chests_west);
        const command_remove_trapped_chests_north = `/fill ${coords} 57d_sbunk:placeholder_trapped_chest_north [] replace trapped_chest ["minecraft:cardinal_direction":"north"]`;
        const command_remove_trapped_chests_east = `/fill ${coords} 57d_sbunk:placeholder_trapped_chest_east [] replace trapped_chest ["minecraft:cardinal_direction":"east"]`;
        const command_remove_trapped_chests_south = `/fill ${coords} 57d_sbunk:placeholder_trapped_chest_south [] replace trapped_chest ["minecraft:cardinal_direction":"south"]`;
        const command_remove_trapped_chests_west = `/fill ${coords} 57d_sbunk:placeholder_trapped_chest_west [] replace trapped_chest ["minecraft:cardinal_direction":"west"]`;
        const command_add_trapped_chests_north = `/fill ${coords} trapped_chest ["minecraft:cardinal_direction":"north"] replace 57d_sbunk:placeholder_trapped_chest_north []`;
        const command_add_trapped_chests_east = `/fill ${coords} trapped_chest ["minecraft:cardinal_direction":"east"] replace 57d_sbunk:placeholder_trapped_chest_east []`;
        const command_add_trapped_chests_south = `/fill ${coords} trapped_chest ["minecraft:cardinal_direction":"south"] replace 57d_sbunk:placeholder_trapped_chest_south []`;
        const command_add_trapped_chests_west = `/fill ${coords} trapped_chest ["minecraft:cardinal_direction":"west"] replace 57d_sbunk:placeholder_trapped_chest_west []`;
        player.runCommand(command_remove_trapped_chests_north);
        player.runCommand(command_remove_trapped_chests_east);
        player.runCommand(command_remove_trapped_chests_south);
        player.runCommand(command_remove_trapped_chests_west);
        player.runCommand(command_add_trapped_chests_north);
        player.runCommand(command_add_trapped_chests_east);
        player.runCommand(command_add_trapped_chests_south);
        player.runCommand(command_add_trapped_chests_west);
        const command_remove_frame_north = `/fill ${coords} glow_frame ["facing_direction":2] replace frame ["facing_direction":2]`;
        const command_remove_frame_east = `/fill ${coords} glow_frame ["facing_direction":3] replace frame ["facing_direction":3]`;
        const command_remove_frame_south = `/fill ${coords} glow_frame ["facing_direction":4] replace frame ["facing_direction":4]`;
        const command_remove_frame_west = `/fill ${coords} glow_frame ["facing_direction":5] replace frame ["facing_direction":5]`;
        const command_add_frame_north = `/fill ${coords} frame ["facing_direction":2] replace glow_frame ["facing_direction":2]`;
        const command_add_frame_east = `/fill ${coords} frame ["facing_direction":3] replace glow_frame ["facing_direction":3]`;
        const command_add_frame_south = `/fill ${coords} frame ["facing_direction":4] replace glow_frame ["facing_direction":4]`;
        const command_add_frame_west = `/fill ${coords} frame ["facing_direction":5] replace glow_frame ["facing_direction":5]`;
        player.runCommand(command_remove_frame_north);
        player.runCommand(command_remove_frame_east);
        player.runCommand(command_remove_frame_south);
        player.runCommand(command_remove_frame_west);
        player.runCommand(command_add_frame_north);
        player.runCommand(command_add_frame_east);
        player.runCommand(command_add_frame_south);
        player.runCommand(command_add_frame_west);
        const box_coords = StructureManager.sharedInstance().boxCoordsString(structure_data.location_0, structure_data.location_1);
        const should_remove_tag = FSUtil.namespacedStringCr("should_remove");
        player.runCommand(`/tag @e[type=armor_stand,${box_coords}] add ${should_remove_tag}`);
        player.runCommand(`/execute as @e[type=armor_stand,${box_coords},tag=${should_remove_tag}] at @s run summon armor_stand ~ ~ ~ ~ ~`);
        player.runCommandAsync(`/kill @e[type=armor_stand,${box_coords},tag=${should_remove_tag}]`).then((commandResult) => {
            player.runCommand(`/kill @e[type=item,${box_coords}]`);
        });
    }
    isBuilding(structureName) {
        return structureName.indexOf("building_") > -1;
    }
    isEntrance(structureName) {
        return structureName.indexOf("entrance_") > -1;
    }
    isRoom(structureName) {
        return structureName.indexOf("room_") > -1;
    }
    isHub(structureName) {
        return structureName.indexOf("hub_") > -1;
    }
    isFirstHub(structureName) {
        return structureName.indexOf("__hub_") > -1;
    }
    isCorridor(structureName) {
        return structureName.indexOf("corridor_") > -1;
    }
    isCorridorFirst(structureName) {
        return structureName.indexOf("corridor_0") > -1;
    }
    isCorridorNormal(structureName) {
        return structureName.indexOf("corridor_1") > -1;
    }
    replaceStructureEntityPlaceholders(player) {
        let eqo = {
            type: "minecraft:armor_stand"
        };
        let dimenion = player.dimension;
        const entities = dimenion.getEntities(eqo);
        entities.forEach(entity => {
            if (entity.nameTag.indexOf(":")) {
                let segments = entity.nameTag.split(":");
                let entityType = "";
                let spawnEvent = "";
                if (segments.length === 2) {
                    if (segments[0] === "fs") {
                        entityType = "57d_sbunk:" + segments[1];
                    }
                    else if (segments[0] === "jig_ccomp") {
                        entityType = segments[0] + ":" + segments[1];
                    }
                    else {
                        entityType = "minecraft:" + segments[0];
                        spawnEvent = "minecraft:" + segments[1];
                    }
                }
                else if (segments.length === 3) {
                    if (segments[0] === "fs") {
                        entityType = "57d_sbunk:" + segments[1];
                    }
                    spawnEvent = "fs:" + segments[2];
                }
                if (entityType.length > 0) {
                    try {
                        const new_entity = dimenion.spawnEntity(entityType, entity.location);
                        new_entity.setRotation(entity.getRotation());
                        new_entity.teleport(new_entity.location);
                        new_entity.addTag("57d_sbunk");
                        if (segments[1] === "entrance_tree" || segments[1] === "entrance_chest") {
                            new_entity.teleport({ x: new_entity.location.x, y: new_entity.location.y + 1, z: new_entity.location.z });
                        }
                        if (spawnEvent.length > 0) {
                            new_entity.triggerEvent(spawnEvent);
                        }
                    }
                    catch (error) {
                    }
                    finally {
                        let location = EntityManager.getLocationOffsetByRotation(entity, 0, -1, 0);
                        FSWorldHelper.replaceBlocks(dimenion, location, location, "minecraft:air", "minecraft:barrier");
                        entity.remove();
                    }
                }
            }
        });
    }
    structureCorner0(location0, location1) {
        const x = location0.x < location1.x ? location0.x : location1.x;
        const y = location0.y < location1.y ? location0.y : location1.y;
        const z = location0.z < location1.z ? location0.z : location1.z;
        return { x: x, y: y, z: z };
    }
    structureCorner1(location0, location1) {
        const x = location0.x > location1.x ? location0.x : location1.x;
        const y = location0.y > location1.y ? location0.y : location1.y;
        const z = location0.z > location1.z ? location0.z : location1.z;
        return { x: x, y: y, z: z };
    }
    static KillCustomEntitiesWithLocs(dimension, loc0, loc1) {
        const box_coords = StructureManager.sharedInstance().boxCoordsString(loc0, loc1);
        let cmd_despawn = `/event entity @e[family=57d_sbunk,${box_coords}] fs:instant_despawn`;
        dimension.runCommand(cmd_despawn);
        cmd_despawn = `/kill @e[type=!player,family=!57d_sbunk,tag=57d_sbunk,${box_coords}]`;
        dimension.runCommand(cmd_despawn);
    }
    static KillCustomEntitiesWithLocsWithUndoId(dimension, undoId) {
        let cmd_despawn = `/event entity @e[tag=${undoId}] fs:instant_despawn`;
        dimension.runCommand(cmd_despawn);
        let cmd_kill = `/kill @e[tag=${undoId}]`;
        dimension.runCommand(cmd_kill);
    }
    static PopCustomEntitiesToItems(dimension, loc0, loc1) {
        const box_coords = StructureManager.sharedInstance().boxCoordsString(loc0, loc1);
        dimension.runCommand(`/event entity @e[family=57d_sbunk,${box_coords}] fs:pop_to_item`);
    }
    static TeleportEntitiesWithLocs(player, dimension, loc0, loc1) {
        const box_coords = StructureManager.sharedInstance().boxCoordsString(loc0, loc1);
        const max_distance_behind = 6;
        let did_teleport = false;
        for (let i = max_distance_behind; i >= 0; i--) {
            const block_behind = dimension.getBlock(EntityManager.getLocationOffsetByRotation(player, 0, 0, -i));
            if (block_behind?.permutation.matches("minecraft:air")) {
                const result = dimension.runCommand(`/tp @e[type=!painting,type=!armor_stand,family=!monster,family=!57d_sbunk,${box_coords}] ${block_behind.x} ${block_behind.y} ${block_behind.z}`);
                if (result.successCount > 0) {
                    did_teleport = true;
                }
                return;
            }
        }
        if (!did_teleport) {
            dimension.runCommand(`/tp @e[type=item,${box_coords}] ${player.location.x} ${player.location.y} ${player.location.z}`);
        }
    }
    static TeleportItemsWithLocs(player, dimenion, loc0, loc1) {
        const box_coords = StructureManager.sharedInstance().boxCoordsString(loc0, loc1);
        const max_distance_behind = 1;
        let did_teleport = false;
        for (let i = max_distance_behind; i > 0; i--) {
            const block_behind = dimenion.getBlock(EntityManager.getLocationOffsetByRotation(player, 0, 0, -i));
            if (block_behind?.permutation.matches("minecraft:air")) {
                const result = dimenion.runCommand(`/tp @e[type=item,${box_coords}] ${block_behind.x} ${block_behind.y} ${block_behind.z}`);
                if (result.successCount > 0) {
                    did_teleport = true;
                }
                return;
            }
        }
        if (!did_teleport) {
            dimenion.runCommand(`/tp @e[type=item,${box_coords}] ${player.location.x} ${player.location.y} ${player.location.z}`);
        }
    }
    static KillEntitiesWithLocs(dimenion, loc0, loc1) {
        const box_coords = StructureManager.sharedInstance().boxCoordsString(loc0, loc1);
        let cmd_despawn = `/event entity @e[family=!57d_sbunk,${box_coords}] fs:instant_despawn`;
        dimenion.runCommand(cmd_despawn);
        cmd_despawn = `/kill @e[type=!player,family=!57d_sbunk,${box_coords}]`;
        dimenion.runCommand(cmd_despawn);
    }
    static KillItemsWithLocs(dimenion, loc0, loc1) {
        const box_coords = StructureManager.sharedInstance().boxCoordsString(loc0, loc1);
        let cmd_despawn = `/event entity @e[type=item,${box_coords}] fs:instant_despawn`;
        dimenion.runCommand(cmd_despawn);
        cmd_despawn = `/kill @e[type=item,${box_coords}]`;
        dimenion.runCommand(cmd_despawn);
    }
    addEntrance(player, location, rotation, type, structure_cost) {
        if (!FSPlayerPerms.sharedInstance().isAdmin(player)) {
            PlayerHelper.outputAdminAccessRequired(player);
            return;
        }
        const hasBuiltBunker = FSDynamicProperty.getBoolean(world, FSUtil.namespacedString("hasBuiltBunker")) || false;
        FSDynamicProperty.setString(world, "lastBuiltBunkerType", type || ""); 
        if (!hasBuiltBunker) {
            FSDynamicProperty.setBoolean(world, FSUtil.namespacedString("shouldSuppressNewCameraNotifications"), true); 
            system.runTimeout(() => {
                world.getPlayers().forEach(player => {
                    player.sendMessage({ translate: FSLocalizedString.localize("chat.bunker.first_built", "§b[!] Congratulations on building your first bunker!", "First bunker built message") });
                    player.playSound("fs.sbunk.success");
                    system.runTimeout(() => {
                        this.showAccessInstructionsForLastBuiltEntrance();
                    }, 80);
                });
            }, 60);
        }
        else {
            system.runTimeout(() => {
                this.showAccessInstructionsForLastBuiltEntrance(); 
            }, 60);
        }
        const bunker_id = FSUtil.shortId();
        this.addStructure(player, { x: location.x, y: location.y, z: location.z }, rotation, type, "", bunker_id, structure_cost);
        location = { x: location.x, y: location.y - 9, z: location.z };
        this.addStructure(player, location, rotation, "entrance_0", "", bunker_id, structure_cost);
        switch (rotation) {
            case Direction.North: {
                location = { x: location.x, y: location.y, z: location.z + 9 };
                break;
            }
            case Direction.East: {
                location = { x: location.x - 8, y: location.y, z: location.z };
                break;
            }
            case Direction.South: {
                location = { x: location.x + 1, y: location.y, z: location.z - 8 };
                break;
            }
            case Direction.West: {
                location = { x: location.x + 9, y: location.y, z: location.z + 1 };
                break;
            }
        }
        FSDynamicProperty.setBoolean(world, FSUtil.namespacedString("hasBuiltBunker"), true);
        location = { x: location.x - 24, y: location.y - 16, z: location.z - 24 };
        this.addStructure(player, location, rotation, "__hub_0", "", bunker_id, structure_cost);
    }
    showAccessInstructionsForLastBuiltEntrance() {
        const lastBuiltBunkerType = FSDynamicProperty.getString(world, "lastBuiltBunkerType") || "";
        if (lastBuiltBunkerType.length >= 1) {
            const accessInstructions = getEntranceInstructionsForType(lastBuiltBunkerType); 
            if (accessInstructions.length >= 1) {
                world.getPlayers().forEach(player => {
                    const rawText = {
                        rawtext: [
                            {
                                text: "§e[!] "
                            },
                            {
                                translate: FSLocalizedString.localize("chat.access_instructions", "Access Instructions: ", "Access Instructions message")
                            },
                            {
                                translate: accessInstructions
                            }
                        ]
                    };
                    player.sendMessage(rawText);
                    player.playSound("fs.sbunk.chat_alert");
                });
            }
        }
    }
    addCorridor(player, location, rotation, structure, structure_cost) {
        this.addStructure(player, location, rotation, structure, "", "", structure_cost);
    }
    showEntranceDialogue(player) {
        const form = new mcui.ActionFormData();
        form.title({ translate: FSLocalizedString.localize("chat.entrance_dialogue.title", "Bunker Entrance", "Bunker Entrance title") });
        form.body({ translate: FSLocalizedString.localize("chat.entrance_dialogue.body", "Select the type of entrance you would like to build.", "Bunker Entrance body") });
        form.button({ translate: "57d_sbunk.entrance_name.hut" }, "textures/57digital/sbunk/icons/bunker_type_0.png");
        form.button({ translate: "57d_sbunk.entrance_name.cave" }, "textures/57digital/sbunk/icons/bunker_type_1.png");
        form.button({ translate: "57d_sbunk.entrance_name.tree" }, "textures/57digital/sbunk/icons/bunker_type_2.png");
        form.button({ translate: "57d_sbunk.entrance_name.boulder" }, "textures/57digital/sbunk/icons/bunker_type_3.png");
        form.button({ translate: "57d_sbunk.entrance_name.bunker" }, "textures/57digital/sbunk/icons/bunker_type_4.png");
        form.button({ translate: "57d_sbunk.entrance_name.bed" }, "textures/57digital/sbunk/icons/bunker_type_5.png");
        form.button({ translate: "57d_sbunk.entrance_name.chest" }, "textures/57digital/sbunk/icons/bunker_type_6.png");
        form.button({ translate: FSLocalizedString.localize("chat.entrance_dialogue.cancel", "Cancel", "Cancel button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            let selection = resp.selection || 0;
            if (selection < 7) {
                this.showEntranceConfirmationForm(player, player.location, PlayerHelper.getInvertedDirection(player), "entrance_" + selection);
            }
        });
    }
    showEntranceConfirmationForm(player, location, rotation, type) {
        const form = new FSActionFormData("build_bunker_entrance");
        const title = getEntranceNameForType(type.replace("entrance_", "building_"));
        form.setTitle({ translate: title });
        const entranceInfo = getEntranceInstructionsForType(type.replace("entrance_", "building_")); 
        let rawMessage = {
            rawtext: []
        };
        const bunkerCostInChips = BUNKER_COST_CHIPS;
        const currentChipAmount = FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0;
        let message = "";
        if (entranceInfo.length >= 1) {
            rawMessage.rawtext.push({
                text: "§e"
            });
            rawMessage.rawtext.push({
                translate: FSLocalizedString.localize("ui.entrance.instructions", "Entrance Instructions", "Entrance instructions which tell the player how they can enter the secret bunker")
            });
            rawMessage.rawtext.push({
                text: ": "
            });
            rawMessage.rawtext.push({
                translate: entranceInfo
            });
            rawMessage.rawtext.push({
                text: "§r\n\n"
            });
        }
        rawMessage.rawtext.push({
            text: "§c"
        });
        rawMessage.rawtext.push({
            translate: FSLocalizedString.localize("ui.entrance.warning", "[!] Place your bunker carefully, as it will replace any blocks beneath its location. When you hit the `Build Now` button, it will be constructed in front of your current position. If the position isn`t quite right you can hit the `Undo Last Action` button in your tablet.", "Warning message")
        });
        rawMessage.rawtext.push({
            text: "§r\n\n"
        });
        if (!FSPlayer.isCreative(player)) {
            rawMessage.rawtext.push({
                text: "§b"
            });
            rawMessage.rawtext.push({
                translate: FSLocalizedString.localize("ui.entrance.cost", "Cost: %s Security Chip(s). You currently have %s Security Chips(s).", "Cost of building the bunker"),
                with: [bunkerCostInChips.toString(), `${currentChipAmount}`]
            });
            rawMessage.rawtext.push({
                text: "\n\n§r"
            });
        }
        else {
            rawMessage.rawtext.push({
                text: "§b"
            });
            rawMessage.rawtext.push({
                translate: FSLocalizedString.localize("ui.entrance.free_creative", "This bunker is free in Creative Mode.", "Cost of building the bunker")
            });
            rawMessage.rawtext.push({
                text: "\n\n§r"
            });
        }
        rawMessage.rawtext.push({
            translate: FSLocalizedString.localize("ui.entrance.confirm", "Are you sure you want to build this bunker?", "Ask player if they want to build the bunker now")
        });
        rawMessage.rawtext.push({
            text: "\n\n"
        });
        form.setBody(rawMessage);
        var rawButtonBuildNow = {
            rawtext: [
                {
                    text: "§l"
                },
                {
                    translate: FSLocalizedString.localize("ui.entrance.build_now", "Build Now", "Button to build the bunker")
                },
                {
                    text: "\n§r§8"
                }
            ]
        };
        if (!FSPlayer.isCreative(player)) {
            rawButtonBuildNow.rawtext.push({
                translate: FSLocalizedString.localize("ui.entrance.button.cost_chips", "[Cost: %s Security Chips]", "Cost of building the bunker"),
                with: [bunkerCostInChips.toString()]
            });
        }
        else {
            rawButtonBuildNow.rawtext.push({
                translate: FSLocalizedString.localize("ui.entrance.button.free_creative", "[Free in Creative Mode]", "Cost of building the bunker")
            });
        }
        form.addButton("build_now", rawButtonBuildNow, "textures/57digital/sbunk/icons/build_now.png");
        form.addButton("buy_security_chips", FSLocalizedString.localize("ui.entrance.buy_chips", "Buy Security Chips >", "Button to buy security chips"), "textures/57digital/sbunk/items/security_chip.png");
        const rawButtonGoBack = {
            rawtext: [
                {
                    text: "< "
                },
                {
                    translate: FSLocalizedString.localize("ui.entrance.go_back", "Go Back", "Button to go back to the previous UI")
                }
            ]
        };
        form.addButton("go_back", rawButtonGoBack);
        form.push(player, false, resp => {
            if (resp.canceled) {
                return;
            }
            if (resp instanceof FSActionFormResponse) {
                const formResponseData = resp.getResponseData();
                if (formResponseData["go_back"]) {
                    this.showEntranceDialogue(player);
                }
                else if (formResponseData["build_now"]) {
                    const bunkerCostInChips = BUNKER_COST_CHIPS;
                    const currentChipAmount = FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0;
                    const canAfford = currentChipAmount >= bunkerCostInChips;
                    if (canAfford || FSPlayer.isCreative(player)) {
                        if (!FSPlayer.isCreative(player)) {
                            if (FSInventoryManager.takeItem(player, "57d_sbunk:security_chip", bunkerCostInChips)) {
                                this.addEntrance(player, location, rotation, type.replace("entrance_", "building_"), bunkerCostInChips);
                            }
                            else {
                                world.sendMessage({ translate: FSLocalizedString.localize("chat.bunker.cannot_afford.message", "§c[!] You do not have enough Security Chips to do that.", "Cannot Afford Bunker") });
                            }
                        }
                        else {
                            this.addEntrance(player, location, rotation, type.replace("entrance_", "building_"), 0);
                        }
                    }
                    else {
                        this.showCanNotAffordBunkerDialogue(player);
                    }
                }
                else if (formResponseData["buy_security_chips"]) {
                    FormManager.sharedInstance().showBuySecurityChips(player);
                }
            }
        });
    }
    showCanNotAffordBunkerDialogue(player, cost = BUNKER_COST_CHIPS) {
        player.playSound("fs.sbunk.chat_alert");
        const shortfall = cost - (FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0);
        const form = new mcui.MessageFormData();
        form.title({ translate: FSLocalizedString.localize("chat.bunker.cannot_afford.title", "Cannot Afford Bunker", "Cannot Afford Bunker title") });
        form.body({
            translate: FSLocalizedString.localize("chat.bunker.cannot_afford.body", "§cYou do not have enough Security Chips to build this bunker. You can purchase more Security Chips in the shop. You are short by %1 Security Chip(s).", "Cannot Afford Bunker body"),
            with: [shortfall.toString()]
        });
        form.button1({ translate: FSLocalizedString.localize("chat.bunker.cannot_afford.ok", "OK", "OK button") });
        form.button2({ translate: FSLocalizedString.localize("chat.bunker.cannot_afford.buy_chips", "Buy Security Chips", "Buy Security Chips button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            if (resp.selection === 1) {
                FormManager.sharedInstance().showBuySecurityChips(player);
            }
        });
    }
    showCanNotAffordRoomDialogue(player, cost = BUNKER_COST_CHIPS) {
        player.playSound("fs.sbunk.chat_alert");
        const shortfall = cost - (FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0);
        const form = new mcui.MessageFormData();
        form.title({ translate: FSLocalizedString.localize("chat.room.cannot_afford.title", "Cannot Afford Room", "Cannot Afford Room title") });
        form.body({
            translate: FSLocalizedString.localize("chat.room.cannot_afford.body", "§cYou do not have enough Security Chips to build this room. You can purchase more Security Chips in the shop. You are short by %1 Security Chip(s).", "Cannot Afford Room body"),
            with: [shortfall.toString()]
        });
        form.button1({ translate: FSLocalizedString.localize("chat.room.cannot_afford.ok", "OK", "OK button") });
        form.button2({ translate: FSLocalizedString.localize("chat.room.cannot_afford.buy_chips", "Buy Security Chips", "Buy Security Chips button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            if (resp.selection === 1) {
                FormManager.sharedInstance().showBuySecurityChips(player);
            }
        });
    }
    showCanNotAffordFloorDialogue(player, cost = BUNKER_COST_CHIPS) {
        player.playSound("fs.sbunk.chat_alert");
        const shortfall = cost - (FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0);
        const form = new mcui.MessageFormData();
        form.title({ translate: FSLocalizedString.localize("chat.floor.cannot_afford.title", "Cannot Afford Floor", "Cannot Afford Floor title") });
        form.body({
            translate: FSLocalizedString.localize("chat.floor.cannot_afford.body", "§cYou do not have enough Security Chips to build a new floor. You can purchase more Security Chips in the shop. You are short by %1 Security Chip(s).", "Cannot Afford Floor body"),
            with: [shortfall.toString()]
        });
        form.button1({ translate: FSLocalizedString.localize("chat.floor.cannot_afford.ok", "OK", "OK button") });
        form.button2({ translate: FSLocalizedString.localize("chat.floor.cannot_afford.buy_chips", "Buy Security Chips", "Buy Security Chips button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            if (resp.selection === 1) {
                FormManager.sharedInstance().showBuySecurityChips(player);
            }
        });
    }
    showCanNotAffordCorridorDialogue(player, cost = BUNKER_COST_CHIPS) {
        player.playSound("fs.sbunk.chat_alert");
        const shortfall = cost - (FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0);
        const form = new mcui.MessageFormData();
        form.title({ translate: FSLocalizedString.localize("chat.corridor.cannot_afford.title", "Cannot Afford Corridor", "Cannot Afford Corridor title") });
        form.body({
            translate: FSLocalizedString.localize("chat.corridor.cannot_afford.body", "§cYou do not have enough Security Chips to expand this corridor. You can purchase more Security Chips in the shop. You are short by %1 Security Chip(s).", "Cannot Afford Corridor body"),
            with: [shortfall.toString()]
        });
        form.button1({ translate: FSLocalizedString.localize("chat.corridor.cannot_afford.ok", "OK", "OK button") });
        form.button2({ translate: FSLocalizedString.localize("chat.corridor.cannot_afford.buy_chips", "Buy Security Chips", "Buy Security Chips button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            if (resp.selection === 1) {
                FormManager.sharedInstance().showBuySecurityChips(player);
            }
        });
    }
    showCanNotAffordDialogue(player, cost = BUNKER_COST_CHIPS) {
        player.playSound("fs.sbunk.chat_alert");
        const shortfall = cost - (FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0);
        const form = new mcui.MessageFormData();
        form.title({ translate: FSLocalizedString.localize("chat.general.cannot_afford.title", "Cannot Afford", "Cannot Afford Structure title") });
        form.body({
            translate: FSLocalizedString.localize("chat.general.cannot_afford.body", "§cYou do not have enough Security Chips to build this. You can purchase more Security Chips in the shop. You are short by %1 Security Chip(s).", "Cannot Afford Structure body"),
            with: [shortfall.toString()]
        });
        form.button1({ translate: FSLocalizedString.localize("chat.general.cannot_afford.ok", "OK", "OK button") });
        form.button2({ translate: FSLocalizedString.localize("chat.general.cannot_afford.buy_chips", "Buy Security Chips", "Buy Security Chips button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            if (resp.selection === 1) {
                FormManager.sharedInstance().showBuySecurityChips(player);
            }
        });
    }
    displayRoomPickerForm(player, location, rotation, category) {
        const form = new mcui.ActionFormData();
        let categoryName = (category.name || "Select Room").replace(/>/g, "").trim();
        form.title({ translate: categoryName });
        form.body({ translate: FSLocalizedString.localize("chat.room_picker.body", "Pick a room type, or go back to the previous menu to select a different category.", "Room Type body") });
        category.structures.sort((a, b) => {
            return a.name.localeCompare(b.name);
        });
        category.structures.forEach(structure => {
            const quirk = structure.quirk || "";
            var rawText = {
                rawtext: [{ translate: structure.name }, { text: " >" }]
            };
            if (quirk.length >= 1) {
                rawText.rawtext.push({ text: "\n" });
                rawText.rawtext.push({ text: "§e" });
                rawText.rawtext.push({ translate: quirk });
            }
            form.button(rawText);
        });
        form.button({
            rawtext: [{ text: "< " }, { translate: FSLocalizedString.localize("chat.room_picker.go_back", "Go Back", "Go Back button") }]
        }); 
        form.show(player).then(resp => {
            if (resp.canceled) {
                this.displayRoomTypeForm(player, location, rotation); 
                return;
            }
            let selection = resp.selection || 0;
            if (selection < category.structures.length) {
                let structure = category.structures[selection];
                this.displayRoomConfirmationForm(player, location, rotation, structure.structure, structure.name_en, category);
            }
            else {
                this.displayRoomTypeForm(player, location, rotation);
            }
        });
    }
    displayRoomConfirmationForm(player, location, rotation, structureId, stuctureName, currentCategory) {
        const structureData = getStructureDataForStructureId(structureId);
        if (!structureData) {
            player.sendMessage({ translate: FSLocalizedString.localize("chat.room_picker.failed", "Failed to find structure data for: ", "Failed to find structure data message. Prefix.") } + structureId);
            return;
        }
        const roomDescription = structureData.description || "";
        const quirk = (structureData.quirk || "").replace("(", "(Special Feature: ");
        const costInChips = structureData.cost || 0;
        const currentChipAmount = FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0;
        const rawTextBody = {
            rawtext: []
        };
        rawTextBody.rawtext.push({ text: "§i" });
        rawTextBody.rawtext.push({ translate: roomDescription });
        rawTextBody.rawtext.push({ text: "§r\n\n" });
        if (quirk.length >= 1) {
            rawTextBody.rawtext.push({ text: "§e" });
            rawTextBody.rawtext.push({ translate: quirk });
            rawTextBody.rawtext.push({ text: "\n\n" });
        }
        if (!FSPlayer.isCreative(player)) {
            rawTextBody.rawtext.push({
                text: "§b"
            });
            rawTextBody.rawtext.push({
                translate: FSLocalizedString.localize("ui.room_dialogue.cost", "Cost: %s Security Chip(s). You currently have %s Security Chips(s).", "Cost of building the bunker"),
                with: [costInChips.toString(), `${currentChipAmount}`]
            });
            rawTextBody.rawtext.push({
                text: "\n\n§r"
            });
        }
        else {
            rawTextBody.rawtext.push({
                text: "§b"
            });
            rawTextBody.rawtext.push({
                translate: FSLocalizedString.localize("ui.room_dialogue.free_creative", "This room is free in Creative Mode.", "Cost of building the bunker")
            });
            rawTextBody.rawtext.push({
                text: "\n\n§r"
            });
        }
        rawTextBody.rawtext.push({ text: "§r" });
        rawTextBody.rawtext.push({ translate: FSLocalizedString.localize("ui.room_dialogue.confirm.body", "Are you sure you want to build this room?", "Are you sure you want to build message") });
        const form = new mcui.ActionFormData();
        form.title({ translate: FSLocalizedString.localize("ui.room_dialogue.confirm.title", "Confirm Room", "Room Type title") });
        form.body(rawTextBody);
        let rawMessageButton = {
            rawtext: []
        };
        rawMessageButton.rawtext.push({
            text: "§l§a"
        }, {
            translate: FSLocalizedString.localize("ui.room_dialogue.confirm.build_now", "Build Now", "Build Now button")
        }, {
            text: "\n§r§8"
        });
        if (!FSPlayer.isCreative(player)) {
            rawMessageButton.rawtext.push({
                translate: FSLocalizedString.localize("ui.room_dialogue.button.cost_chips", "[Cost: %s Security Chips]", "Cost of building a corridor"),
                with: [costInChips.toString()]
            });
        }
        else {
            rawMessageButton.rawtext.push({
                translate: FSLocalizedString.localize("ui.room_dialogue.button.free_creative", "[Free in Creative Mode]", "Cost of building a corridor in creative mode")
            });
        }
        form.button(rawMessageButton, "textures/57digital/sbunk/icons/build_now.png");
        form.button({
            rawtext: [{ text: "§4< " }, { translate: FSLocalizedString.localize("ui.room_dialogue.confirm.go_back", "Go Back", "Go Back button") }]
        });
        form.show(player).then((resp) => {
            if (resp.canceled) {
                this.displayRoomPickerForm(player, location, rotation, currentCategory); 
                return;
            }
            let selectedIndex = resp.selection;
            if (selectedIndex === 0) {
                const canAfford = currentChipAmount >= costInChips;
                if (canAfford || FSPlayer.isCreative(player)) {
                    if (!FSPlayer.isCreative(player)) {
                        FSInventoryManager.takeItem(player, "57d_sbunk:security_chip", costInChips);
                    }
                    this.addStructure(player, location, rotation, structureId, stuctureName, "", costInChips);
                }
                else {
                    this.showCanNotAffordRoomDialogue(player, costInChips);
                }
            }
            else {
                this.displayRoomPickerForm(player, location, rotation, currentCategory); 
            }
        });
    }
    displayRoomTypeForm(player, location, rotation) {
        const form = new mcui.ActionFormData();
        form.title({ translate: FSLocalizedString.localize("ui.room_dialogue.title", "Room Type", "Room Type title") });
        form.body({ translate: FSLocalizedString.localize("ui.room_dialogue.body", "Pick a room category. You will be able to select a room type next.", "Room Type body") });
        structure_data.categories.forEach(category => {
            const rawText = {
                rawtext: [{ translate: category.name }, { text: " >" }]
            };
            form.button(rawText);
        });
        form.button({ translate: FSLocalizedString.localize("ui.room_dialogue.cancel", "Cancel", "Cancel button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            let selection = resp.selection || 0;
            if (selection < structure_data.categories.length) {
                let category = structure_data.categories[selection];
                this.displayRoomPickerForm(player, location, rotation, category);
            }
        });
    }
    addRoom(player, location, rotation) {
        this.displayRoomTypeForm(player, location, rotation);
    }
    isBlockRoomBlockage(block) {
        if (!block) {
            return false;
        }
        return block.permutation?.matches("57d_sbunk:blockage_room");
    }
    isBlockHubCorridorBlockage(block) {
        if (!block) {
            return false;
        }
        return block.permutation?.matches("57d_sbunk:blockage_hub_corridor");
    }
    isBlockCorridorBlockage(block) {
        if (!block) {
            return false;
        }
        return block.permutation?.matches("57d_sbunk:blockage_corridor");
    }
    isBlockRoomMarker(block) {
        if (!block) {
            return false;
        }
        return block.permutation?.matches("57d_sbunk:marker_room");
    }
    isBlockHubCorridorMarker(block) {
        if (!block) {
            return false;
        }
        return block.permutation?.matches("57d_sbunk:marker_hub_corridor");
    }
    isBlockCorridorMarker(block) {
        if (!block) {
            return false;
        }
        return block.permutation?.matches("57d_sbunk:marker_corridor");
    }
    findRoomMarker(block) {
        if (!block) {
            return undefined;
        }
        let dimension = world.getDimension("overworld");
        let marker = undefined;
        for (let x = -4; x < 8; x++) {
            for (let y = -4; y < 8; y++) {
                for (let z = -4; z < 8; z++) {
                    let location = { x: block.location.x + x, y: block.location.y + y, z: block.location.z + z };
                    let test_block = dimension.getBlock(location);
                    if (this.isBlockRoomMarker(test_block)) {
                        marker = test_block;
                        break;
                    }
                }
            }
        }
        return marker;
    }
    findCorridorMarker(block) {
        if (!block) {
            return undefined;
        }
        let dimension = world.getDimension("overworld");
        let marker = undefined;
        for (let x = -4; x < 8; x++) {
            for (let y = -4; y < 8; y++) {
                for (let z = -4; z < 8; z++) {
                    let location = { x: block.location.x + x, y: block.location.y + y, z: block.location.z + z };
                    let test_block = dimension.getBlock(location);
                    if (this.isBlockCorridorMarker(test_block)) {
                        marker = test_block;
                        break;
                    }
                }
            }
        }
        return marker;
    }
    findHubCorridorMarker(block) {
        if (!block) {
            return undefined;
        }
        let dimension = world.getDimension("overworld");
        let marker = undefined;
        for (let x = -4; x < 8; x++) {
            for (let y = -4; y < 8; y++) {
                for (let z = -4; z < 8; z++) {
                    let location = { x: block.location.x + x, y: block.location.y + y, z: block.location.z + z };
                    let test_block = dimension.getBlock(location);
                    if (this.isBlockHubCorridorMarker(test_block)) {
                        marker = test_block;
                        break;
                    }
                }
            }
        }
        return marker;
    }
    shouldShowRoomOptions(player) {
        if (!FSPlayerPerms.sharedInstance().isAdmin(player)) {
            return false;
        }
        if (player.hasTag("sbunk_building")) {
            return false;
        }
        let options = {
            includeLiquidBlocks: false,
            includePassableBlocks: false,
            maxDistance: raycast_max_distance
        };
        let shouldShowOptions = false;
        try {
            let raycastHit = world.getDimension(player.dimension.id).getBlockFromRay(player.getHeadLocation(), player.getViewDirection(), options);
            if (raycastHit && raycastHit.block) {
                shouldShowOptions = this.isBlockRoomBlockage(raycastHit.block) || this.isBlockRoomMarker(raycastHit.block);
            }
        }
        catch (error) {
            shouldShowOptions = false;
        }
        return shouldShowOptions;
    }
    shouldShowCorridorOptions(player) {
        if (!FSPlayerPerms.sharedInstance().isAdmin(player)) {
            return false;
        }
        if (player.hasTag("sbunk_building")) {
            return false;
        }
        let options = {
            includeLiquidBlocks: false,
            includePassableBlocks: false,
            maxDistance: raycast_max_distance
        };
        let shouldShowOptions = false;
        try {
            let raycastHit = world.getDimension(player.dimension.id).getBlockFromRay(player.getHeadLocation(), player.getViewDirection(), options);
            if (raycastHit && raycastHit.block) {
                shouldShowOptions = this.isBlockCorridorBlockage(raycastHit.block) || this.isBlockCorridorMarker(raycastHit.block);
            }
        }
        catch (error) {
            shouldShowOptions = false;
        }
        return shouldShowOptions;
    }
    shouldShowHubCorridorOptions(player) {
        if (!FSPlayerPerms.sharedInstance().isAdmin(player)) {
            return false;
        }
        if (player.hasTag("sbunk_building")) {
            return false;
        }
        let options = {
            includeLiquidBlocks: false,
            includePassableBlocks: false,
            maxDistance: raycast_max_distance
        };
        let shouldShowOptions = false;
        try {
            let raycastHit = world.getDimension(player.dimension.id).getBlockFromRay(player.getHeadLocation(), player.getViewDirection(), options);
            if (raycastHit && raycastHit.block) {
                shouldShowOptions = this.isBlockHubCorridorBlockage(raycastHit.block) || this.isBlockHubCorridorMarker(raycastHit.block);
            }
        }
        catch (error) {
            shouldShowOptions = false;
        }
        return shouldShowOptions;
    }
    showRoomOptions(player) {
        let options = {
            includeLiquidBlocks: false,
            includePassableBlocks: false,
            maxDistance: raycast_max_distance
        };
        let raycastHit = world.getDimension(player.dimension.id).getBlockFromRay(player.getHeadLocation(), player.getViewDirection(), options);
        if (raycastHit && raycastHit.block) {
            let block = this.isBlockRoomMarker(raycastHit?.block) ? raycastHit.block : undefined;
            if (this.isBlockRoomBlockage(raycastHit?.block)) {
                block = this.findRoomMarker(raycastHit?.block);
            }
            if (block) {
                this.addRoom(player, block.location, raycastHit.face);
                return;
            }
        }
    }
    addLowerLevel(player, structure_cost) {
        let dimension = world.getDimension("overworld");
        for (let x = -4; x < 8; x++) {
            for (let z = -4; z < 8; z++) {
                let location = { x: player.location.x + x, y: player.location.y - 1, z: player.location.z + z };
                let block = dimension.getBlock(location);
                if (block && block.permutation.matches("57d_sbunk:hub_marker")) {
                    location = { x: Math.floor(location.x), y: Math.floor(location.y), z: Math.floor(location.z) };
                    location = { x: location.x - 24, y: location.y - 16, z: location.z - 24 };
                    this.addStructure(player, location, Direction.Down, "hub_0", "", "", structure_cost);
                    break;
                }
            }
        }
    }
    showCorridorOptions(player) {
        let options = {
            includeLiquidBlocks: false,
            includePassableBlocks: false,
            maxDistance: raycast_max_distance
        };
        let raycastHit = world.getDimension(player.dimension.id).getBlockFromRay(player.getHeadLocation(), player.getViewDirection(), options);
        if (raycastHit && raycastHit.block) {
            let block = this.isBlockCorridorMarker(raycastHit?.block) ? raycastHit.block : undefined;
            if (this.isBlockCorridorBlockage(raycastHit?.block)) {
                block = this.findCorridorMarker(raycastHit?.block);
            }
            if (block) {
                this.displayCorridorConfirm(player, block.location, raycastHit.face, "corridor_1");
                return;
            }
        }
    }
    showHubCorridorOptions(player) {
        let options = {
            includeLiquidBlocks: false,
            includePassableBlocks: false,
            maxDistance: raycast_max_distance
        };
        let raycastHit = world.getDimension(player.dimension.id).getBlockFromRay(player.getHeadLocation(), player.getViewDirection(), options);
        if (raycastHit && raycastHit.block) {
            let block = this.isBlockHubCorridorMarker(raycastHit?.block) ? raycastHit.block : undefined;
            if (this.isBlockHubCorridorBlockage(raycastHit?.block)) {
                block = this.findHubCorridorMarker(raycastHit?.block);
            }
            if (block) {
                this.displayCorridorConfirm(player, block.location, raycastHit.face, "corridor_0", true);
                return;
            }
        }
    }
    displayCorridorConfirm(player, location, rotation, structure, is_hub_corridor = false) {
        const form = new mcui.ActionFormData();
        form.title({ translate: FSLocalizedString.localize("chat.corridor_dialogue.title", "Expand Bunker", "Bunker expand title") });
        const costInChips = is_hub_corridor ? HUB_CORRIDOR_COST_CHIPS : CORRIDOR_COST_CHIPS;
        const currentChipAmount = FSInventoryManager.getCountOfItem(player, "57d_sbunk:security_chip") || 0;
        let rawMessageBody = {
            rawtext: []
        };
        rawMessageBody.rawtext.push({
            translate: FSLocalizedString.localize("chat.corridor_dialogue.body", "Would you like to expand your bunker with a corridor? You can add rooms to the corridor later.", "Bunker expand body text")
        });
        if (!FSPlayer.isCreative(player)) {
            rawMessageBody.rawtext.push({
                text: "\n\n§b"
            });
            rawMessageBody.rawtext.push({
                translate: FSLocalizedString.localize("ui.corridor_dialogue.cost", "Cost: %s Security Chip(s). You currently have %s Security Chips(s).", "Cost of building the bunker"),
                with: [costInChips.toString(), `${currentChipAmount}`]
            });
            rawMessageBody.rawtext.push({
                text: "§r"
            });
        }
        else {
            rawMessageBody.rawtext.push({
                text: "\n\n§b"
            });
            rawMessageBody.rawtext.push({
                translate: FSLocalizedString.localize("ui.corridor_dialogue.free_creative", "This corridor is free in Creative Mode.", "Cost of building the bunker")
            });
            rawMessageBody.rawtext.push({
                text: "§r"
            });
        }
        form.body(rawMessageBody);
        let rawMessage = {
            rawtext: []
        };
        rawMessage.rawtext.push({
            text: "§l"
        }, {
            translate: FSLocalizedString.localize("chat.corridor_dialogue.confirm", "Expand Now", "Button text to expand bunker building")
        }, {
            text: "\n§r§8"
        });
        if (!FSPlayer.isCreative(player)) {
            rawMessage.rawtext.push({
                translate: FSLocalizedString.localize("ui.corridor_dialogue.button.cost_chips", "[Cost: %s Security Chips]", "Cost of building a corridor"),
                with: [costInChips.toString()]
            });
        }
        else {
            rawMessage.rawtext.push({
                translate: FSLocalizedString.localize("ui.corridor_dialogue.button.free_creative", "[Free in Creative Mode]", "Cost of building a corridor in creative mode")
            });
        }
        form.button(rawMessage);
        form.button({ translate: FSLocalizedString.localize("chat.corridor_dialogue.cancel", "Cancel", "Cancel button") });
        form.show(player).then(resp => {
            if (resp.canceled) {
                return;
            }
            let selection = resp.selection || 0;
            if (selection === 0) {
                const canAfford = currentChipAmount >= costInChips;
                if (canAfford || FSPlayer.isCreative(player)) {
                    if (!FSPlayer.isCreative(player)) {
                        FSInventoryManager.takeItem(player, "57d_sbunk:security_chip", costInChips);
                    }
                    this.addCorridor(player, location, rotation, structure, costInChips);
                }
                else {
                    this.showCanNotAffordCorridorDialogue(player, costInChips);
                }
            }
        });
    }
    addUndo(player, structureData, undo_bunker_id = "", structure_cost) {
        const undo_id = undo_bunker_id.length > 0 ? undo_bunker_id : FSUtil.shortId();
        const dimension = player.dimension;
        FSDynamicProperty.clear(world, "undo_type");
        FSDynamicProperty.clear(world, "undo_id");
        FSDynamicProperty.clear(world, "undo_player_locs");
        FSDynamicProperty.clear(world, "undo_loc_0");
        FSDynamicProperty.clear(world, "undo_loc_1");
        FSDynamicProperty.clear(world, "undo_loc_door_0");
        FSDynamicProperty.clear(world, "undo_loc_door_1");
        const structure_corner_0 = this.structureCorner0(structureData.location_0, structureData.location_1);
        const structure_corner_1 = this.structureCorner1(structureData.location_0, structureData.location_1);
        const structure_corner_door_0 = this.structureCorner0(structureData.door_loc_0, structureData.door_loc_1);
        const structure_corner_door_1 = this.structureCorner1(structureData.door_loc_0, structureData.door_loc_1);
        const players = world.getAllPlayers();
        const player_locs = [];
        players.forEach(player => {
            player_locs.push({
                id: player.id,
                name: player.name,
                location: player.location,
                rotation: player.getRotation()
            });
        });
        let undo_data = {
            undo_id: undo_id,
            structure_type: "",
            structure_corner_0: { x: 0, y: 0, z: 0 },
            structure_corner_1: { x: 0, y: 0, z: 0 },
            structure_corner_door_0: { x: 0, y: 0, z: 0 },
            structure_corner_door_1: { x: 0, y: 0, z: 0 },
            structure_bunker_a_0: { x: 0, y: 0, z: 0 },
            structure_bunker_a_1: { x: 0, y: 0, z: 0 },
            structure_bunker_b_0: { x: 0, y: 0, z: 0 },
            structure_bunker_b_1: { x: 0, y: 0, z: 0 },
            structure_bunker_c_0: { x: 0, y: 0, z: 0 },
            structure_bunker_c_1: { x: 0, y: 0, z: 0 },
            player_locs: player_locs,
            structure_cost: structure_cost
        };
        if (undo_id === undo_bunker_id) {
            const bunker_undo_data = FSDynamicProperty.getString(world, `undo_data_${undo_bunker_id}`);
            if (bunker_undo_data) {
                undo_data = JSON.parse(bunker_undo_data);
            }
        }
        if (this.isRoom(structureData.structure) || this.isCorridor(structureData.structure)) {
            undo_data.structure_type = this.isRoom(structureData.structure) ? "room" : "corridor";
            undo_data.structure_corner_0 = structure_corner_0;
            undo_data.structure_corner_1 = structure_corner_1;
            undo_data.structure_corner_door_0 = structure_corner_door_0;
            undo_data.structure_corner_door_1 = structure_corner_door_1;
            let loc0 = structure_corner_0;
            let loc1 = structure_corner_1;
            dimension.runCommand(`/structure save 57d_sbunk:undo_${undo_id} ${loc0.x} ${loc0.y} ${loc0.z} ${loc1.x} ${loc1.y} ${loc1.z} disk`);
            loc0 = structure_corner_door_0;
            loc1 = structure_corner_door_1;
            dimension.runCommand(`/structure save 57d_sbunk:undo_door_${undo_id} ${loc0.x} ${loc0.y} ${loc0.z} ${loc1.x} ${loc1.y} ${loc1.z} disk`);
        }
        if (this.isHub(structureData.structure)) {
            undo_data.structure_type = "hub";
            undo_data.structure_corner_0 = structure_corner_0;
            undo_data.structure_corner_1 = structure_corner_1;
            let loc0 = structure_corner_0;
            let loc1 = structure_corner_1;
            dimension.runCommand(`/structure save 57d_sbunk:undo_${undo_id} ${loc0.x} ${loc0.y} ${loc0.z} ${loc1.x} ${loc1.y} ${loc1.z} disk`);
        }
        if (this.isBuilding(structureData.structure)) {
            undo_data.structure_type = "bunker";
            undo_data.structure_bunker_a_0 = structure_corner_0;
            undo_data.structure_bunker_a_1 = structure_corner_1;
            const loc0 = structure_corner_0;
            const loc1 = structure_corner_1;
            dimension.runCommand(`/structure save 57d_sbunk:undo_bunker_a_${undo_id} ${loc0.x} ${loc0.y} ${loc0.z} ${loc1.x} ${loc1.y} ${loc1.z} disk`);
        }
        if (this.isEntrance(structureData.structure)) {
            undo_data.structure_type = "bunker";
            undo_data.structure_bunker_b_0 = structure_corner_0;
            undo_data.structure_bunker_b_1 = structure_corner_1;
            const loc0 = structure_corner_0;
            const loc1 = structure_corner_1;
            dimension.runCommand(`/structure save 57d_sbunk:undo_bunker_b_${undo_id} ${loc0.x} ${loc0.y} ${loc0.z} ${loc1.x} ${loc1.y} ${loc1.z} disk`);
        }
        if (this.isFirstHub(structureData.original_structure)) {
            undo_data.structure_type = "bunker";
            undo_data.structure_bunker_c_0 = structure_corner_0;
            undo_data.structure_bunker_c_1 = structure_corner_1;
            const loc0 = structure_corner_0;
            const loc1 = structure_corner_1;
            dimension.runCommand(`/structure save 57d_sbunk:undo_bunker_c_${undo_id} ${loc0.x} ${loc0.y} ${loc0.z} ${loc1.x} ${loc1.y} ${loc1.z} disk`);
        }
        StructureManager.AddUndoEntry(undo_id, undo_data);
        return undo_id;
    }
    static GetUndoArray() {
        if (FULL_UNDO_SUPPORT_ENABLED) {
            let undo_array_string = FSDynamicProperty.getString(world, "undo_array");
            if (undo_array_string.length === 0) {
                undo_array_string = "[]";
            }
            const undo_array = JSON.parse(undo_array_string);
            if (undo_array.length === 0) {
                const last_undo_id = FSDynamicProperty.getString(world, "last_undo_id");
                if (last_undo_id.length > 0) {
                    undo_array.push(last_undo_id);
                }
            }
            return JSON.parse(undo_array_string);
        }
        else {
            const last_undo_array = [];
            const last_undo_id = FSDynamicProperty.getString(world, "last_undo_id");
            if (last_undo_id.length > 0) {
                last_undo_array.push(last_undo_id);
            }
            return last_undo_array;
        }
    }
    static AddUndoEntry(undo_id, undo_data) {
        FSDynamicProperty.setString(world, "last_undo_id", undo_id);
        FSDynamicProperty.setString(world, `undo_data_${undo_id}`, JSON.stringify(undo_data));
        let undo_array = StructureManager.GetUndoArray();
        undo_array.push(undo_id);
        FSDynamicProperty.setString(world, "undo_array", JSON.stringify(undo_array));
    }
    static RemoveUndoEntry(undo_id) {
        let undo_array = StructureManager.GetUndoArray();
        const index = undo_array.indexOf(undo_id);
        if (index > -1) {
            undo_array.splice(index, 1);
        }
        FSDynamicProperty.setString(world, "undo_array", JSON.stringify(undo_array));
    }
    static Undo(player, force_undo_id = "") {
        if (!FSPlayerPerms.sharedInstance().isAdmin(player)) {
            PlayerHelper.outputAdminAccessRequired(player);
            return;
        }
        let undo_id = "";
        if (force_undo_id.length > 0) {
            undo_id = force_undo_id;
        }
        if (undo_id.length === 0) {
            let undo_array = StructureManager.GetUndoArray();
            if (undo_array.length > 0) {
                undo_id = undo_array[undo_array.length - 1];
            }
        }
        const undo_data = JSON.parse(FSDynamicProperty.getString(world, `undo_data_${undo_id}`));
        const undo_type = undo_data.structure_type;
        if (undo_type.length === 0) {
            world.playSound("fs.sbunk.chat_ping", player.location);
            world.sendMessage({ translate: FSLocalizedString.localize("chat.undo.failed", "§c[!] Unable to undo!", "Unable to undo message") });
            return;
        }
        let player_locs = undo_data.player_locs;
        player_locs.forEach(player_loc => {
            const po = {
                name: player_loc.name
            };
            const players = world.getPlayers(po);
            if (players.length > 0) {
                const player = players[0];
                player.teleport(player_loc.location);
                player.setRotation(player_loc.rotation);
            }
        });
        const undo_loc_0 = undo_data.structure_corner_0;
        const undo_loc_1 = undo_data.structure_corner_1;
        const undo_loc_door_0 = undo_data.structure_corner_door_0;
        const undo_loc_door_1 = undo_data.structure_corner_door_1;
        const structure_bunker_a_0 = undo_data.structure_bunker_a_0;
        const structure_bunker_a_1 = undo_data.structure_bunker_a_1;
        const structure_bunker_b_0 = undo_data.structure_bunker_b_0;
        const structure_bunker_b_1 = undo_data.structure_bunker_b_1;
        const structure_bunker_c_0 = undo_data.structure_bunker_c_0;
        const structure_bunker_c_1 = undo_data.structure_bunker_c_1;
        const structure_cost = undo_data.structure_cost || 0;
        let loc_0;
        let loc_1;
        if (undo_type === "bunker") {
            loc_0 = structure_bunker_a_0;
            loc_1 = structure_bunker_a_1;
            StructureManager.executeUndo(player, undo_id, undo_type, loc_0, loc_1, "57d_sbunk:undo_bunker_a", true, structure_cost);
            loc_0 = structure_bunker_b_0;
            loc_1 = structure_bunker_b_1;
            StructureManager.executeUndo(player, undo_id, undo_type, loc_0, loc_1, "57d_sbunk:undo_bunker_b", true, 0);
            loc_0 = structure_bunker_c_0;
            loc_1 = structure_bunker_c_1;
            StructureManager.executeUndo(player, undo_id, undo_type, loc_0, loc_1, "57d_sbunk:undo_bunker_c", true, 0);
        }
        else {
            loc_0 = undo_loc_0;
            loc_1 = undo_loc_1;
            StructureManager.executeUndo(player, undo_id, undo_type, loc_0, loc_1, "57d_sbunk:undo", true, structure_cost);
        }
        if (undo_type === "room" || undo_type === "corridor") {
            loc_0 = undo_loc_door_0;
            loc_1 = undo_loc_door_1;
            StructureManager.executeUndo(player, undo_id, undo_type, loc_0, loc_1, "57d_sbunk:undo_door", true, 0);
        }
    }
    static executeUndo(player, undo_id, undo_type, loc_0, loc_1, structure_prefix, delete_structure = false, structure_cost) {
        const command = `/structure load ${structure_prefix}_${undo_id} ${loc_0.x} ${loc_0.y} ${loc_0.z} 0_degrees none true true false`;
        StructureManager.KillCustomEntitiesWithLocsWithUndoId(player.dimension, undo_id);
        system.runTimeout(() => {
            StructureManager.KillItemsWithLocs(player.dimension, loc_0, loc_1);
            StructureManager.PopCustomEntitiesToItems(player.dimension, loc_0, loc_1);
        }, 1);
        system.runTimeout(() => {
            StructureManager.KillCustomEntitiesWithLocs(player.dimension, loc_0, loc_1);
            StructureManager.TeleportItemsWithLocs(player, player.dimension, loc_0, loc_1);
            StructureManager.TeleportEntitiesWithLocs(player, player.dimension, loc_0, loc_1);
        }, 2);
        system.runTimeout(() => {
            StructureManager.KillEntitiesWithLocs(player.dimension, loc_0, loc_1);
        }, 3);
        system.runTimeout(() => {
            StructureManager.KillItemsWithLocs(player.dimension, loc_0, loc_1);
        }, 4);
        system.runTimeout(() => {
            const result = player.runCommand(command);
            if (result.successCount > 0) {
                if (delete_structure) {
                    const command_delete = `/structure delete ${structure_prefix}_${undo_id}`;
                    player.runCommand(command_delete);
                }
                if (structure_prefix !== "57d_sbunk:undo_door") {
                    world.playSound("fs.sbunk.chat_ping", player.location);
                    world.sendMessage({ translate: FSLocalizedString.localize("chat.undo.complete", "§q[!] Undo complete!", "Undo complete message") });
                }
                if (structure_cost > 0 && SHOULD_REFUND_CHIPS_ON_UNDO) {
                    let refund_amount = structure_cost * REFUND_PERCENTAGE_ON_UNDO;
                    FSInventoryManager.giveItem(player, "57d_sbunk:security_chip", Math.ceil(refund_amount));
                }
                FSDynamicProperty.clear(world, "last_undo_id");
                FSDynamicProperty.clear(world, `undo_data_${undo_id}`);
                StructureManager.RemoveUndoEntry(undo_id);
            }
            else {
                world.playSound("fs.sbunk.chat_ping", player.location);
                world.sendMessage({ translate: FSLocalizedString.localize("chat.undo.execute_failed", "§c[!] Unable to undo!", "Unable to undo message") });
            }
        }, 5);
    }
}

//# sourceMappingURL=../../../_SBUNK_BPDebug/fs_sbunk/StructureManager.js.map
