import {system, world, ItemStack, PlayerPlaceBlockAfterEvent, Entity, Player, Block } from "@minecraft/server"
import { JsonDatabase } from "./con-database.js"
import { ActionFormData,MessageFormData,ModalFormData } from "@minecraft/server-ui"


const playerfirstLoadWorld = new JsonDatabase('playerfirstLoadWorld');

  
let firstLoadWorld = false;

//after the player join they're given a book and a tag
world.afterEvents.playerSpawn.subscribe(async (eventData) => {
		const playerId = eventData.player;
		const returnPlayerLoaded = playerfirstLoadWorld.get("snailstudiosfirstloadgame");

		playerId.runCommandAsync('execute at @a[tag=!snailstudios_tutorial] run structure load snail_scary:book ~ ~ ~');
		playerId.runCommandAsync('execute at @a[tag=!snailstudios_tutorial] run tag @a[tag=!snailstudios_tutorial] add snailstudios_tutorial');
		if (returnPlayerLoaded !== true) {
			world.sendMessage('§f{§2Scary§f} §fAdd-on Loaded!');
			firstLoadWorld = true;
			playerfirstLoadWorld.set("snailstudiosfirstloadgame", firstLoadWorld);
		}
	 }
  )




world.afterEvents.entityHitEntity.subscribe(event => {
    const { damagingEntity, hitEntity } = event;
    if (damagingEntity?.typeId === "minecraft:player") {
        const heldItem = damagingEntity.getComponent('equippable').getEquipment('Mainhand');
        if (heldItem?.typeId === 'snailstudios_scary:goat_dagger') {
            hitEntity.addEffect('poison', 30 * 5, { amplifier: 2, showParticles: true });
        } else if (heldItem?.typeId === 'snailstudios_scary:hammer_of_filth') {
            hitEntity.addEffect('wither', 20 * 2, { amplifier: 2, showParticles: true });
        } else if (heldItem?.typeId === 'snailstudios_scary:villager_sword') {
            damagingEntity.addEffect('jump_boost', 20 * 2, { amplifier: 2, showParticles: true });
        } else if (heldItem?.typeId === 'snailstudios_scary:loud_weapon') {
            damagingEntity.addEffect('strength', 20 * 2, { amplifier: 2, showParticles: true });
        } else if (heldItem?.typeId === 'snailstudios_scary:smiler_sword') {
            hitEntity.addEffect('darkness', 30 * 5, { amplifier: 2, showParticles: true });
        } else if (heldItem?.typeId === 'snailstudios_scary:ancient_sword') {
            damagingEntity.addEffect('resistance', 20 * 2, { amplifier: 2, showParticles: true });
        } else if (heldItem?.typeId === 'snailstudios_scary:witch_sword') {
            damagingEntity.addEffect('speed', 20 * 2, { amplifier: 2, showParticles: true });
        }
    }
});

world.afterEvents.itemCompleteUse.subscribe((event) => {
    const item = event.itemStack;
    const source = event.source;

    // Check for specific food items that apply fixed effects
    if (
        item.typeId === "snailstudios_scary:nasty_meat" ||
        item.typeId === "snailstudios_scary:raw_lamb_chop" ||
        item.typeId === "snailstudios_scary:raw_chicken_leg" ||
        item.typeId === "snailstudios_scary:weird_meat" ||
        item.typeId === "snailstudios_scary:creeper_clump" ||
        item.typeId === "snailstudios_scary:thick_rotten_flesh"
    ) {
        const effectDuration = 100;
        const effectAmplifier = 2;
        source.addEffect("poison", effectDuration, { amplifier: effectAmplifier });
    } else if (item.typeId === "snailstudios_scary:cooked_nasty_meat") {
        const effectDuration = 100;
        const effectAmplifier = 1;
        source.addEffect("speed", effectDuration, { amplifier: effectAmplifier });
    } else if (item.typeId === "snailstudios_scary:cooked_weird_meat") {
        const effectDuration = 30;
        const effectAmplifier = 1;
        source.addEffect("regeneration", effectDuration, { amplifier: effectAmplifier });
    } else if (item.typeId === "snailstudios_scary:cooked_chicken_leg") {
        const effectDuration = 100;
        const effectAmplifier = 2;
        source.addEffect("absorption", effectDuration, { amplifier: effectAmplifier });
    } else if (item.typeId === "snailstudios_scary:cooked_lamb_chop") {
        const effectDuration = 40;
        const effectAmplifier = 2;
        source.addEffect("jump_boost", effectDuration, { amplifier: effectAmplifier });
    } else if (item.typeId === "snailstudios_scary:serum") {
        // Duration and amplifier settings
        const effectDuration = 200;
        const effectAmplifier = 2;

        // List of effects
        const effects = ["strength", "resistance", "wither","poison"];

        // Randomly select an effect from the array
        const randomIndex = Math.floor(Math.random() * effects.length);
        const chosenEffect = effects[randomIndex];

        // Apply the chosen effect
        source.addEffect(chosenEffect, effectDuration, { amplifier: effectAmplifier });
    } else if (item.typeId === "snailstudios_scary:serum_y") {
        // Duration and amplifier settings
        const effectDuration = 500;
        const effectAmplifier = 3;

        // List of effects
        const effects = ["strength", "resistance","speed","jump_boost"];

        // Randomly select an effect from the array
        const randomIndex = Math.floor(Math.random() * effects.length);
        const chosenEffect = effects[randomIndex];

        // Apply the chosen effect
        source.addEffect(chosenEffect, effectDuration, { amplifier: effectAmplifier });
    }
});

world.afterEvents.itemUseOn.subscribe((event) => {
    const item = event.itemStack;
    const source = event.source;

    if (
		item.typeId === "snailstudios_scary:loud_weapon"
	) {
	// Here we clearly identify what the duration and amplifier should be based on if the player has a full
    // armor set or not
    const effectDuration = 100
    const effectAmplifier = 2

    // Finally apply the effect
    source.runCommandAsync("/playsound snailstudios_scary.loud_weapon_hit @s")
    }
});



system.runInterval(() => {
	const entity = world.getDimension("overworld").getEntities();
	for (let entities of entity) {
		if (entities.typeId === "snailstudios_scary:dweller")
		{
			// Define the coordinates for the block directly below the player
			const x = Math.floor(entities.location.x);
			const y = Math.floor(entities.location.y) + 2; // Adjust to get the block below
			const z = Math.floor(entities.location.z);

			// Retrieve the block below the player
			const blockAboveEntity = entities.dimension.getBlock({
				x,
				y,
				z
			});

			if (blockAboveEntity?.typeId !== 'minecraft:air') {
				entities.triggerEvent('plague_dweller_crouch');
			}
			else {
				entities.triggerEvent('plague_dweller_spawn');
			}
	}
  }
}, 3)













world.afterEvents.projectileHitBlock.subscribe((event) => {
    const entity = event.projectile;
    const locationEntity = event.location;
	const sourceEntity = event.source;
	const dimensionId = sourceEntity.dimension.id;


	const dimension = world.getDimension(dimensionId);

    if (entity.typeId === "snailstudios_scary:loud_head") {
        // Use setTimeout to delay execution
        system.runTimeout(() => {
                dimension.runCommandAsync(`particle snailstudios_scary:mob_spawn ${locationEntity.x} ${locationEntity.y + 1} ${locationEntity.z}`)
				if (entity.isValid()) {
					entity.remove();
					}
        }, 5);
    }
});


world.afterEvents.projectileHitEntity.subscribe((event) => {
    const entity = event.projectile;
    const locationEntity = event.location;
	const sourceEntity = event.source;
	const dimensionId = sourceEntity.dimension.id;


	const dimension = world.getDimension(dimensionId);

    if (entity.typeId === "snailstudios_scary:loud_head") {
        // Use setTimeout to delay execution
        system.runTimeout(() => {
                dimension.runCommandAsync(`particle snailstudios_scary:mob_spawn ${locationEntity.x} ${locationEntity.y + 1} ${locationEntity.z}`)
				if (entity.isValid()) {
                entity.remove();
				}
        }, 5);
    }
});





world.afterEvents.entitySpawn.subscribe((event) => {
    const entity = event.entity;  // Get the spawned entity
        // Check if the entity is valid
        if (entity.typeId !== "snailstudios_scary:ground_posion") {
            return; // Exit if the entity is not valid
        }
    
        // Attempt to get the dimension where the entity spawned
        const dimension = entity.dimension; 
    
        // Check if the dimension is valid
        if (!dimension) {
            return; // Exit if the dimension is not valid
        }
    const locationEntity = entity.location;  // Get the spawn location of the entity

    // Check if the spawned entity is of the specific type "snailstudios_scary:ground_posion"
    if (entity.typeId === "snailstudios_scary:ground_posion") {
        const radius = 3;  // Define the radius within which to apply poison
        const effectDurationPoison = 100;  // Duration in ticks
        const effectAmplifierPoison = 4;  // Poison effect amplifier

        // Get all nearby players within the radius from the spawn location
        const nearbyPlayers = dimension.getPlayers({
            location: locationEntity,
            maxDistance: radius
        });

        // Apply the poison effect to each nearby player
        nearbyPlayers.forEach((player) => {
            player.addEffect('poison', effectDurationPoison, { amplifier: effectAmplifierPoison, showParticles: true });
        });
    } else {
        return;
    }
});

























const trackedPlayers = new Map(); // Map to track player positions and stalkers
const playerTrackingStatus = new Map(); // Map to track if a player has been recently tracked

const stalkers = [
    {
        id: "dweller",
        entity: "snailstudios_scary:dweller",
        sounds: [
            "snailstudios_scary.dweller_scary_01",
            "snailstudios_scary.dweller_scary_02",
            "snailstudios_scary.dweller_scary_03",
            "snailstudios_scary.dweller_scary_04",
            "snailstudios_scary.dweller_scary_05"
        ],
        soundChance: 4,
        spawnChance: 1
    }
];

// Helper function to check if the player is underground
function isPlayerUnderground(yLevel) {
    return yLevel >= -64 && yLevel <= 20;
}

// Function to reset tracking status after a delay
function resetTrackingStatus(playerID) {
    system.runTimeout(() => {
        playerTrackingStatus.set(playerID, false);
    }, 30000); // Reset after 30 seconds
}

function getRandomSound(stalker) {
    const sounds = stalker.sounds;
    const randomIndex = Math.floor(Math.random() * sounds.length);
    return sounds[randomIndex];
}

// Main logic for checking player movement and triggering stalker behavior
system.runInterval(() => {
    if (!isDwellerOn) {
        return; // Exit if isDwellerOn is false
    }

    for (const player of world.getPlayers()) {
        const playerID = player.id;
        const playerPosition = player.location;
        const playerYLevel = Math.floor(playerPosition.y);

        // Check if the player is underground
        if (!isPlayerUnderground(playerYLevel)) {
            continue;
        }

        // Check if the player is already being tracked
        if (trackedPlayers.has(playerID)) {
            const { lastBlockPos, currentStalker } = trackedPlayers.get(playerID);

            // Calculate the distance between the last block position and the current position
            const distance = Math.sqrt(
                Math.pow(playerPosition.x - lastBlockPos.x, 2) +
                Math.pow(playerPosition.y - lastBlockPos.y, 2) +
                Math.pow(playerPosition.z - lastBlockPos.z, 2)
            );

            // Check if the player moved more than 10 blocks
            if (distance >= 10) {
                const randomSpawnChance = Math.random() * 100;
                const randomSoundChance = Math.random() * 100;

                // Try to spawn stalker
                if (randomSpawnChance <= currentStalker.spawnChance) {
                    const direction = {
                        x: (playerPosition.x - lastBlockPos.x) / distance,
                        y: (playerPosition.y - lastBlockPos.y) / distance,
                        z: (playerPosition.z - lastBlockPos.z) / distance
                    };

                    const spawnLocation = {
                        x: lastBlockPos.x - direction.x * 10,
                        y: lastBlockPos.y - direction.y * 10,
                        z: lastBlockPos.z - direction.z * 10
                    };

                    const blockAtSpawn = player.dimension.getBlock({
                        x: Math.floor(spawnLocation.x),
                        y: Math.floor(spawnLocation.y),
                        z: Math.floor(spawnLocation.z)
                    });

                    if (blockAtSpawn.typeId === "minecraft:air") {
                        player.dimension.spawnEntity(currentStalker.entity, spawnLocation);
                    }
                }

                // Try to play sound
                if (randomSoundChance <= currentStalker.soundChance) {
                    const randomSound = getRandomSound(currentStalker);
                    world.playSound(randomSound, playerPosition);
                }

                // Update tracking data with the new position
                trackedPlayers.set(playerID, {
                    lastBlockPos: playerPosition,
                    currentStalker
                });
            }
        } else {
            // If not tracked, start tracking with a random stalker
            const randomStalker = stalkers[Math.floor(Math.random() * stalkers.length)];
            trackedPlayers.set(playerID, {
                lastBlockPos: playerPosition,
                currentStalker: randomStalker
            });
        }
    }
}, 20); // Run every 20 ticks (~1 second)

// Remove player tracking when they leave
world.afterEvents.playerLeave.subscribe((event) => {
    const { player } = event;
    trackedPlayers.delete(player.id);
    playerTrackingStatus.delete(player.id);
});







































// Map to store player-specific data
const playerData = new Map();
let isPlacingStructure = false; // Global variable to track if a structure is being placed

// Define the blocks and structures
const logBlockIds = ["minecraft:oak_log", "minecraft:spruce_log", "minecraft:birch_log"]; // Add other log types as needed

const structureMap = {
    "north": "snail_scary:jester_s",
    "south": "snail_scary:jester_n",
    "east": "snail_scary:jester_w",
    "west": "snail_scary:jester_e"
};


// Function to get or initialize a player's data
function getPlayerData(player) {
    if (!playerData.has(player.name)) {
        playerData.set(player.name, {
            isPlacingStructure: false,
            scareCounter: 0, // Track how many times scares happened
            intervalIdLog: null, // Player-specific interval
        });
    }
    return playerData.get(player.name);
}

// Function to clear player data once the script stops running
function clearPlayerData(player) {
    if (playerData.has(player.name)) {
        const data = playerData.get(player.name);
        if (data.intervalIdLog) system.clearRun(data.intervalIdLog);
        playerData.delete(player.name);
    }
}

// Function to get the player's facing direction
function getPlayerDirection(player) {
    const playerRotation = player.getRotation();
    const yaw = playerRotation.y;

    if (yaw >= -45 && yaw < 45) return "south";
    if (yaw >= 45 && yaw < 135) return "west";
    if (yaw >= 135 || yaw < -135) return "north";
    return "east";
}

// Function to check for logs in front of the player, across multiple Y levels
function checkForLogs(player) {
    if (isPlacingStructure) return; // Prevents multiple structures from being placed simultaneously

    const { x, y, z } = player.location;
    const playerDirection = getPlayerDirection(player); // Get the player's facing direction

    // Define the range of blocks to check in front of the player
    const rangeStart = 15; // Distance from the player to start checking
    const rangeEnd = 25;   // Distance from the player to end checking
    const width = 3;       // Width of the area to check (1 block on each side of the center)
    const heightOffset = 2; // Vertical range above and below the player

    const foundLogs = []; // Store coordinates of found logs

    // Loop through the range in front of the player
    for (let i = rangeStart; i <= rangeEnd; i++) {
        for (let j = -width; j <= width; j++) {
            let blockX = x;
            let blockZ = z;

            // Adjust X and Z based on the player's direction
            if (playerDirection === "north") {
                blockZ -= i;
                blockX += j;
            } else if (playerDirection === "south") {
                blockZ += i;
                blockX += j;
            } else if (playerDirection === "east") {
                blockX += i;
                blockZ += j;
            } else if (playerDirection === "west") {
                blockX -= i;
                blockZ += j;
            }

            // Loop through Y levels (heightOffset above and below the player's Y)
            for (let yOffset = -heightOffset; yOffset <= heightOffset; yOffset++) {
                const targetY = y + yOffset;

                // Get the block at the current X, Y, Z
                const targetBlock = world.getDimension(player.dimension.id).getBlock({
                    x: Math.floor(blockX),
                    y: Math.floor(targetY),
                    z: Math.floor(blockZ)
                });

                // Check if the block is a log
                if (targetBlock && logBlockIds.includes(targetBlock.typeId)) {
                    // Check if the block below the found log is dirt
                    const blockBelow = world.getDimension(player.dimension.id).getBlock({
                        x: Math.floor(blockX),
                        y: Math.floor(targetY) - 1,
                        z: Math.floor(blockZ)
                    });

                    if (blockBelow.typeId === "minecraft:dirt") {
                        foundLogs.push({ x: blockX, y: targetY, z: blockZ });
                    }
                }
            }
        }
    }

    // If logs were found, place a structure at one of the found logs
    if (foundLogs.length > 0) {
        const randomLog = foundLogs[Math.floor(Math.random() * foundLogs.length)]; // Pick a random log
        const structureToPlace = structureMap[playerDirection]; // Get the structure for the player's direction
        isPlacingStructure = true;

        // Place the structure at the found log's location if it's an air block
        placeStructureWithEffect(player, structureToPlace, randomLog.x, randomLog.y, randomLog.z, playerDirection);
    } else {
    }
}

// Function to place the structure with a timeout for the darkness effect, checking for air blocks
function placeStructureWithEffect(player, structureName, logX, logY, logZ, direction) {
    const data = getPlayerData(player);
    let structureX = logX;
    let structureZ = logZ;

    // Adjust structure placement based on the player's direction
    if (direction === "north") structureZ -= 1;
    if (direction === "south") structureZ += 1;
    if (direction === "east") structureX += 1;
    if (direction === "west") structureX -= 1;

    // Check if the target location for placing the structure is air
    const dimension = world.getDimension(player.dimension.id);
    const blockAtPlacement = dimension.getBlock({ x: Math.floor(structureX), y: Math.floor(logY), z: Math.floor(structureZ) });

    if (blockAtPlacement.typeId === "minecraft:air") {
        // Set a timeout to allow the darkness effect to kick in
        system.runTimeout(() => {
            data.scareCounter++; // Increment scare counter for the player
            dimension.runCommand(`structure load ${structureName} ${structureX} ${logY} ${structureZ}`);
            isPlacingStructure = false; // Reset flag after placing the structure
        }, 60); // Delay before placing the structure (60ms)
    } else {
        // If the target block is not air, don't place the structure
        isPlacingStructure = false; // Reset flag if no structure is placed
    }
}

function spawnCreatureNearPlayer(player) {
    const data = getPlayerData(player);
    const { x, y, z } = player.location;
    const dimension = world.getDimension(player.dimension.id);
    const validGroundBlocks = [
        "minecraft:grass_block", 
        "minecraft:dirt", 
        "minecraft:sand", 
        "minecraft:stone", 
        "minecraft:grass_path"
    ]; // Expanded valid blocks for spawning

    // Try multiple random locations within the radius to find a valid block
    for (let attempt = 0; attempt < 10; attempt++) { // Limit to 10 attempts to find a valid block
        // Random radius between 20 and 40 blocks away
        const radius = Math.floor(Math.random() * (40 - 20 + 1)) + 20;
        const angle = Math.random() * 2 * Math.PI; // Random angle around the player

        // Calculate random spawn coordinates
        const spawnX = x + Math.cos(angle) * radius;
        const spawnZ = z + Math.sin(angle) * radius;

        // Check multiple Y levels starting above and scanning downwards
        let isValidSpawn = false;
        let spawnY = y + 20; // Start checking 20 blocks above the player

        while (spawnY > y - 10) { // Scan downwards for valid ground
            const blockAtSpawn = dimension.getBlock({
                x: Math.floor(spawnX),
                y: Math.floor(spawnY) - 1, // Check one block below the current location for ground
                z: Math.floor(spawnZ),
            });

            // Check if the block is a valid ground block
            if (blockAtSpawn && validGroundBlocks.includes(blockAtSpawn.typeId)) {
                // Check if the blocks above are air for spawning
                isValidSpawn = true;
                for (let i = 0; i <= 4; i++) { // Ensure there is space above for the creature to spawn
                    const blockAbove = dimension.getBlock({
                        x: Math.floor(spawnX),
                        y: Math.floor(spawnY) + i,
                        z: Math.floor(spawnZ),
                    });
                    if (blockAbove.typeId !== "minecraft:air") {
                        isValidSpawn = false; // Abort if any of the blocks above are not air
                        break;
                    }
                }

                // If all blocks above are air, we found a valid spawn location
                if (isValidSpawn) {
                    break; // Break out of the Y-level scan loop
                }
            }

            spawnY--; // Move down and check the next Y level
        }

        // If a valid spawn location was found, summon the creature and play sound
        if (isValidSpawn) {
            data.scareCounter++;
            dimension.runCommand(
                `summon snailstudios_scary:jester_idle ${Math.floor(spawnX)} ${Math.floor(spawnY)} ${Math.floor(spawnZ)}`
            );

            // Play a sound effect at the player's location
            dimension.runCommand(
                `playsound minecraft:entity.ghast.warn ${player.name} ${x} ${y} ${z} 1 1`
            );

            return; // Stop after successfully spawning
        }
    }
}


function spawnRealEntity(player) {
    const dimension = world.getDimension(player.dimension.id);
    const { x, y, z } = player.location;

    // Define min and max distance for spawning
    const minDistance = 15;
    const maxDistance = 25;

    // Function to generate random distance between min and max blocks
    function randomDistance(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    // Generate random distances (between minDistance and maxDistance)
    const distanceX = randomDistance(minDistance, maxDistance);
    const distanceZ = randomDistance(minDistance, maxDistance);

    // Randomize the direction (positive or negative) for both X and Z axes
    const offsetX = Math.random() < 0.5 ? distanceX : -distanceX;
    const offsetZ = Math.random() < 0.5 ? distanceZ : -distanceZ;

    // Calculate the new spawn coordinates
    const spawnX = x + offsetX;
    const spawnZ = z + offsetZ;

    // Introduce rarity check (25% chance to spawn, 75% to skip)
    const spawnChance = Math.random(); // Random number between 0 and 1

    if (spawnChance <= 0.5) { // 25% chance
        // Check for a valid surface location starting from a high Y and move downward to find ground
        let spawnY = y + 10; // Start checking from above player Y level, adjust as needed

        // Check for a valid surface and 5 air blocks above
        let isValidSpawn = false;
        while (spawnY > 0) { // Loop downward to find surface
            const blockAtLocation = dimension.getBlock({ x: Math.floor(spawnX), y: Math.floor(spawnY), z: Math.floor(spawnZ) });

            // Check if it's a solid block (not air or fluid)
            if (blockAtLocation && blockAtLocation.typeId !== "minecraft:air" && blockAtLocation.typeId !== "minecraft:water") {
                // Check if there are 5 air blocks above the surface
                let airAbove = true;
                for (let i = 1; i <= 5; i++) {
                    const blockAbove = dimension.getBlock({ x: Math.floor(spawnX), y: Math.floor(spawnY) + i, z: Math.floor(spawnZ) });
                    if (blockAbove.typeId !== "minecraft:air") {
                        airAbove = false;
                        break;
                    }
                }

                // If there are 5 air blocks above, it's a valid spawn location
                if (airAbove) {
                    isValidSpawn = true;
                    break;
                }
            }

            spawnY--; // Move down and check the next block
        }

        if (isValidSpawn) {


            // Spawn the entity at the valid location
            dimension.runCommand(`summon snailstudios_scary:jester_running ${spawnX} ${spawnY + 1} ${spawnZ}`);
        } else {

        }
    } else {
        // 75% chance to not spawn anything

    }
}

// Variable to keep track of the currently stalked player
let currentlyStalkedPlayer = null; // Store the currently stalked player
let stalkingIntervalId = null; // Store the interval ID for stalking events
let isStalkingCheckStarted = false; // Flag to check if stalking check has started

// Function to trigger a random event for the currently stalked player
function triggerRandomEvent(player) {
    const data = getPlayerData(player);

    // Check if the player is still above Y level 35
    if (player.location.y < 35) {
        // Player moved below Y 35, stop stalking and clear data
        clearPlayerData(player);
        stopStalking(); // Stop stalking the current player
        return; // Exit the function
    }

    const randomEvent = Math.random();

    if (randomEvent < 0.06) {
        checkForLogs(player);
    } else if (randomEvent < 0.09) {
        spawnCreatureNearPlayer(player);
    }

    // Once scareCounter reaches 6, trigger the final entity spawn or stop the script
    if (data.scareCounter >= 6) {
        spawnRealEntity(player);
        clearPlayerData(player);
        stopStalking(); // Stop stalking the player
    }
}

// Function to stop stalking the current player
function stopStalking() {
    if (currentlyStalkedPlayer) {
        system.clearRun(stalkingIntervalId); // Clear the stalking interval
        currentlyStalkedPlayer = null; // Reset the currently stalked player
    }
}

// Function to start checking for random events at an interval for the currently stalked player
function startRandomEventInterval(player) {
    if (stalkingIntervalId) {
        system.clearRun(stalkingIntervalId); // Clear existing interval if it exists
    }

    stalkingIntervalId = system.runInterval(() => {
        triggerRandomEvent(player);
    }, 100); // Check every 100ms
}

// Function to randomly select a player based on rarity and start stalking them
function selectPlayerToStalk() {
    const players = world.getPlayers();

    // Filter out players who are currently being stalked or are below Y level 35
    const eligiblePlayers = players.filter(player => player !== currentlyStalkedPlayer && player.location.y >= 35);

    if (eligiblePlayers.length === 0) return; // No eligible players

    // Randomly select a player based on a rarity chance
    const randomIndex = Math.floor(Math.random() * eligiblePlayers.length);
    const selectedPlayer = eligiblePlayers[randomIndex];

    // Rarity chance to stalk this player
    const rarityChance = Math.random();

    if (rarityChance < 0.05) { // 5% chance to stalk this player
        currentlyStalkedPlayer = selectedPlayer; // Set the currently stalked player
        startRandomEventInterval(selectedPlayer); // Start random events for this player
    }
}

// Function to check for stalking at intervals
function startStalkingCheck() {
    system.runInterval(() => {
        selectPlayerToStalk();
    }, 500); // Check every 2 seconds
}

// Subscribe to player join event to start stalking players
world.afterEvents.playerSpawn.subscribe(event => {
    const player = event.player; // Get the player who joined
    if (!isStalkingCheckStarted && player) {
        isStalkingCheckStarted = true; // Set flag to true
        startStalkingCheck(); // Start the stalking check loop
    }
});











// Function to get the chunk key based on player's position
function getChunkKey(playerPosition) {
    const chunkX = Math.floor(playerPosition.x / CHUNK_SIZE);
    const chunkZ = Math.floor(playerPosition.z / CHUNK_SIZE);
    return `${chunkX},${chunkZ}`; // Returns the chunk key as a string
}

// Start tracking player movement to save chunks
function startChunkSaving() {
    // Run the interval to check player positions every tick
    system.runInterval(() => {
        const players = world.getAllPlayers();

        players.forEach(player => {
            const playerPosition = player.location;

            // Get the chunk key for the current position
            const chunkKey = getChunkKey(playerPosition);

            // Retrieve previously saved chunks from the shared database
            const savedChunks = allSavedChunks.get("exploredChunks") || [];

            // Check if the chunk has already been explored
            if (!savedChunks.includes(chunkKey)) {
                // Mark this chunk as visited
                savedChunks.push(chunkKey);
                allSavedChunks.set("exploredChunks", savedChunks); // Save back to the shared database
            }
        });
    }, 1); // 1 tick interval
}

// Call the function to start saving chunks
startChunkSaving();
















// Global variables to manage structure generation and infection settings
let isStructureGenEnabled = false; // Default state
const structureGenEnabled = new JsonDatabase('structureGenEnabled'); // Create or get the database


// Array of available structures
const availableStructures = [
    'snail_scary:lab_00', 'snail_scary:lab_01', 'snail_scary:lab_02', 
    'snail_scary:big_store', 'snail_scary:camp', 'snail_scary:garage',
    'snail_scary:gas_station', 'snail_scary:little_store', 
    'snail_scary:old_cabin', 'snail_scary:radio_tower'
];

let structureGenChance = 10; // Default rate

const chancePercentage = structureGenChance * 5


// Infection settings
let infectionSliderChance = 10; // Default rate
let isInfectionOn = true; // Default state

let isDwellerOn = true; // Default state

// Event listener for item use (settings item)
world.afterEvents.itemUse.subscribe((event) => {
    const item = event.itemStack;
    const player = event.source;

    // Check if the used item is the settings item
    if (item.typeId === "snailstudios_scary:settings") {
        // Get current state from the database or use the default
        const returnIsGenOn = structureGenEnabled.get("structureGenEnabled") ?? isStructureGenEnabled;
        
        // Display the settings form to the player
        const form = new ModalFormData()
            .title("Structure Generation Settings")
            .toggle("Enable Structure Generation", returnIsGenOn) // Toggle option
            .slider("Structure Generation Chance", 1, 20, 1, structureGenChance)
            .toggle("Enable Infections", isInfectionOn)
            .slider("Infection Chance", 1, 20, 1, infectionSliderChance)
            .toggle("Enable Dweller Stalker", isDwellerOn);

        form.show(player).then((response) => {
            // Ensure the player submitted the form and the response is valid
            if (!response.canceled) {
                const values = response.formValues;

                // Update the global variables based on the player's selection
                isStructureGenEnabled = values[0];
                structureGenChance = values[1];

                // Update the infection and stalker states
                isInfectionOn = values[2];
                infectionSliderChance = values[3];
                isDwellerOn = values[4];

                // Save the new structure generation state in the database
                structureGenEnabled.set("structureGenEnabled", isStructureGenEnabled);

                // Send a message in chat based on the new state
                if (isStructureGenEnabled) {
                    world.sendMessage("Structure generation has been §aenabled");
                    startStructureChecking(availableStructures); // Start structure generation when enabled
                } else {
                    world.sendMessage("Structure generation has been §cdisabled");
                    stopStructureChecking(); // Stop structure generation when disabled
                }
            } else {
            }
        }).catch((error) => {
            // Handle errors gracefully
            console.error("Error displaying form:", error);
        });
    }
});











world.afterEvents.entityDie.subscribe((die) => {
    const deadEntity = die.deadEntity; // Entity that died

    // Skip if there's no dead entity
    if (!deadEntity) return;

    const deadTypeId = deadEntity.typeId;
    const dimension = deadEntity.dimension;

    // Skip infection logic if it's disabled
    if (!isInfectionOn) return;

    // Infection logic
    const infectionChance = infectionSliderChance * 5; // Convert slider to percentage
    const chance = Math.random() * 100;

    if (chance > infectionChance) return; // Skip if random chance fails

    const { x, y, z } = deadEntity.location;

    // Map of entities to their infected counterparts
    const infectionMapping = {
        "minecraft:chicken": "snailstudios_scary:plague_chicken",
        "minecraft:cow": "snailstudios_scary:plague_cow",
        "minecraft:sheep": "snailstudios_scary:plague_sheep",
        "minecraft:pig": "snailstudios_scary:plague_pig",
        "minecraft:goat": "snailstudios_scary:plague_goat",
        "minecraft:villager": "snailstudios_scary:ill_villager",
        "minecraft:witch": "snailstudios_scary:plague_witch",
        "minecraft:zombie": "snailstudios_scary:plague_zombie",
        "minecraft:skeleton": "snailstudios_scary:plague_skeleton",
        "minecraft:creeper": "snailstudios_scary:plague_creeper",
        "minecraft:spider": "snailstudios_scary:plague_spider",
    };

    // Find infected counterpart
    const customEntityType = infectionMapping[deadTypeId];

    if (customEntityType) {
        // Spawn infected entity
        dimension.spawnEntity(customEntityType, { x, y, z });
    } else {
        // Log if there's no matching infection logic
    }
});























const spawnWeights = {
    tier1: ['snailstudios_scary:plague_creeper', 'snailstudios_scary:ill_villager', 'snailstudios_scary:plague_skeleton', 'snailstudios_scary:plague_spider', 'snailstudios_scary:plague_zombie'], // Tier 1 mobs
    tier2: ['snailstudios_scary:creature', 'snailstudios_scary:stone_eye', 'snailstudios_scary:weeping_rock'], // Tier 2 mobs
    tier3: ['snailstudios_scary:horde_zombie', 'snailstudios_scary:horde_zombie_hazmat'] // Tier 3 mobs
};

const weights = {
    tier1: 40, // Weight for tier 1
    tier2: 25, // Weight for tier 2
    tier3: 15, // Weight for tier 3
    none: 20  // Weight for no mob to spawn
};

// Number of horde zombies to spawn if tier 3 is selected
const hordeZombieCount = 8;  // Number of normal horde zombies
const hazmatZombieCount = 4;  // Number of hazmat zombies

// Function to pick a random mob based on weights
function getRandomMob() {
    const totalWeight = weights.tier1 + weights.tier2 + weights.tier3 + weights.none;
    const randomNum = Math.random() * totalWeight;

    if (randomNum < weights.tier1) {
        return { mob: spawnWeights.tier1[Math.floor(Math.random() * spawnWeights.tier1.length)], isHorde: false };
    } else if (randomNum < weights.tier1 + weights.tier2) {
        return { mob: spawnWeights.tier2[Math.floor(Math.random() * spawnWeights.tier2.length)], isHorde: false };
    } else if (randomNum < weights.tier1 + weights.tier2 + weights.tier3) {
        // If tier 3 is selected, return as horde (specific logic for spawning 4 hazmat and 8 horde zombies)
        return { mob: 'snailstudios_scary:tier3_horde', isHorde: true }; // Use a custom identifier for tier 3 horde
    } else {
        return { mob: null, isHorde: false }; // No mob will spawn
    }
}

// Function to check for nearby players and spawn mobs
function checkNearbyPlayersAndSpawnMob(event) {
    const { block, dimension } = event;
    const blockPos = block.location;

    // Get all players in the dimension
    const players = dimension.getPlayers();

    // Check if any player is within 20 blocks of the block
    for (const player of players) {
        const distance = Math.sqrt(
            Math.pow(player.location.x - blockPos.x, 2) +
            Math.pow(player.location.y - blockPos.y, 2) +
            Math.pow(player.location.z - blockPos.z, 2)
        );

        if (distance <= 20) {
            // Pick a random mob
            const { mob, isHorde } = getRandomMob();

            if (mob === null) {
                break; // Stop if no mob should spawn
            }

            if (isHorde && mob === 'snailstudios_scary:tier3_horde') {
                // Spawn 4 hazmat zombies and 8 regular horde zombies (tier 3 specific logic)
                for (let i = 0; i < hazmatZombieCount; i++) {
                    dimension.spawnEntity('snailstudios_scary:horde_zombie_hazmat', {
                        x: blockPos.x + (Math.random() - 0.5) * 2, // Randomize spawn position
                        y: blockPos.y,
                        z: blockPos.z + (Math.random() - 0.5) * 2
                    });
                }
                for (let i = 0; i < hordeZombieCount; i++) {
                    dimension.spawnEntity('snailstudios_scary:horde_zombie', {
                        x: blockPos.x + (Math.random() - 0.5) * 2, // Randomize spawn position
                        y: blockPos.y,
                        z: blockPos.z + (Math.random() - 0.5) * 2
                    });
                }
            } else {
                // Spawn a single mob if not a horde
                dimension.spawnEntity(mob, {
                    x: blockPos.x + (Math.random() - 0.5) * 2, // Randomize spawn position
                    y: blockPos.y,
                    z: blockPos.z + (Math.random() - 0.5) * 2
                });
            // Set the block to air after spawning
            dimension.runCommand(`setblock ${blockPos.x} ${blockPos.y} ${blockPos.z} air`);
            }

            // Set the block to air after spawning
            dimension.runCommand(`setblock ${blockPos.x} ${blockPos.y} ${blockPos.z} air`);

            break; // Exit loop after spawning
        }
    }
}

// Register the custom block component
world.beforeEvents.worldInitialize.subscribe((initEvent) => {
    initEvent.blockComponentRegistry.registerCustomComponent("snailstudios_scary:mob_spawning_component", {
        onTick: checkNearbyPlayersAndSpawnMob
    });
});

const lootTiers = {
    tier1: ['snail_scary:ct1_00', 'snail_scary:ct1_01', 'snail_scary:ct1_02', 'snail_scary:ct1_03'],
    tier2: ['snail_scary:ct2_00', 'snail_scary:ct2_01', 'snail_scary:ct2_02', 'snail_scary:ct2_03'],
    tier3: ['snail_scary:ct3_00', 'snail_scary:ct3_01', 'snail_scary:ct3_02', 'snail_scary:ct3_03'],
    tier4: ['snail_scary:ct4_00'],  // Only one option for tier 4
    tier5: null // No loot for this tier
};

const weightsLoot = {
    tier1: 65, // Weight for tier 1
    tier2: 15, // Weight for tier 2
    tier3: 7, // Weight for tier 3
    tier4: 2,  // Weight for tier 4
    tier5: 10   // Weight for tier 5 (no loot)
};

// Function to get a random element from an array
function getRandomArrayElement(array) {
    return array[Math.floor(Math.random() * array.length)];
}

// Function to get random loot tier based on weights
function getRandomLootTier() {
    const totalWeight = weightsLoot.tier1 + weightsLoot.tier2 + weightsLoot.tier3 + weightsLoot.tier4 + weightsLoot.tier5;
    const randomNum = Math.random() * totalWeight;

    if (randomNum < weightsLoot.tier1) {
        return getRandomArrayElement(lootTiers.tier1); // Pick a random structure from tier 1
    } else if (randomNum < weightsLoot.tier1 + weightsLoot.tier2) {
        return getRandomArrayElement(lootTiers.tier2); // Pick a random structure from tier 2
    } else if (randomNum < weightsLoot.tier1 + weightsLoot.tier2 + weightsLoot.tier3) {
        return getRandomArrayElement(lootTiers.tier3); // Pick a random structure from tier 3
    } else if (randomNum < weightsLoot.tier1 + weightsLoot.tier2 + weightsLoot.tier3 + weightsLoot.tier4) {
        return lootTiers.tier4[0]; // Only one structure in tier 4
    } else {
        return lootTiers.tier5; // No loot for this tier
    }
}

// Function to check the block's facing direction
function getRotationForDirection(direction) {
    switch (direction) {
        case "north":
            return "90_degrees";
        case "east":
            return "180_degrees";
        case "south":
            return "270_degrees";
        case "west":
            return "0_degrees";
        default:
            return "0_degrees"; // Default to north if no direction is found
    }
}

// Function to handle loot block logic
function handleLootBlock(event) {
    const { block, dimension } = event;
    const blockPos = block.location;

    // Get the block's facing direction
    const direction = block.permutation.getState('minecraft:cardinal_direction');
    const rotation = getRotationForDirection(direction);

    // Get a random loot structure based on the weights
    const lootStructure = getRandomLootTier();
    
    if (!lootStructure) {
        return; // If loot tier 5, do nothing
    }

    // Store block's location and direction
    const storedLocation = { x: blockPos.x, y: blockPos.y, z: blockPos.z };

    // Replace the block with air
    dimension.runCommandAsync(`setblock ${blockPos.x} ${blockPos.y} ${blockPos.z} air`)
        .then(() => {
            // Place the selected structure with correct rotation
            dimension.runCommandAsync(`structure load ${lootStructure} ${storedLocation.x} ${storedLocation.y} ${storedLocation.z} ${rotation}`)
                .then(() => {
                })
                .catch(error => {
                });
        })
        .catch(error => {
        });
}

// Register the custom block component for block loot
world.beforeEvents.worldInitialize.subscribe((initEvent) => {
    initEvent.blockComponentRegistry.registerCustomComponent("snailstudios_scary:block_loot_component", {
        onPlace: handleLootBlock
    });
});
































































// Event listener for player joining
world.afterEvents.playerSpawn.subscribe((event) => {
    const player = event.player;

    // Get the saved state from the database or use the default
    const returnIsGenOn = structureGenEnabled.get("structureGenEnabled") ?? isStructureGenEnabled;
    isStructureGenEnabled = returnIsGenOn; // Update global variable with database state

    // Check if structure generation is enabled and no interval is running
    if (isStructureGenEnabled && !intervalIdStructure) {
        startStructureChecking(); // Start the interval if not already running
    }
});

// Function to get a random structure ID from randomPick that is enabled (true)
function getRandomStructure() {

    // Filter the structures to include only those that are enabled (true)
    const enabledStructures = availableStructures[Math.floor(Math.random() * availableStructures.length)];

    // Select a random structure from the enabled list
    const selectedStructure = enabledStructures[Math.floor(Math.random() * enabledStructures.length)];
    return selectedStructure;
}

const structures = [
    { id: getRandomStructure(), rotation: [0, 90, 180, -90] }, // Random structure for grass
    { id: 'snail_scary:lab_01', rotation: [0, 90, 180, -90] }  // Smaller structure for sand
];

// Store occupied chunks
const occupiedChunks = new Set();
const allSavedChunks = new JsonDatabase('allSavedChunks');
const CHUNK_SIZE = 64; // Size of a chunk in blocks (64 for 4x4 blocks)
let intervalIdStructure = null; // Initialize interval ID as null

const MAX_ENTRIES = 28000;
const MAX_DATABASES = 10;
let chunkDatabases = [];

// Initialize multiple chunk databases
for (let i = 0; i < MAX_DATABASES; i++) {
    chunkDatabases.push(new JsonDatabase(`allSavedChunks_${i}`));
}

// Function to get the current database that isn't full
function getCurrentDatabase() {
    for (let i = 0; i < chunkDatabases.length; i++) {
        const currentDb = chunkDatabases[i];
        const allEntries = currentDb.toJSON ? currentDb.toJSON() : {}; // Assuming toJSON() fetches all entries
        const currentSize = Object.keys(allEntries).length;
        if (currentSize < MAX_ENTRIES) {
            return currentDb;
        }
    }
    throw new Error('All chunk databases are full!'); // Error if all are full
}

// Function to check and switch to the next database if the current one is full
function checkAndSwitchDatabase() {
    try {
        getCurrentDatabase(); // Check if there is a database with space
    } catch (error) {
    }
}

// Function to save a chunk to the appropriate database
function saveChunkToDatabase(chunkX, chunkZ) {
    const db = getCurrentDatabase(); // Get the available database
    const chunkKey = `${chunkX},${chunkZ}`;
    
    // Check if the chunk is already saved across all databases
    if (isChunkSaved(chunkX, chunkZ)) {
        return;
    }

    db.set(chunkKey, true); // Save the chunk
}

// Function to check if a chunk is already saved across all databases
function isChunkSaved(chunkX, chunkZ) {
    const chunkKey = `${chunkX},${chunkZ}`;
    for (const db of chunkDatabases) {
        if (db.has(chunkKey)) { // Assuming has() checks for existence of the key
            return true;
        }
    }
    return false;
}

// Start the structure checking process
function startStructureChecking() {
    // If there's already an interval running, cancel it first
    if (intervalIdStructure) {
        system.clearRun(intervalIdStructure);
    }

    // Start a new interval for structure checking
    intervalIdStructure = system.runInterval(() => {
        if (!isStructureGenEnabled) return; // Do nothing if generation is disabled

        const players = world.getAllPlayers();

        players.forEach(player => {
            const playerPosition = player.location;
            const playerDimension = player.dimension.id;

            const chunkX = Math.floor(playerPosition.x / CHUNK_SIZE);
            const chunkZ = Math.floor(playerPosition.z / CHUNK_SIZE);

            const chunkKey = `${chunkX},${chunkZ},${playerDimension}`;

            // Check if the chunk has already been explored across all databases
            if (!isChunkSaved(chunkX, chunkZ) && !isChunkOccupied(chunkX, chunkZ)) {
                // Save the chunk key to the current database
                saveChunkToDatabase(chunkX, chunkZ);

                // Generate a structure in the new chunk
                generateStructure(player, chunkX, chunkZ);
            }
        });
    }, 40); // Interval delay of 40 ticks
}

// Stop the structure checking process
function stopStructureChecking() {
    if (intervalIdStructure) {
        system.clearRun(intervalIdStructure);
        intervalIdStructure = null; // Reset the interval ID
    }
}

function isChunkOccupied(chunkX, chunkZ) {
    const occupied = occupiedChunks.has(`${chunkX},${chunkZ}`);
    return occupied;
}

// Generate structure based on the player's location and block type
function generateStructure(player, chunkX, chunkZ) {
    const dimension = player.dimension;
    const centerX = chunkX * CHUNK_SIZE + 12;
    const centerZ = chunkZ * CHUNK_SIZE + 12;

    let centerY = null;
    for (let y = 70; y >= 40; y--) {
        const blockAtY = dimension.getBlock({ x: centerX, y: y, z: centerZ });
        if (blockAtY && (blockAtY.typeId === "minecraft:sand" || blockAtY.typeId === "minecraft:grass_block")) {
            centerY = y;
            break;
        }
    }

    if (centerY === null) {
        return;
    }

    // Check if there is a villager nearby before placing the structure
    const villagerNearby = isVillagerNearby(player, dimension, centerX, centerY, centerZ, 40); // 40-block radius
    if (villagerNearby) {
        return;
    }

    const blockBelow = dimension.getBlock({ x: centerX, y: centerY, z: centerZ });
    const corners = [];
    let cornerHeights = [];

    // Define the rectangle corners
    if (blockBelow && blockBelow.typeId === "minecraft:grass_block") {
        const width = 26;  // Total width of the rectangle (12 on each side of the center)
        const height = 20; // Total height of the rectangle (10 on each side of the center)

        // Calculate half the dimensions
        const halfWidth = width / 2;
        const halfHeight = height / 2;

        corners.push(
            { x: centerX - halfWidth, z: centerZ - halfHeight }, // Bottom-left
            { x: centerX - halfWidth, z: centerZ + halfHeight }, // Top-left
            { x: centerX + halfWidth, z: centerZ - halfHeight },  // Bottom-right
            { x: centerX + halfWidth, z: centerZ + halfHeight }   // Top-right
        );

        // Check the height of each corner for sand structure
        for (const corner of corners) {
            let foundHeight = false; // Flag to track if a valid height has been found
        
            for (let y = 70; y >= 40; y--) {
                const blockAtCorner = dimension.getBlock({ x: corner.x, y: y, z: corner.z });
                
                if (blockAtCorner) {
                    // Check for sand, grass, or water blocks
                    if (blockAtCorner.typeId === "minecraft:sand" || blockAtCorner.typeId === "minecraft:grass_block") {
                        cornerHeights.push(y);
                        foundHeight = true; // Valid height found
                        break; // Stop checking once we hit sand or grass
                    } else if (blockAtCorner.typeId === "minecraft:water") {
                        // If water is found, we should not proceed with this corner
                        break; // Stop checking this corner if water is found
                    }
                }
            }
        
            // If no valid height was found for this corner, send a message and return
            if (!foundHeight) {
                return; // Exit if no valid height found
            }
        }

        // Check if corner heights are roughly the same as centerY
        const allHeightsEqual = cornerHeights.every(height => Math.abs(height - centerY) <= 3); // Allow a tolerance of 2 blocks

        if (allHeightsEqual) {
            const randomChance = Math.random() * 100;
            if (randomChance <= chancePercentage) {
                const selectedStructureId = availableStructures[Math.floor(Math.random() * availableStructures.length)];
                

                // Set the location based on the selected structure
                let structureHeightOffset;
                if (selectedStructureId === 'snail_scary:lab_00') {
                    structureHeightOffset = - 22; // Specific height for lab_00
                } else if (selectedStructureId === 'snail_scary:lab_02') {
                    structureHeightOffset = - 6; // Specific height for lab_02
                } else if (selectedStructureId === 'snail_scary:big_store') {
                    structureHeightOffset = - 1; // Specific height for big_store
                } else if (selectedStructureId === 'snail_scary:camp') {
                    structureHeightOffset = - 0; // Specific height for camp
                } else if (selectedStructureId === 'snail_scary:garage') {
                    structureHeightOffset = - 1; // Specific height for garage
                } else if (selectedStructureId === 'snail_scary:gas_station') {
                    structureHeightOffset = - 2; // Specific height for gas_station
                } else if (selectedStructureId === 'snail_scary:little_store') {
                    structureHeightOffset = - 1; // Specific height for little_store
                } else if (selectedStructureId === 'snail_scary:old_cabin') {
                    structureHeightOffset = - 1; // Specific height for old_cabin
                } else if (selectedStructureId === 'snail_scary:radio_tower') {
                    structureHeightOffset = - 6; // Specific height for radio_tower
                }

                const location = { x: centerX - 17, y: centerY + structureHeightOffset, z: centerZ - 12 }; // Adjusted height
                placeStructure(selectedStructureId, location);
                markOccupiedChunks(chunkX, chunkZ);

            } else {
            }
        } else {
        }
    } else if (blockBelow && blockBelow.typeId === "minecraft:sand") {
        corners.push(
            { x: centerX - 5, z: centerZ - 5 },
            { x: centerX - 5, z: centerZ + 5 },
            { x: centerX + 5, z: centerZ - 5 },
            { x: centerX + 5, z: centerZ + 5 }
        );

// Check the height of each corner for sand structure
for (const corner of corners) {
    let foundHeight = false; // Flag to track if a valid height has been found

    for (let y = 70; y >= 40; y--) {
        const blockAtCorner = dimension.getBlock({ x: corner.x, y: y, z: corner.z });
        
        if (blockAtCorner) {
            // Check for sand, grass, or water blocks
            if (blockAtCorner.typeId === "minecraft:sand") {
                cornerHeights.push(y);
                foundHeight = true; // Valid sand height found
                break; // Stop checking once we hit sand
            } else if (blockAtCorner.typeId === "minecraft:grass_block") {
                cornerHeights.push(y);
                foundHeight = true; // Valid grass height found
                break; // Stop checking once we hit grass
            } else if (blockAtCorner.typeId === "minecraft:water") {
                // If water is found, we should not proceed with this corner
                break; // Stop checking this corner if water is found
            }
        }
    }

    // If no valid height was found for this corner, send a message and return
    if (!foundHeight) {
        return; // Exit if no valid height found
    }
}

// Check if corner heights are roughly the same as centerY
const allSandHeightsEqual = cornerHeights.every(height => Math.abs(height - centerY) <= 2); // Allow a tolerance of 2 blocks

        if (allSandHeightsEqual) {
            const randomChance = Math.random() * 100;
            if (randomChance <= chancePercentage) {
                const structureName = 'snail_scary:lab_01'; // Directly use the structure name
                const structureInfo = structures.find(struct => struct.id === structureName);
                
                if (!structureInfo) {
                    return; // Exit if structureInfo is undefined
                }
                
                const location = { x: centerX - 38, y: centerY - 33, z: centerZ - 23 }; // Place 34 blocks below the sand
                
                // Use structureInfo.id instead of structureInfo
                placeStructure(structureInfo.id, location); 
                markOccupiedChunks(chunkX, chunkZ);

            } else {
            }
        } else {
        }
    }

    checkAndSwitchDatabase();
}

// Function to check if there is a villager within a certain distance
function isVillagerNearby(player, dimension, centerX, centerY, centerZ, radius) {
    const villagers = dimension.getEntities();
    for (const villager of villagers) {
        if (villager.typeId === 'minecraft:villager') {
            const distanceSquared = (villager.location.x - centerX) ** 2 + (villager.location.z - centerZ) ** 2;
            if (distanceSquared <= radius ** 2) {
                return true; // A villager is within the specified radius
            }
        }
    }
    return false; // No villagers found within the radius
}

// Function to place the structure using its ID and location
function placeStructure(structureInfo, location) {

    const dimension = world.getDimension("overworld");
    dimension.runCommandAsync(`structure load ${structureInfo} ${location.x} ${location.y} ${location.z}`)
        .then(() => {
        })
        .catch(error => {
        });
}

// Function to mark chunks as occupied
function markOccupiedChunks(chunkX, chunkZ) {
    occupiedChunks.add(`${chunkX},${chunkZ}`);
}

// Optionally, you might want to load previously saved chunk data when starting the script
function loadChunkData() {
    const savedChunks = allSavedChunks.get("occupiedChunks") || [];
    savedChunks.forEach(chunk => occupiedChunks.add(chunk));
}

// Call loadChunkData at the start of your script to initialize occupied chunks
loadChunkData();





















// Store interval ID to prevent stacking intervals
let intervalIdWeeping = null;

// Function to handle the weeping angel behavior
function weepingAngelBehavior() {
    world.getPlayers().forEach((player) => {
        // Define the raycast parameters
        const raycastOptions = {
            maxDistance: 20 // Check within 20 block radius
        };

        // Raycast to find entities in the player's view direction
        const hitEntities = player.getEntitiesFromViewDirection(raycastOptions);

        // Create an array to store the type IDs of the hit entities
        const hitEntityTypeIds = hitEntities.map(hitResult => hitResult.entity.typeId);

        // Check if the player is looking at a "snailstudios_scary:weeping_rock"
        if (hitEntityTypeIds.includes("snailstudios_scary:weeping_rock")) {
            
            // Apply the default component group if looking at the entity
            hitEntities.forEach(hit => {
                if (hit.entity.typeId === "snailstudios_scary:weeping_rock") {
                    hit.entity.runCommandAsync('event entity @s snailstudios_default');
                }
            });
        } else {
            // If not looking at the entity, apply the active component group
            const dimension = player.dimension;
            const entities = dimension.getEntities({
                type: "snailstudios_scary:weeping_rock",
                location: player.location,
                maxDistance: 20
            });

            entities.forEach((entity) => {
                entity.runCommandAsync('event entity @s snailstudios_active');
            });
        }
    });
}

// Function to start the interval when a player joins
function startInterval() {
    if (!intervalIdWeeping) {
        intervalIdWeeping = system.runInterval(() => {
            weepingAngelBehavior();
        }, 10); // Run every 10 ticks (0.5 seconds)
    }
}

// Event listener for player joining
world.afterEvents.playerSpawn.subscribe((event) => {
    startInterval();
});

// Event listener for player leaving (optional cleanup)
world.beforeEvents.playerLeave.subscribe(() => {
    // Optionally stop the interval if no players are in the world
    if (world.getPlayers().length === 0 && intervalIdWeeping) {
        system.clearRun(intervalIdWeeping);
        intervalIdWeeping = null;
    }
});

