import { MinecraftDimensionTypes, world, system, BlockTypes, ItemUseAfterEvent, Player, EntityInventoryComponent,system as Za, world as ts, EntityDamageCause, ItemStack, EquipmentSlot, Direction } from "@minecraft/server";
import { ModalFormData, MessageFormData, ActionFormData } from "@minecraft/server-ui";

const scoreboard = world.scoreboard;

let currentSpawnRate = "medium";
let blockBreakingEnabled = true;
let birdsEnabled = true;
let currentStageBehavior = 0;

function initScoreboard() {
  if (!scoreboard.getObjective("hunter_stage_behavior")) {
    scoreboard.addObjective("hunter_stage_behavior", "Stage Behavior (0=loop,1=no loop,2=forced)");
  }
  if (!scoreboard.getParticipants().find(p => p.displayName === "settings_stage_behavior")) {
    scoreboard.getObjective("hunter_stage_behavior").setScore("settings_stage_behavior", 1);
  }

  if (!scoreboard.getObjective("spawn_rate")) {
    scoreboard.addObjective("spawn_rate", "Spawn Rate (0=low, 1=medium, 2=high)");
  }
  if (!scoreboard.getObjective("block_breaking")) {
    scoreboard.addObjective("block_breaking", "Block Breaking Enabled (1=true, 0=false)");
  }
  if (!scoreboard.getObjective("bird_system_enabled")) {
    scoreboard.addObjective("bird_system_enabled", "Bird System Enabled (1=true, 0=false)");
  }
  
  const participants = scoreboard.getParticipants();
  if (!participants.find(p => p.displayName === "settings_spawn")) {
    scoreboard.getObjective("spawn_rate").setScore("settings_spawn", 1);
  }
  if (!participants.find(p => p.displayName === "settings_breaking")) {
    scoreboard.getObjective("block_breaking").setScore("settings_breaking", 1);
  }
  if (!participants.find(p => p.displayName === "settings_bird")) {
    scoreboard.getObjective("bird_system_enabled").setScore("settings_bird", 1);
  }
  }

function loadSettings() {
  
  const spawnObj = scoreboard.getObjective("spawn_rate");
  const breakingObj = scoreboard.getObjective("block_breaking");
  const birdObj = scoreboard.getObjective("bird_system_enabled");
  
  const spawnPart = scoreboard.getParticipants().find(p => p.displayName === "settings_spawn");
  const breakingPart = scoreboard.getParticipants().find(p => p.displayName === "settings_breaking");
  const birdPart = scoreboard.getParticipants().find(p => p.displayName === "settings_bird");
  
  const spawnScore = spawnObj.getScore(spawnPart);
  const breakingScore = breakingObj.getScore(breakingPart);
  const birdScore = birdObj.getScore(birdPart);

  const stageObj = scoreboard.getObjective("hunter_stage_behavior");
  const stagePart = scoreboard.getParticipants().find(p => p.displayName === "settings_stage_behavior");
  if (stageObj && stagePart) currentStageBehavior = stageObj.getScore(stagePart);
  
  currentSpawnRate = ["low", "medium", "high"][spawnScore] || "medium";
  blockBreakingEnabled = breakingScore === 1;
  birdsEnabled = birdScore === 1;
  }

initScoreboard();
loadSettings();

function showGuidebook(player) {
  const mainMenu = new ActionFormData()
    .title("Call of The Forest Guidebook")
    .body("Choose a category for more information about the add-on:")
    .button("Settings")
    .button("Changelog")
    .button("Information")
    .button("Mechanics");

  mainMenu.show(player).then(response => {
    if (response.canceled) return;

    if (response.selection === 0) {
      showSettingsMenu(player);
    } else if (response.selection === 1) {
      showChangelog(player);
    } else if (response.selection === 2) {
      showInformation(player);

    } else if (response.selection === 3) {
      showMechanics(player);
    }
  });
}

function showSpawnRateForm(player) {
  const form = new ModalFormData()
    .title("Creature Spawn Rate")
    .dropdown("Select spawn rate", ["Low", "Medium", "High"], ["low", "medium", "high"].indexOf(currentSpawnRate));

  form.show(player).then(resp => {
    if (resp.canceled) return;

    const selectedIndex = resp.formValues[0];
    currentSpawnRate = ["low", "medium", "high"][selectedIndex];
    scoreboard.getObjective("spawn_rate").setScore("settings_spawn", selectedIndex);
    player.sendMessage(`§aSpawn rate set to: ${currentSpawnRate.toUpperCase()}`);
  });
}

const playerEntityMap = new Map();
const fogNextParticleTick = new Map();

const entityOwnerMap = new Map();
const birdNextSoundTick = new Map();
let currentTick = 0;

function spawnEntityForPlayer(player) {
  if (!player || !player.location) return;
  if (player.location.y < 62) return;
  if (playerEntityMap.has(player.id)) return;

  const pos = { x: player.location.x, y: player.location.y, z: player.location.z };
  const overworld = world.getDimension("overworld");
  const entity = overworld.spawnEntity("tc:bird_controller", pos);

  playerEntityMap.set(player.id, entity);
  entityOwnerMap.set(entity, player.id);

  teleportEntityToPlayer(player, entity);
}

function teleportEntityToPlayer(player, entity) {
  const tickCallback = () => {
    if (!player?.isValid() || !entity?.isValid()) {
      playerEntityMap.delete(player?.id);
      entityOwnerMap.delete(entity);
      birdNextSoundTick.delete(entity);
      fogNextParticleTick.delete(player?.id);
      return;
    }

    if (player.location.y < 62) {
      entity.remove();
      return;
    }

    const playerDimension = player.dimension.id;
    const entityDimension = entity.dimension.id;
    if (playerDimension !== entityDimension) {
      entity.remove();
      return;
    }

    const yaw = player.getRotation().y;
    const radians = (yaw + 180) * (Math.PI / 180);
    const distance = 1;
    const backX = -Math.sin(radians) * distance;
    const backZ = Math.cos(radians) * distance;

    const pos = {
      x: player.location.x + backX,
      y: player.location.y + 3,
      z: player.location.z + backZ
    };

    entity.teleport(pos, { dimension: player.dimension });

    system.run(tickCallback);
  };

  system.run(tickCallback);
}


system.runInterval(() => {
  currentTick++;

  const overworld = world.getDimension("overworld");
  const birds = overworld.getEntities({ type: "tc:bird_controller" });

  for (const bird of birds) {
    const ownerId = entityOwnerMap.get(bird);
    if (!ownerId) continue;
  
    const ownerPlayer = world.getPlayers().find(p => p.id === ownerId);
    if (!ownerPlayer) continue;
  
    if (birdsEnabled) {
      bird.triggerEvent("tc:birds_enabled");
    } else {
      bird.triggerEvent("tc:birds_disabled");
    }
  
    const birdForestProperty = bird.getProperty("tc:birdforest");
    const birdToggleProperty = bird.getProperty("tc:birdtoggle");
    
    if (birdForestProperty && birdToggleProperty === 2 && birdsEnabled) {
      const nearbyCraetures = bird.dimension.getEntities({
        type: "tc:creature",
        location: bird.location,
        maxDistance: 60
      });
    
      if (nearbyCraetures.length === 0) {
        const nextTick = birdNextSoundTick.get(bird) || 0;
        if (currentTick >= nextTick) {
          ownerPlayer.runCommand(`playsound mob.bird_controller.ambient @s`);
          const delayTicks = 200 + Math.floor(Math.random() * 300);
          birdNextSoundTick.set(bird, currentTick + delayTicks);
        }
      }
    }
  
      }

  for (const player of world.getPlayers()) {
    if (player.location.y < 62) {
      const entity = playerEntityMap.get(player.id);
      if (entity?.isValid()) {
        entity.remove();
      }
      playerEntityMap.delete(player.id);
      entityOwnerMap.delete(entity);
      birdNextSoundTick.delete(entity);
      fogNextParticleTick.delete(player.id);
      continue;
    }
    if (!playerEntityMap.has(player.id) && player.location) {
      spawnEntityForPlayer(player);
    }
  }
}, 1);

function showChangelog(player) {
  const changelogText = `
§l§aCall of The Forest Version 1.1 Preview§r

§fThis is a Preview Version 1.1 of the Call of The Forest Add-On.

Additions:

-added a chance to get weakness from the Hunter when hurt.
-added and changed many animations.
-added and changed multiple sounds.
-added lots of new ai pathways and options.
-added an item called the Heart of The Forest, which can be obtained by killing the Hunter.
-added better close up encounters with the Hunter, like fainting and panicking effects.
-added snowy footsteps.
-added hunting phases.

Changes:

-improved manifest compatability.
-fixed Vibrant Visuals compatability for some of the textures.
-fixed information tab executing the wrong event.
-fixed a bug which made the Hunter attack players randomly.
-updated grabbing behavior.
-changed the Hunters model and texture.

And much more!
`;

  const changelogForm = new ActionFormData()
    .title("Changelog")
    .body(changelogText)
    .button("Close");

  changelogForm.show(player).then(() => {});
}
function showInformation(player) {
  const informationText = `
This horror add-on adds a monster, known as "The Hunter" to your world. The Hunter is a large, intelligent bear, whose only goal is to hunt you down and kill you. Hence the name. Throughout your play through, you'll notice something is always off. A mysterious sound might echo from the forest. Or perhaps you get the spine-chilling feeling that you're being watched. This add-on creates a sense of constant psychological horror. But is it psychological? Or is something actually there? Stay alert, and never let your guard down. He's on the hunt.
`;

  const informationForm = new ActionFormData()
    .title("Information")
    .body(informationText)
    .button("Close");

  informationForm.show(player).then(() => {});
}
function showMechanics(player) {
  const mechanicsText = `
WARNING: Reading past this point will spoil the gameplay in this add-on.

The Hunter spawns in forests only, and has progressive ai. The longer you play, the more aggressive The Hunter will be. He can do anything from stalk you from a far distance, to place multiple traps around the forest. If you get caught in one of these traps, it will alert The Hunter to your location. Aside from The Hunter, there is one other mob in the add-on: birds. Birds will spawn in forests, chirping subtley in the background to create a constant ambience. They stop chirping at night, when they sleep. If they stop chirping during the daytime... just run.
`;

  const mechanicsForm = new ActionFormData()
    .title("Mechanics")
    .body(mechanicsText)
    .button("Close");

    mechanicsForm.show(player).then(() => {});
}
world.afterEvents.itemUse.subscribe((event) => {
  const { itemStack, source } = event;
  if (!(source instanceof Player)) return;
  if (itemStack?.typeId === "tc:call_of_the_forest_settings") {
    showGuidebook(source);
  }
});

world.afterEvents.entitySpawn.subscribe((event) => {
  const newEntity = event.entity;
  if (newEntity.typeId !== HUNTER_ENTITY_TYPE) return;

  const existing = newEntity.dimension.getEntities({
    type: HUNTER_ENTITY_TYPE,
    maxDistance: 10000,
  });
  for (const entity of existing) {
    if (entity.id !== newEntity.id) {
      const nearbyPlayers = newEntity.dimension.getPlayers({
        location: newEntity.location,
        maxDistance: 5,
      });
      for (const player of nearbyPlayers) {
        player.runCommandAsync('tellraw @s {"rawtext":[{"text":"§4You cannot spawn multiple Hunters."}]}');
      }
      newEntity.remove();
      return;
    }
  }

  if (currentStageBehavior === 2) {
    newEntity.triggerEvent("tc:behavior_stage5");
    newEntity.setProperty("tc:stageconf", true);
  }
});
const HUNTER_KNOCKBACK_ENTITY_TYPE = 'tc:creature';
const HUNTER_KNOCKBACK_FORCE = 1.5;
const HUNTER_VERTICAL_FORCE = 1.0;
const HUNTER_KNOCKBACK_CHANCE = 0.3;
const HUNTER_SHIELD_DISABLE_DURATION = 200;
const HUNTER_EXTRA_WOLF_DAMAGE = 29;

world.afterEvents.entityHurt.subscribe((event) => {
  const { damageSource, hurtEntity } = event;
  const damager = damageSource.damagingEntity;

  if (!damager || damager.typeId !== HUNTER_KNOCKBACK_ENTITY_TYPE) return;
  if (!hurtEntity) return;

  if (hurtEntity.typeId === 'minecraft:player') {
    if (Math.random() <= HUNTER_KNOCKBACK_CHANCE) {
      const dx = hurtEntity.location.x - damager.location.x;
      const dz = hurtEntity.location.z - damager.location.z;
      const distance = Math.sqrt(dx * dx + dz * dz);
      if (distance !== 0) {
        const knockbackX = (dx / distance) * HUNTER_KNOCKBACK_FORCE;
        const knockbackZ = (dz / distance) * HUNTER_KNOCKBACK_FORCE;

        hurtEntity.applyKnockback(knockbackX, knockbackZ, HUNTER_KNOCKBACK_FORCE, HUNTER_VERTICAL_FORCE);
      }
      try {
        hurtEntity.runCommandAsync(`effect @s weakness 5 1 true`);
      } catch (err) {
        console.warn("TC: Failed to apply Weakness effect:", err);
      }
      try {
        hurtEntity.startItemCooldown("shield", HUNTER_SHIELD_DISABLE_DURATION);
      } catch (err) {
        console.warn("TC: Failed to apply shield cooldown:", err);
      }
    }
  }
  if (hurtEntity.typeId === 'minecraft:wolf') {
    try {
      hurtEntity.runCommand(`damage @s ${HUNTER_EXTRA_WOLF_DAMAGE}`);
    } catch (err) {
      console.warn("TC: Failed to apply extra damage to wolf:", err);
    }
  }
});

function isAir(block) {
  return block === undefined
    || block.isAir
    || block.typeId === "minecraft:air";
}

function isStepDownAir(block) {
  return block === undefined
    || block.isStepDownAir
    || block.typeId === "minecraft:air"
    || block.typeId === "minecraft:snow_layer"
    || block.typeId === "minecraft:short_grass"
    || block.typeId === "minecraft:tall_grass";
}

const leapCooldowns = new WeakMap();
const LEAP_COOLDOWN_TICKS = 80;

function applyLeapAndStepDownLogic(entity) {
  if (!entity || !entity.isOnGround || entity.isClimbing) return;

  const pos = entity.location;
  const dir = entity.getViewDirection();
  const dimension = entity.dimension;

  const horizontalMag = Math.sqrt(dir.x * dir.x + dir.z * dir.z);
  if (horizontalMag === 0) return;

  const forward = {
    x: dir.x / horizontalMag,
    y: 0,
    z: dir.z / horizontalMag
  };

  try {
    const movementProp = entity.getProperty("tc:movement");
    if (movementProp === 2) {
      const checkDown = {
        x: pos.x + forward.x,
        y: pos.y - 1,
        z: pos.z + forward.z
      };

      const blockBelow = dimension.getBlock(checkDown);
      const groundBelow = dimension.getBlock({
        x: checkDown.x,
        y: checkDown.y - 1,
        z: checkDown.z
      });

      if (isStepDownAir(blockBelow) && !isStepDownAir(groundBelow)) {
        entity.teleport(
          {
            x: checkDown.x,
            y: checkDown.y + 0.01,
            z: checkDown.z
          },
          {
            facingLocation: {
              x: pos.x + dir.x,
              y: pos.y + dir.y,
              z: pos.z + dir.z
            }
          }
        );
      }
    }
  } catch (err) {
    console.warn("Failed to read tc:movement property:", err);
  }

  try {
    const chasing = entity.getProperty("tc:chasing");
    if (!chasing) return;
  } catch (err) {
    console.warn("Failed to read tc:chasing property:", err);
    return;
  }

  const ahead = {
    x: pos.x + forward.x * 1.6,
    y: pos.y + 1,
    z: pos.z + forward.z * 1.6
  };

  const blockBelowAhead = dimension.getBlockFromRay(
    ahead,
    { x: 0, y: -1, z: 0 },
    { maxDistance: 3, includePassableBlocks: true }
  );

  const isGap = !blockBelowAhead || isAir(blockBelowAhead.block);
  if (!isGap) return;

  const lastLeapTick = leapCooldowns.get(entity) ?? -Infinity;
  const currentTick = system.currentTick;

  if (currentTick - lastLeapTick < LEAP_COOLDOWN_TICKS) return;
  leapCooldowns.set(entity, currentTick);

  const players = entity.dimension.getPlayers();
  let closestPlayer = null;
  let closestDistSq = Infinity;

  for (const player of players) {
    const dx = player.location.x - entity.location.x;
    const dy = (player.location.y + 1.6) - (entity.location.y + 1.6);
    const dz = player.location.z - entity.location.z;
    const distSq = dx * dx + dy * dy + dz * dz;

    if (distSq < closestDistSq && distSq < 10000) {
      closestPlayer = player;
      closestDistSq = distSq;
    }
  }

  if (closestPlayer) {
    const dx = closestPlayer.location.x - entity.location.x;
    const dz = closestPlayer.location.z - entity.location.z;
    const dy = (closestPlayer.location.y + 1.6) - (entity.location.y + 1.6);

    const yaw = -Math.atan2(dx, dz) * (180 / Math.PI);
    const pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)) * (180 / Math.PI);

    try {
      entity.setRotation({ x: pitch, y: yaw });
    } catch (err) {
      console.warn("Leap: Could not rotate entity to face player:", err);
    }
  }

  const viewDir = entity.getViewDirection();
  const newHorizontalMag = Math.sqrt(viewDir.x * viewDir.x + viewDir.z * viewDir.z);
  const newForward = {
    x: viewDir.x / newHorizontalMag,
    y: 0,
    z: viewDir.z / newHorizontalMag
  };

let distance = Math.sqrt(closestDistSq);
distance = Math.min(distance, 60);

const horizScale = 1.2 + (distance / 60) * 2.0;
const vertScale = 0.18 + (distance / 60) * 0.35;

const powerRandom = 0.95 + Math.random() * 0.25;
const overshootFactor = 1.15;

const impulse = {
  x: newForward.x * horizScale * powerRandom * overshootFactor,
  y: vertScale * powerRandom,
  z: newForward.z * horizScale * powerRandom * overshootFactor
};

  entity.applyImpulse(impulse);
}


system.runInterval(() => {
  for (const entity of world.getDimension("overworld").getEntities({ type: "tc:creature" })) {
    try {
      applyLeapAndStepDownLogic(entity);
    } catch (e) {
      console.warn("Error with tc:creature behavior:", e);
    }
  }
}, 5);
    
function getArmorPoints(player) {
  let armorPoints = 0;
  const slots = [
    EquipmentSlot.Head,
    EquipmentSlot.Chest,
    EquipmentSlot.Legs,
    EquipmentSlot.Feet,
  ];

  for (const slot of slots) {
    const item = player.getComponent("equippable").getEquipment(slot);
    if (!item) continue;

    const armorValues = {
      "minecraft:leather_helmet": 1,
      "minecraft:leather_boots": 1,
      "minecraft:leather_leggings": 2,
      "minecraft:leather_chestplate": 3,

      "minecraft:iron_helmet": 2,
      "minecraft:golden_helmet": 2,
      "minecraft:chainmail_helmet": 2,

      "minecraft:iron_boots": 2,
      "minecraft:golden_boots": 1,
      "minecraft:chainmail_boots": 1,

      "minecraft:iron_leggings": 5,
      "minecraft:golden_leggings": 3,
      "minecraft:chainmail_leggings": 4,

      "minecraft:iron_chestplate": 6,
      "minecraft:golden_chestplate": 5,
      "minecraft:chainmail_chestplate": 5,

      "minecraft:diamond_helmet": 3,
      "minecraft:netherite_helmet": 3,

      "minecraft:diamond_boots": 3,
      "minecraft:netherite_boots": 3,

      "minecraft:diamond_leggings": 6,
      "minecraft:netherite_leggings": 6,

      "minecraft:diamond_chestplate": 8,
      "minecraft:netherite_chestplate": 8,
    };

    if (armorValues[item.typeId]) {
      armorPoints += armorValues[item.typeId];
    }

    const enchComp = item.getComponent("enchantments");
    if (enchComp) {
      const prot = enchComp.enchantments.getEnchantment("protection");
      if (prot) {
        armorPoints += prot.level * 2;
      }
    }
  }

  return armorPoints;
}

function getStageDamage(stage) {
  switch (stage) {
    case 1: return 4;
    case 2: return 6;
    case 3: return 8;
    case 4: return 10;
    case 5: return 12;
    default: return 3;
  }
}

world.afterEvents.entityHitEntity.subscribe(event => {
  const { damagingEntity, hitEntity } = event;
  if (!damagingEntity || damagingEntity.typeId !== HUNTER_ENTITY_TYPE) return;
  if (!hitEntity || hitEntity.typeId !== "minecraft:player") return;

  const stage = damagingEntity.getProperty("tc:stage") ?? 1;

  let damage = getStageDamage(stage);

  const armorPoints = getArmorPoints(hitEntity);
  damage += armorPoints * 0.5;

  try {
    hitEntity.applyDamage(damage, {
      cause: EntityDamageCause.entityAttack,
      damagingEntity,
    });
  } catch (err) {
    console.warn("Hunter failed to deal damage:", err);
  }
});

const TC_BREAK_COOLDOWNS = new Map();

const TC_UNBREAKABLES = new Set([
  "minecraft:air",
  "minecraft:end_portal_frame",
  "minecraft:end_portal",
  "minecraft:repeating_command_block",
  "minecraft:chain_command_block",
  "minecraft:structure_block",
  "minecraft:structure_void",
  "minecraft:allow",
  "minecraft:deny",
  "minecraft:light_block_0",
  "minecraft:light_block_1",
  "minecraft:light_block_2",
  "minecraft:light_block_3",
  "minecraft:light_block_4",
  "minecraft:light_block_5",
  "minecraft:light_block_6",
  "minecraft:light_block_7",
  "minecraft:light_block_8",
  "minecraft:light_block_9",
  "minecraft:light_block_10",
  "minecraft:light_block_11",
  "minecraft:light_block_12",
  "minecraft:light_block_13",
  "minecraft:light_block_14",
  "minecraft:light_block_15",
  "minecraft:water",
  "minecraft:flowing_water",
  "minecraft:lava",
  "minecraft:nether_portal",
  "minecraft:invisible_bedrock",
  "minecraft:mob_spawner",
  "minecraft:bubble_column",
  "minecraft:bedrock",
  "minecraft:beehive",
  "minecraft:bee_nest",
  "minecraft:border_block",
  "minecraft:barrier",
  "minecraft:balloon",
  "minecraft:bleach",
  "minecraft:client_request_placeholder_block",
  "minecraft:camera",
  "minecraft:chemical_heat",
  "minecraft:command_block",
  "minecraft:chemistry_table",
  "minecraft:daylight_detector_inverted",
  "minecraft:end_gateway",
  "minecraft:flowing_lava",
  "minecraft:fire",
  "minecraft:glow_stick",
  "minecraft:hard_glass",
  "minecraft:hard_glass_pane",
  "minecraft:hard_stained_glass",
  "minecraft:hard_stained_glass_pane",
  "minecraft:info_update",
  "minecraft:info_update2",
  "minecraft:invisibleBedrock",
  "minecraft:invisiblebedrock",
  "minecraft:ice_bomb",
  "minecraft:netherreactor",
  "minecraft:netherReactor",
  "minecratf:movingBlock",
  "minecraft:movingblock",
  "minecraft:melon_stem",
  "minecraft:monster_egg",
  "minecraft:medicine",
  "minecraft:lit_furnace",
  "minecraft:lit_blast_furnace",
  "minecraft:lit_smoker",
  "minecraft:lit_redstone_lamp",
  "minecraft:lit_redstone_ore",
  "minecraft:lava_cauldron",
  "minecraft:powered_comparator",
  "minecraft:powered_repeater",
  "minecraft:pumpkin_stem",
  "minecraft:potatoes",
  "minecraft:pistonArmCollision",
  "minecraft:pistomarmcollision",
  "minecraft:portal",
  "minecraft:reserved3",
  "minecraft:reserved4",
  "minecraft:reserved6",
  "minecraft:redstone_wire",
  "minecraft:rapid_fertilizer",
  "minecraft:stonecutter",
  "minecraft:stickyPistonArmCollision",
  "minecraft:stickypistonarmcollision",
  "minecraft:soul_fire",
  "minecraft:sparkler",
  "minecraft:spawn_egg",
  "minecraft:unknown",
  "minecraft:unpowered_repeater",
  "minecraft:unpowered_comparator",
  "minecraft:unlit_redstone_torch",
  "minecraft:underwater_torch",
  "minecraft:wall_sign",
  "minecraft:wall_banner",
  "minecraft:glowingobsidian"
]);

function TC_isBreakable(block) {
  return block && !block.isAir && !TC_UNBREAKABLES.has(block.typeId);
}

function TC_breakBlockWithDrop(block, dimension) {
  if (!TC_isBreakable(block)) return;
  const blockType = block.typeId;
  const location = block.location;

  console.log(`[tc:creature] Breaking block ${blockType} at ${location.x},${location.y},${location.z}`);
  try {
    block.setType("minecraft:air");
    dimension.spawnItem(new ItemStack(blockType, 1), location);
  } catch (err) {
    console.warn(`[tc:creature] Failed to drop ${blockType}:`, err);
  }
}

system.runInterval(() => {
  if (!blockBreakingEnabled) return;

  const overworld = world.getDimension("overworld");

  for (const player of overworld.getPlayers()) {
    const creatures = overworld.getEntities({ type: HUNTER_ENTITY_TYPE });

    for (const creature of creatures) {
      const movementProp = Number(creature.getProperty("tc:movement"));

      if (movementProp !== 2) continue;

      const creatureId = creature.id;
      const lastTime = TC_BREAK_COOLDOWNS.get(creatureId) ?? 0;
      const now = system.currentTick;

      if (now - lastTime < 40) continue;
      TC_BREAK_COOLDOWNS.set(creatureId, now);

      const creatureLoc = creature.location;
      const playerLoc = player.location;

      let dir = {
        x: playerLoc.x - creatureLoc.x,
        y: playerLoc.y - creatureLoc.y,
        z: playerLoc.z - creatureLoc.z
      };
      const length = Math.sqrt(dir.x ** 2 + dir.y ** 2 + dir.z ** 2);
      if (length === 0) continue;

      dir.x /= length;
      dir.y /= length;
      dir.z /= length;

      const offsets = [
        [{ x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 1, y: 1, z: 0 }],
        [{ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 1 }],
        [{ x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }],
        [{ x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: -1, y: 1, z: 0 }, { x: 0, y: 1, z: 0 }],
        [{ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 1, z: -1 }, { x: 0, y: 1, z: 0 }],
        [{ x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: -1 }, { x: -1, y: 1, z: -1 }, { x: 0, y: 1, z: 0 }],
      ];

      let blockBroken = false;

      for (let i = 1; i <= 4 && !blockBroken; i++) {
        const offsetCluster = offsets[Math.floor(Math.random() * offsets.length)];

        for (const offset of offsetCluster) {
          const bx = Math.floor(creatureLoc.x + dir.x * i + offset.x);
          const by = Math.floor(creatureLoc.y + dir.y * i + offset.y);
          const bz = Math.floor(creatureLoc.z + dir.z * i + offset.z);

          const blockLoc = { x: bx, y: by, z: bz };
          const block = creature.dimension.getBlock(blockLoc);

          if (TC_isBreakable(block)) {
            creature.runCommandAsync("event entity @s tc:break_block");
            creature.runCommandAsync("playsound break.dripstone_block @a[r=20]");
            system.runTimeout(() => {
              creature.runCommandAsync("event entity @s tc:break_block_end");
            }, 3);

            TC_breakBlockWithDrop(block, creature.dimension);
            blockBroken = true;
          } else {
            console.log(`[tc:creature] Block at ${bx},${by},${bz} not breakable: ${block?.typeId}`);
          }
        }
      }

      if (!blockBroken) console.log(`[tc:creature] No valid blocks found to break for creature ${creatureId}`);
    }
  }
}, 20);

const HUNTER_FOLLOW_DISTANCE = 3.2;
const HUNTER_FOLLOW_HEIGHT = 0.5;

system.runInterval(() => {
  const overworld = world.getDimension("overworld");
  const players = overworld.getPlayers();
  const hunters = overworld.getEntities({ type: HUNTER_ENTITY_TYPE });

  for (const hunter of hunters) {
    if (hunter.getProperty("tc:in_water") !== true) continue;
    if (hunter.getProperty("tc:chasing") !== true) continue;

    let closestPlayer = null;
    let closestDistSq = Infinity;

    for (const player of players) {
      const dx = player.location.x - hunter.location.x;
      const dy = player.location.y - hunter.location.y;
      const dz = player.location.z - hunter.location.z;
      const distSq = dx * dx + dy * dy + dz * dz;

      if (distSq < 10 * 10 && distSq < closestDistSq) {
        closestPlayer = player;
        closestDistSq = distSq;
      }
    }

    if (!closestPlayer) continue;

    try {
      const yaw = closestPlayer.getRotation().y;
      const radians = (yaw + 180) * (Math.PI / 180);

      const backX = -Math.sin(radians) * HUNTER_FOLLOW_DISTANCE;
      const backZ = Math.cos(radians) * HUNTER_FOLLOW_DISTANCE;

      const pos = {
        x: closestPlayer.location.x + backX,
        y: closestPlayer.location.y + HUNTER_FOLLOW_HEIGHT,
        z: closestPlayer.location.z + backZ,
      };

      const blockPos = {
        x: Math.floor(pos.x),
        y: Math.floor(pos.y),
        z: Math.floor(pos.z),
      };
      const block = overworld.getBlock(blockPos);
      if (!block || !block.typeId.includes("water")) {
        continue;
      }

      hunter.teleport(pos, {
        dimension: hunter.dimension,
        facingLocation: closestPlayer.location,
      });
    } catch (err) {
      console.warn("Hunter teleport failed:", err);
    }
  }
}, 10);

      function getDistance(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;
        const dz = a.z - b.z;
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
      }
      
      system.runInterval(() => {
        const overworld = world.getDimension("overworld");
        const creatures = overworld.getEntities({ type: "tc:creature" });
      
        for (const creature of creatures) {
          const creatureLoc = creature.location;
          let playerNearby = false;
      
          for (const player of world.getPlayers()) {
            const playerLoc = player.location;
            if (getDistance(creatureLoc, playerLoc) <= 90) {
              playerNearby = true;
              break;
            }
          }
      
          if (!playerNearby) {
            try {
              creature.remove();
            } catch (err) {
              console.warn(`Failed to remove tc:creature: ${err}`);
            }
          }
        }
      }, 20);

      function getDistanceAmbient(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;
        const dz = a.z - b.z;
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
      }
      
      system.runInterval(() => {
        const overworld = world.getDimension("overworld");
        const ambients = overworld.getEntities({ type: "tc:ambient" });
      
        for (const ambient of ambients) {
          const ambientLoc = ambient.location;
          let playerNearby = false;
      
          for (const player of world.getPlayers()) {
            const playerLoc = player.location;
            if (getDistanceAmbient(ambientLoc, playerLoc) <= 40) {
              playerNearby = true;
              break;
            }
          }
      
          if (!playerNearby) {
            try {
              ambient.remove();
            } catch (err) {
              console.warn(`Failed to remove tc:ambient: ${err}`);
            }
          }
        }
      }, 1);

      const HUNTER_SLOW_CREATURE_TYPE = "tc:creature";
      const HUNTER_SLOW_CHECK_INTERVAL = 10;
      const HUNTER_SLOW_EFFECT_DURATION = 40;
      
      function isBreakable(block) {
        if (!block || block.isAir) return false;
      
        const unbreakables = [
          "minecraft:air",
          "minecraft:end_portal_frame",
          "minecraft:end_portal",
          "minecraft:repeating_command_block",
          "minecraft:chain_command_block",
          "minecraft:structure_block",
          "minecraft:structure_void",
          "minecraft:allow",
          "minecraft:deny",
          "minecraft:light_block_0",
          "minecraft:light_block_1",
          "minecraft:light_block_2",
          "minecraft:light_block_3",
          "minecraft:light_block_4",
          "minecraft:light_block_5",
          "minecraft:light_block_6",
          "minecraft:light_block_7",
          "minecraft:light_block_8",
          "minecraft:light_block_9",
          "minecraft:light_block_10",
          "minecraft:light_block_11",
          "minecraft:light_block_12",
          "minecraft:light_block_13",
          "minecraft:light_block_14",
          "minecraft:light_block_15",
          "minecraft:water",
          "minecraft:flowing_water",
          "minecraft:lava",
          "minecraft:nether_portal",
          "minecraft:invisible_bedrock",
          "minecraft:mob_spawner",
          "minecraft:bubble_column",
          "minecraft:bedrock",
          "minecraft:beehive",
          "minecraft:bee_nest",
          "minecraft:border_block",
          "minecraft:barrier",
          "minecraft:balloon",
          "minecraft:bleach",
          "minecraft:client_request_placeholder_block",
          "minecraft:camera",
          "minecraft:chemical_heat",
          "minecraft:command_block",
          "minecraft:chemistry_table",
          "minecraft:daylight_detector_inverted",
          "minecraft:end_gateway",
          "minecraft:flowing_lava",
          "minecraft:fire",
          "minecraft:glow_stick",
          "minecraft:hard_glass",
          "minecraft:hard_glass_pane",
          "minecraft:hard_stained_glass",
          "minecraft:hard_stained_glass_pane",
          "minecraft:info_update",
          "minecraft:info_update2",
          "minecraft:invisibleBedrock",
          "minecraft:invisiblebedrock",
          "minecraft:ice_bomb",
          "minecraft:netherreactor",
          "minecraft:netherReactor",
          "minecratf:movingBlock",
          "minecraft:movingblock",
          "minecraft:melon_stem",
          "minecraft:monster_egg",
          "minecraft:medicine",
          "minecraft:lit_furnace",
          "minecraft:lit_blast_furnace",
          "minecraft:lit_smoker",
          "minecraft:lit_redstone_lamp",
          "minecraft:lit_redstone_ore",
          "minecraft:lava_cauldron",
          "minecraft:powered_comparator",
          "minecraft:powered_repeater",
          "minecraft:pumpkin_stem",
          "minecraft:potatoes",
          "minecraft:pistonArmCollision",
          "minecraft:pistomarmcollision",
          "minecraft:portal",
          "minecraft:reserved3",
          "minecraft:reserved4",
          "minecraft:reserved6",
          "minecraft:redstone_wire",
          "minecraft:rapid_fertilizer",
          "minecraft:stonecutter",
          "minecraft:stickyPistonArmCollision",
          "minecraft:stickypistonarmcollision",
          "minecraft:soul_fire",
          "minecraft:sparkler",
          "minecraft:spawn_egg",
          "minecraft:unknown",
          "minecraft:unpowered_repeater",
          "minecraft:unpowered_comparator",
          "minecraft:unlit_redstone_torch",
          "minecraft:underwater_torch",
          "minecraft:wall_sign",
          "minecraft:wall_banner",
          "minecraft:glowingobsidian"
        ];
      
        return !unbreakables.includes(block.typeId);
      }
      
      function breakBlockWithDrop(block, dimension) {
        if (!block || !isBreakable(block)) return;
      
        const blockType = block.typeId;
        const location = block.location;
      
        try {
          block.setType("minecraft:air");
          dimension.spawnItem(new ItemStack(blockType, 1), location);
        } catch (err) {
          console.warn(`Failed to simulate breaking block ${blockType}:`, err);
        }
      }
      
      function breakBlocksAroundEntity(entity) {
        const dimension = entity.dimension;
        const loc = entity.location;
      
        const centerX = Math.floor(loc.x);
        const centerY = Math.floor(loc.y);
        const centerZ = Math.floor(loc.z);
      
        for (let dx = -1; dx <= 1; dx++) {
          for (let dz = -1; dz <= 1; dz++) {
            if (dx === 0 && dz === 0) continue;
      
            const block = dimension.getBlock({
              x: centerX + dx,
              y: centerY,
              z: centerZ + dz
            });
      
            breakBlockWithDrop(block, dimension);
          }
        }
      }
      function applySlownessEffect(entity) {
        try {
          entity.addEffect("slowness", HUNTER_SLOW_EFFECT_DURATION, {
            amplifier: 1,
            showParticles: false,
            showIcon: true
          });
        } catch (err) {
          console.warn("Failed to apply slowness effect:", err);
        }
      }
      
      system.runInterval(() => {
        if (!blockBreakingEnabled) return;
      
        const overworld = world.getDimension("overworld");
        const creatures = overworld.getEntities({ type: HUNTER_SLOW_CREATURE_TYPE });
      
        for (const creature of creatures) {
          const pose = creature.getProperty("tc:pose");
          if (pose === 1) {
            applySlownessEffect(creature);
            breakBlocksAroundEntity(creature);
          }
        }
      }, HUNTER_SLOW_CHECK_INTERVAL);

const ambientSoundIntervals = {
  high: [15000, 18000],
  medium: [17000, 20000],
  low: [19000, 22000],
};

const stageAmbientMultipliers = [1.5, 1.3, 1.1, 1.0, 1.0];

function getStageAdjustedAmbient(baseRange) {
  const [min, max] = baseRange;
  const multiplier = stageAmbientMultipliers[currentStageIndex] ?? 1.0;
  return [
    Math.floor(min * multiplier),
    Math.floor(max * multiplier)
  ];
}


function attemptPlayAmbientSounds() {
  const players = world.getPlayers();
  if (players.length === 0) {
    system.runTimeout(attemptPlayAmbientSounds, 1000);
    return;
  }

  const player = players[Math.floor(Math.random() * players.length)];
  if (!player || !player.isValid()) {
    system.runTimeout(attemptPlayAmbientSounds, 1000);
    return;
  }

  const nearbyCreatures = player.dimension.getEntities({
    location: player.location,
    maxDistance: 50,
    type: "tc:creature",
  });

  if (nearbyCreatures.length > 0) {
    console.warn(`[AmbientDebug] Stage ${currentStageIndex + 1} → Skipping, creature nearby`);
  } else {
    console.warn(`[AmbientDebug] Stage ${currentStageIndex + 1} → Playing forest ambience`);
    playAmbientSoundIfForest(player);
  }

  const adjustedRange = getStageAdjustedAmbient(ambientSoundIntervals[currentSpawnRate]);
  const delay = getRandomInterval(adjustedRange);

  console.warn(`[AmbientDebug] Stage ${currentStageIndex + 1} → Delay Range: ${adjustedRange[0]}-${adjustedRange[1]}, Picked: ${delay}`);

  system.runTimeout(attemptPlayAmbientSounds, delay);
}

function playAmbientSoundIfForest(player) {
  if (!player || !player.isValid()) {
    console.warn("[DEBUG] playAmbientSoundIfForest called with invalid player");
    return;
  }

  const dim = player.dimension;
  const { x, z } = player.location;
  const startY = 320;

  let y = startY;
  let foundSurface = false;

  while (y > 0) {
    const block = dim.getBlock({ x: Math.floor(x), y, z: Math.floor(z) });
    if (block && !block.isAir && !block.isLiquid) {
      foundSurface = true;
      break;
    }
    y--;
  }

  if (!foundSurface) {
    console.warn(`[DEBUG] No surface found above player`);
    return;
  }

  const spawnLoc = { x, y: y + 1, z };
  const detector = dim.spawnEntity("tc:ambient", spawnLoc);
  if (!detector) {
    console.warn(`[DEBUG] Failed to spawn tc:ambient at surface`);
    return;
  }

  let ticksWaited = 0;
  const interval = system.runInterval(() => {
    ticksWaited++;
    if (!detector.isValid()) {
      system.clearRun(interval);
      return;
    }

    const detectionValue = detector.getProperty("tc:ambientdetection");
    if (detectionValue === 1) {
      detector.triggerEvent("tc:play_sound");
      system.clearRun(interval);
    } else if (detectionValue === 2) {
      detector.triggerEvent("tc:vanish");
      system.clearRun(interval);
    }

    if (ticksWaited > 40) {
      detector.remove();
      system.clearRun(interval);
    }
  }, 1);
}
attemptPlayAmbientSounds();

const HUNTER_SPAWN_ENTITY_TYPE = "tc:creature";
const HUNTER_MAX_ENTITIES = 1;
const HUNTER_SPAWN_TAG = "horror_spawned";
const spawnRates = {
  high: [5600, 8600],
  medium: [7400, 11000],
  low: [9800, 14600],
};

function triggerMidSpawnEvent(stage, players) {
  const player = players[Math.floor(Math.random() * players.length)];
  if (!player) return;

  console.warn(`[SpawnDebug] Mid-event triggered at Stage ${stage + 1} for player ${player.name}`);

  isInForestBiome(player, (isForest) => {
    if (!isForest) {
      console.warn("[SpawnDebug] Player not in forest, mid-event aborted.");
      return;
    }

    if (stage <= 1) {
      if (Math.random() < 0.7) {
        console.warn("[SpawnDebug] Playing rock loop instead of spawning creature.");
        playRockLoop(player);
      } else {
        console.warn("[SpawnDebug] Silent observe creature spawn triggered.");
        spawnCreatureSilent(player, "tc:behavior_observe");
      }
    } 
    else if (stage === 2) {
      if (Math.random() < 0.7) {
        console.warn("[SpawnDebug] Dirt patch event triggered.");
        spawnDirtPatch(player, true);
      } else {
        console.warn("[SpawnDebug] Silent hunt creature spawn triggered.");
        spawnCreatureSilent(player, "tc:behavior_hunt");
      }
    } 
    else {
      const roll = Math.random();
      if (roll < 0.4) {
        console.warn("[SpawnDebug] Dirt patch + fire ritual triggered.");
        spawnDirtPatchWithFire(player);
      } else if (roll < 0.8) {
        console.warn("[SpawnDebug] Silent hunt creature spawn triggered.");
        spawnCreatureSilent(player, "tc:behavior_hunt");
      } else {
        console.warn("[SpawnDebug] Rock distant sound triggered instead of spawn.");
        try { player.runCommand(`playsound mob.creature.rock_distant @s`); } catch {}
      }
    }
  });
}

function playRockLoop(player) {
  function loop() {
    try {
      player.runCommand(`playsound mob.creature.rock_distant @s`);
      console.warn(`[SpawnDebug] Rock loop sound played for ${player.name}`);
    } catch (e) {
      console.warn("[SpawnDebug] Rock loop sound failed:", e);
    }

    const delay = getRandomInterval([100, 200]);
    system.runTimeout(() => {
      if (Math.random() < 0.5) {
        console.warn("[SpawnDebug] Rock loop ended.");
        return;
      }
      loop();
    }, delay);
  }
  loop();
}

function spawnCreatureSilent(player, behavior) {
  console.warn(`[SpawnDebug] Silent spawn requested near ${player.name}, behavior = ${behavior}`);
  
  const spawnTick = system.currentTick;
  trySpawnNearPlayer(player);

  system.runTimeout(() => {
    const entities = player.dimension.getEntities({ type: HUNTER_SPAWN_ENTITY_TYPE });
    if (!entities.length) {
      console.warn("[SpawnDebug] No hunter entities found to assign behavior.");
      return;
    }

    const last = entities[entities.length - 1];
    if (!last) return;

    if (!last.getTags().some(t => t.startsWith("birthTick:"))) {
      last.addTag(`birthTick:${spawnTick}`);
      console.warn(`[SpawnDebug] Marked entity with birthTick:${spawnTick}`);
    }

    let birthTick = null;
    for (const tag of last.getTags()) {
      if (tag.startsWith("birthTick:")) {
        birthTick = parseInt(tag.split(":")[1]);
        break;
      }
    }

    if (birthTick === null) return;

    const age = system.currentTick - birthTick;
    if (age <= 120) {
      console.warn(`[SpawnDebug] Entity is ${age} ticks old → Triggering ${behavior}`);
      last.triggerEvent(behavior);
    } else {
      console.warn(`[SpawnDebug] Entity too old (${age} ticks) → Skipping behavior trigger.`);
    }
  }, 60);
}

function spawnDirtPatch(player, playSound = false) {
  const dim = player.dimension;

  const allowedGround = new Set([
    "minecraft:dirt",
    "minecraft:grass_block",
    "minecraft:podzol",
    "minecraft:coarse_dirt"
  ]);

  const ignoredBlocks = new Set([
    "minecraft:oak_leaves",
    "minecraft:spruce_leaves",
    "minecraft:birch_leaves",
    "minecraft:jungle_leaves",
    "minecraft:acacia_leaves",
    "minecraft:dark_oak_leaves",
    "minecraft:mangrove_leaves",
    "minecraft:azalea_leaves",
    "minecraft:flowering_azalea_leaves",
    "minecraft:cherry_leaves",
    "tc:redwood_leaves",
    "minecraft:snow_layer"
  ]);

  const replaceableAbove = new Set([
    "minecraft:air",
    "minecraft:snow",
    "minecraft:snow_layer",
    "minecraft:tall_grass",
    "minecraft:fern",
    "minecraft:large_fern",
    "minecraft:flower",
    "minecraft:poppy",
    "minecraft:dandelion",
    "minecraft:blue_orchid",
    "minecraft:allium",
    "minecraft:red_tulip",
    "minecraft:orange_tulip",
    "minecraft:white_tulip",
    "minecraft:pink_tulip",
    "minecraft:oxeye_daisy",
    "minecraft:lilac",
    "minecraft:sunflower",
    "minecraft:rose_bush",
    "minecraft:peony",
    "minecraft:grass",
    "minecraft:bush"
  ]);

  let loc = null;
  const maxAttempts = 100;

  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const angle = Math.random() * Math.PI * 2;
    const distance = 15 + Math.random() * 5;
    const x = Math.floor(player.location.x + Math.cos(angle) * distance);
    const z = Math.floor(player.location.z + Math.sin(angle) * distance);

    let candidateY = null;

    for (let y = 320; y > 0; y--) {
      const block = dim.getBlock({ x, y, z });
      if (!block) continue;

      if (ignoredBlocks.has(block.typeId)) continue;

      if (allowedGround.has(block.typeId)) {
        const above = dim.getBlock({ x, y: y + 1, z });
        if (!above || replaceableAbove.has(above.typeId)) {
          candidateY = y;
          break;
        }
      }
    }

    if (candidateY !== null) {
      loc = { x, y: candidateY, z };
      break;
    }
  }

  if (!loc) {
    console.warn("[SpawnDebug] Failed to find valid patch location after max attempts.");
    return;
  }

  const patchBlocks = [];
  for (let dx = -2; dx <= 2; dx++) {
    for (let dz = -2; dz <= 2; dz++) {
      const x = loc.x + dx;
      const z = loc.z + dz;
      const y = loc.y;

      const block = dim.getBlock({ x, y, z });
      if (!block || !allowedGround.has(block.typeId)) continue;

      const above = dim.getBlock({ x, y: y + 1, z });
      if (!above || !replaceableAbove.has(above.typeId)) continue;

      if (above.typeId === "minecraft:snow" || above.typeId === "minecraft:snow_layer") {
        above.setType("minecraft:air");
      }

      const r = Math.random();
      if (r < 0.7) block.setType("minecraft:coarse_dirt");
      else if (r < 0.9) block.setType("minecraft:grass_path");
      else block.setType("minecraft:dirt");

      patchBlocks.push({ x, y, z });
    }
  }

  if (playSound) {
    try { player.runCommand(`playsound mob.creature.rock_distant @s`); } catch {}
  }

  console.warn(`[SpawnDebug] Dirt patch generated at [${loc.x}, ${loc.y}, ${loc.z}], size ~${patchBlocks.length}`);
  return patchBlocks;
}

function spawnDirtPatchWithFire(player) {
  console.warn("[SpawnDebug] Attempting dirt patch + fire event.");

  const patchBlocks = spawnDirtPatch(player, false);
  if (!patchBlocks || patchBlocks.length === 0) {
    console.warn("[SpawnDebug] No patch blocks generated → aborting fire event.");
    return;
  }

  const dim = player.dimension;

  const fireSpot = patchBlocks[Math.floor(Math.random() * patchBlocks.length)];
  const fireBase = dim.getBlock(fireSpot);
  if (!fireBase) return;

  fireBase.setType("minecraft:netherrack");
  const fireAbove = dim.getBlock({ x: fireSpot.x, y: fireSpot.y + 1, z: fireSpot.z });
  if (fireAbove && (fireAbove.typeId === "minecraft:air" || fireAbove.typeId === "minecraft:snow" || fireAbove.typeId === "minecraft:snow_layer")) {
    fireAbove.setType("minecraft:fire");
  }

  console.warn(`[SpawnDebug] Fire ritual patch created at [${fireSpot.x}, ${fireSpot.y}, ${fireSpot.z}]`);

  const allowedGround = new Set([
    "minecraft:dirt",
    "minecraft:grass_block",
    "minecraft:podzol",
    "minecraft:coarse_dirt"
  ]);

  let spawnLoc = null;

  for (let dx = -2; dx <= 2 && !spawnLoc; dx++) {
    for (let dz = -2; dz <= 2 && !spawnLoc; dz++) {
      if (dx === 0 && dz === 0) continue;

      const ground = dim.getBlock({ x: fireSpot.x + dx, y: fireSpot.y, z: fireSpot.z + dz });
      const above = dim.getBlock({ x: fireSpot.x + dx, y: fireSpot.y + 1, z: fireSpot.z + dz });

      if (!ground || !above) continue;
      if (!allowedGround.has(ground.typeId)) continue;
      if (ground.typeId === "minecraft:netherrack") continue;
      if (above.typeId !== "minecraft:air") continue;
      if (above.typeId === "minecraft:fire") continue;
      if (ground.typeId === "minecraft:fire") continue;

      spawnLoc = { x: fireSpot.x + dx, y: fireSpot.y + 1, z: fireSpot.z + dz };
    }
  }

  if (!spawnLoc) {
    console.warn("[SpawnDebug] No valid adjacent spawn location → skipping creature spawn.");
    return;
  }

  const spawnTick = system.currentTick;
  const entity = dim.spawnEntity(HUNTER_SPAWN_ENTITY_TYPE, spawnLoc);
  if (entity) {
    entity.addTag(HUNTER_SPAWN_TAG);
    entity.addTag(`birthTick:${spawnTick}`);
    console.warn(`[SpawnDebug] Ritual creature spawned at [${spawnLoc.x}, ${spawnLoc.y}, ${spawnLoc.z}] with birthTick:${spawnTick}`);
  }

  system.runTimeout(() => {
    if (!entity) return;

    let birthTick = null;
    for (const tag of entity.getTags()) {
      if (tag.startsWith("birthTick:")) {
        birthTick = parseInt(tag.split(":")[1]);
        break;
      }
    }

    if (birthTick === null) return;

    const age = system.currentTick - birthTick;
    if (age <= 120) {
      console.warn(`[SpawnDebug] Entity is ${age} ticks old → Triggering tc:behavior_distant_fire`);
      entity.triggerEvent("tc:behavior_distant_fire");
    } else {
      console.warn(`[SpawnDebug] Entity too old (${age} ticks) → Skipping distant_fire behavior.`);
    }
  }, 60);
}

function getRandomInterval([min, max]) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function isInForestBiome(player, callback) {
  const dim = player.dimension;
  const loc = player.location;

  const spawnerEntity = dim.spawnEntity("tc:spawner", loc);
  let ticksWaited = 0;
  let callbackCalled = false;

  const interval = system.runInterval(() => {
    if (!spawnerEntity || !spawnerEntity.isValid()) {
      if (!callbackCalled) {
        callbackCalled = true;
        callback(false);
      }
      system.clearRun(interval);
      return;
    }

    ticksWaited++;

    const value = spawnerEntity.getProperty("tc:spawnerdetection");

    if (value === 1 || value === 2) {
      system.clearRun(interval);
      if (spawnerEntity && spawnerEntity.isValid()) spawnerEntity.remove();

      if (!callbackCalled) {
        callbackCalled = true;
        callback(value === 1);
      }
    }

    if (ticksWaited > 40) {
      system.clearRun(interval);
      if (spawnerEntity && spawnerEntity.isValid()) spawnerEntity.remove();

      if (!callbackCalled) {
        callbackCalled = true;
        callback(false);
      }
    }
  }, 1);
}

function countSpawnedEntities(dimension) {
  return dimension.getEntities({
    tags: [HUNTER_SPAWN_TAG],
    type: HUNTER_SPAWN_ENTITY_TYPE,
  }).length;
}

function trySpawnNearPlayer(player) {
  console.warn(`[SpawnDebug] Trying to spawn creature near ${player.name}`);
  isInForestBiome(player, (isForest) => {
    if (!isForest) {
      console.warn("[SpawnDebug] Player not in forest, spawn aborted.");
      return;
    }

    const dimension = player.dimension;
    if (countSpawnedEntities(dimension) >= HUNTER_MAX_ENTITIES) {
      console.warn("[SpawnDebug] Spawn cap reached, aborting.");
      return;
    }

    const allowedBlocks = new Set([
      "minecraft:grass_block",
      "minecraft:dirt",
      "minecraft:coarse_dirt",
      "minecraft:podzol",
      "minecraft:snow",
      "minecraft:snow_layer",
      "minecraft:bush",
      "minecraft:tall_grass"
    ]);

    const ignoredBlocks2 = new Set([
      "minecraft:oak_leaves",
      "minecraft:spruce_leaves",
      "minecraft:birch_leaves",
      "minecraft:jungle_leaves",
      "minecraft:acacia_leaves",
      "minecraft:dark_oak_leaves",
      "minecraft:mangrove_leaves",
      "minecraft:azalea_leaves",
      "minecraft:snow_layer",
      "minecraft:flowering_azalea_leaves",
      "minecraft:cherry_leaves",
      "tc:redwood_leaves"
    ]);

    const maxAttempts = 200;
    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      const minDistance = 18;
      const maxDistance = 38;
      const angle = Math.random() * Math.PI * 2;
      const distance = minDistance + Math.random() * (maxDistance - minDistance);
      const x = Math.floor(player.location.x + Math.cos(angle) * distance);
      const z = Math.floor(player.location.z + Math.sin(angle) * distance);

      let y = 320;
      let solidY = null;

      while (y > 0) {
        const block = dimension.getBlock({ x, y, z });
        if (block && !block.isAir && !ignoredBlocks2.has(block.typeId)) {
          solidY = y + 1;
          break;
        }
        y--;
      }

      if (solidY === null) continue;

      const groundBlock = dimension.getBlock({ x, y: solidY - 1, z });
      if (!groundBlock || !allowedBlocks.has(groundBlock.typeId)) continue;

      const spawnLocation = { x, y: solidY, z };
      const entity = dimension.spawnEntity(HUNTER_SPAWN_ENTITY_TYPE, spawnLocation);

      if (entity) {
        entity.addTag(HUNTER_SPAWN_TAG);

        const birthTick = system.currentTick;
        entity.addTag(`birthTick:${birthTick}`);
        console.warn(`[SpawnDebug] Creature spawned at [${spawnLocation.x}, ${spawnLocation.y}, ${spawnLocation.z}] with birthTick:${birthTick}`);
      } else {
        console.warn("[SpawnDebug] Spawn attempt failed at", spawnLocation);
      }

      return;
    }

    console.warn("[SpawnDebug] No valid spawn locations found after max attempts.");
  });
}

const stageSpawnMultipliers = [1.6, 1.3, 1.1, 1.0, 1.0];

function getStageAdjustedInterval(baseRange) {
  const [min, max] = baseRange;
  const multiplier = stageSpawnMultipliers[currentStageIndex] ?? 1.0;
  return [
    Math.floor(min * multiplier),
    Math.floor(max * multiplier)
  ];
}

function attemptSpawnCreature() {
  const players = world.getPlayers();
  if (players.length === 0) {
    system.runTimeout(attemptSpawnCreature, 1000);
    return;
  }

  const adjustedRange = getStageAdjustedInterval(spawnRates[currentSpawnRate]);
  const delay = getRandomInterval(adjustedRange);

  console.warn(`[SpawnDebug] Stage ${currentStageIndex + 1} → Delay Range: ${adjustedRange[0]}-${adjustedRange[1]}, Picked: ${delay}`);

  const halfDelay = Math.floor(delay / 2);
  system.runTimeout(() => {
    if (Math.random() < 0.2) {
      console.warn(`[SpawnDebug] Mid-event triggered for stage ${currentStageIndex + 1}`);
      triggerMidSpawnEvent(currentStageIndex, players);
    }
  }, halfDelay);

  system.runTimeout(() => {
    const ambienceChance = [0.05, 0.1, 0.15, 0.2, 0.2][currentStageIndex] ?? 0.2;

    if (Math.random() < ambienceChance) {
      console.warn(`[SpawnDebug] Stage ${currentStageIndex + 1} → Playing ambience (chance ${ambienceChance})`);

      players.forEach((p) => {
        isInForestBiome(p, (isForest) => {
          if (isForest) {
            try { p.runCommand(`playsound mob.creature.ambience @s`); } 
            catch (e) { console.warn("[SpawnDebug] Sound failed:", e); }
          }
        });
      });

      const soundDelay = getRandomInterval([400, 1000]);
      console.warn(`[SpawnDebug] Extra sound delay before spawn: ${soundDelay}`);

      system.runTimeout(() => {
        const player = players[Math.floor(Math.random() * players.length)];
        trySpawnNearPlayer(player);
        attemptSpawnCreature();
      }, soundDelay);

    } else {
      console.warn(`[SpawnDebug] Stage ${currentStageIndex + 1} → No ambience, spawning directly`);
      const player = players[Math.floor(Math.random() * players.length)];
      trySpawnNearPlayer(player);
      attemptSpawnCreature();
    }
  }, delay);
}

system.run(() => {
  attemptSpawnCreature();
});

  const TICKS_TO_BREAK = 10;

  const targetBlockIds = new Set([
    "minecraft:oak_leaves",
    "minecraft:spruce_leaves",
    "minecraft:birch_leaves",
    "minecraft:jungle_leaves",
    "minecraft:acacia_leaves",
    "minecraft:dark_oak_leaves",
    "minecraft:mangrove_leaves",
    "minecraft:cherry_leaves",
    "minecraft:azalea_leaves",
    "minecraft:flowering_azalea_leaves"
  ]);
  
  function isBreakableLeaf(block) {
    if (!block || block.isAir) return false;
  
    const unbreakables = new Set([
      "minecraft:air",
      "minecraft:end_portal_frame",
      "minecraft:end_portal",
      "minecraft:repeating_command_block",
      "minecraft:chain_command_block",
      "minecraft:structure_block",
      "minecraft:structure_void",
      "minecraft:allow",
      "minecraft:deny",
      "minecraft:light_block_0",
      "minecraft:light_block_1",
      "minecraft:light_block_2",
      "minecraft:light_block_3",
      "minecraft:light_block_4",
      "minecraft:light_block_5",
      "minecraft:light_block_6",
      "minecraft:light_block_7",
      "minecraft:light_block_8",
      "minecraft:light_block_9",
      "minecraft:light_block_10",
      "minecraft:light_block_11",
      "minecraft:light_block_12",
      "minecraft:light_block_13",
      "minecraft:light_block_14",
      "minecraft:light_block_15",
      "minecraft:water",
      "minecraft:flowing_water",
      "minecraft:lava",
      "minecraft:nether_portal",
      "minecraft:invisible_bedrock",
      "minecraft:mob_spawner",
      "minecraft:bubble_column",
      "minecraft:bedrock",
      "minecraft:beehive",
      "minecraft:bee_nest",
      "minecraft:border_block",
      "minecraft:barrier",
      "minecraft:balloon",
      "minecraft:bleach",
      "minecraft:client_request_placeholder_block",
      "minecraft:camera",
      "minecraft:chemical_heat",
      "minecraft:command_block",
      "minecraft:chemistry_table",
      "minecraft:daylight_detector_inverted",
      "minecraft:end_gateway",
      "minecraft:flowing_lava",
      "minecraft:fire",
      "minecraft:glow_stick",
      "minecraft:hard_glass",
      "minecraft:hard_glass_pane",
      "minecraft:hard_stained_glass",
      "minecraft:hard_stained_glass_pane",
      "minecraft:info_update",
      "minecraft:info_update2",
      "minecraft:invisibleBedrock",
      "minecraft:invisiblebedrock",
      "minecraft:ice_bomb",
      "minecraft:netherreactor",
      "minecraft:netherReactor",
      "minecratf:movingBlock",
      "minecraft:movingblock",
      "minecraft:melon_stem",
      "minecraft:monster_egg",
      "minecraft:medicine",
      "minecraft:lit_furnace",
      "minecraft:lit_blast_furnace",
      "minecraft:lit_smoker",
      "minecraft:lit_redstone_lamp",
      "minecraft:lit_redstone_ore",
      "minecraft:lava_cauldron",
      "minecraft:powered_comparator",
      "minecraft:powered_repeater",
      "minecraft:pumpkin_stem",
      "minecraft:potatoes",
      "minecraft:pistonArmCollision",
      "minecraft:pistomarmcollision",
      "minecraft:portal",
      "minecraft:reserved3",
      "minecraft:reserved4",
      "minecraft:reserved6",
      "minecraft:redstone_wire",
      "minecraft:rapid_fertilizer",
      "minecraft:stonecutter",
      "minecraft:stickyPistonArmCollision",
      "minecraft:stickypistonarmcollision",
      "minecraft:soul_fire",
      "minecraft:sparkler",
      "minecraft:spawn_egg",
      "minecraft:unknown",
      "minecraft:unpowered_repeater",
      "minecraft:unpowered_comparator",
      "minecraft:unlit_redstone_torch",
      "minecraft:underwater_torch",
      "minecraft:wall_sign",
      "minecraft:wall_banner",
      "minecraft:glowingobsidian"
    ]);
  
    return !unbreakables.has(block.typeId);
  }
  
  function breakBlockWithDropLeaf(block, dimension) {
    if (!block || !isBreakableLeaf(block)) return;
  
    const blockType = block.typeId;
    const location = block.location;
  
    try {
      block.setType("minecraft:air");
      dimension.spawnItem(new ItemStack(blockType, 1), location);
    } catch (err) {
      console.warn(`Failed to simulate breaking block ${blockType}:`, err);
    }
  }
  
  const leafTimers = new Map();
  
  system.runInterval(() => {
    const overworld = world.getDimension("overworld");
  
    for (const entity of overworld.getEntities({ type: "tc:creature" })) {
      const entityId = entity.id;
  
      const belowLoc = {
        x: Math.floor(entity.location.x),
        y: Math.floor(entity.location.y - 1),
        z: Math.floor(entity.location.z),
      };
      const blockBelow = overworld.getBlock(belowLoc);
  
      if (!blockBelow || !targetBlockIds.has(blockBelow.typeId)) {
        leafTimers.delete(entityId);
        continue;
      }
  
      let timer = leafTimers.get(entityId) ?? 0;
      timer += 1;
  
      if (timer >= TICKS_TO_BREAK) {
        for (let dx = -1; dx <= 1; dx++) {
          for (let dz = -1; dz <= 1; dz++) {
            const targetPos = {
              x: belowLoc.x + dx,
              y: belowLoc.y,
              z: belowLoc.z + dz
            };
            const targetBlock = overworld.getBlock(targetPos);
            if (targetBlock && targetBlockIds.has(targetBlock.typeId)) {
              breakBlockWithDropLeaf(targetBlock, overworld);
            }
          }
        }
        leafTimers.delete(entityId);
      } else {
        leafTimers.set(entityId, timer);
      }
    }
  
    const activeIds = new Set(
      overworld.getEntities({ type: "tc:creature" }).map(e => e.id)
    );
    for (const id of leafTimers.keys()) {
      if (!activeIds.has(id)) {
        leafTimers.delete(id);
      }
    }
  }, 10);

const nonBlockingBlocks = new Set([
  "minecraft:grass",
  "minecraft:tall_grass",
  "minecraft:snow_layer",
  "minecraft:snow",
  "minecraft:dandelion",
  "minecraft:poppy",
  "minecraft:torch",
  "minecraft:redstone_torch",
  "minecraft:flowering_azalea",
  "minecraft:bamboo_sapling"
]);

function isAirLike(block) {
  if (!block) return true;
  if (block.typeId === "minecraft:air") return true;
  return nonBlockingBlocks.has(block.typeId);
}

world.beforeEvents.worldInitialize.subscribe(() => {
  system.runInterval(() => {
    const dim = world.getDimension("overworld");
    const entities = dim.getEntities({ type: "tc:creature" });

    for (const entity of entities) {
      try {
        const pos = entity.location;
        const yBase = Math.floor(pos.y);

        const block1 = dim.getBlock({ x: Math.floor(pos.x), y: yBase + 1, z: Math.floor(pos.z) });
        const block2 = dim.getBlock({ x: Math.floor(pos.x), y: yBase + 2, z: Math.floor(pos.z) });
        const block3 = dim.getBlock({ x: Math.floor(pos.x), y: yBase + 3, z: Math.floor(pos.z) });

        const PoseisAir1 = isAirLike(block1);
        const PoseisAir2 = isAirLike(block2);
        const PoseisAir3 = isAirLike(block3);
        const isWater1 = block1 && block1.typeId === "minecraft:water";

        let entityHeight = 1;

        if (!PoseisAir1 && !isWater1) {
          entity.runCommandAsync("event entity @s crawl");
          entityHeight = 1;
        } else if (PoseisAir1 && PoseisAir2 && PoseisAir3) {
          entity.runCommandAsync("event entity @s 4_blocks_high");
          entityHeight = 4;
        } else if (PoseisAir1 && PoseisAir2 && !PoseisAir3) {
          entity.runCommandAsync("event entity @s 3_blocks_high");
          entityHeight = 3;
        } else if (PoseisAir1 && !PoseisAir2) {
          entity.runCommandAsync("event entity @s 2_blocks_high");
          entityHeight = 2;
        }

        const yawRad = ((entity.getRotation().y + 90) * Math.PI) / 180;
        const sideOffset = 0.8;
        const forwardOffsetDist = 1.3;

        const forward = { x: Math.cos(yawRad), z: Math.sin(yawRad) };
        const right = { x: Math.cos(yawRad + Math.PI / 2), z: Math.sin(yawRad + Math.PI / 2) };
        const left = { x: -right.x, z: -right.z };

        let leftBlocked = false;
        let rightBlocked = false;

        for (let i = 0; i < entityHeight; i++) {
          const leftBlock = dim.getBlock({
            x: Math.floor(pos.x + left.x * sideOffset),
            y: yBase + i,
            z: Math.floor(pos.z + left.z * sideOffset)
          });
          const rightBlock = dim.getBlock({
            x: Math.floor(pos.x + right.x * sideOffset),
            y: yBase + i,
            z: Math.floor(pos.z + right.z * sideOffset)
          });

          if (leftBlock && !isAirLike(leftBlock)) leftBlocked = true;
          if (rightBlock && !isAirLike(rightBlock)) rightBlocked = true;
        }

        if (leftBlocked && rightBlocked) {
          entity.runCommandAsync("event entity @s tc:both_right_left_full");
        } else if (leftBlocked) {
          entity.runCommandAsync("event entity @s tc:left_full");
        } else if (rightBlocked) {
          entity.runCommandAsync("event entity @s tc:right_full");
        } else {
          entity.runCommandAsync("event entity @s tc:big_space");
        }

        const isCrawling = entityHeight === 1;
        const frontYBase = yBase + (isCrawling ? 0 : 1);

        let blockedHeight = 0;

        for (let i = 0; i < 4; i++) {
          const block = dim.getBlock({
            x: Math.floor(pos.x + forward.x * forwardOffsetDist),
            y: frontYBase + i,
            z: Math.floor(pos.z + forward.z * forwardOffsetDist)
          });

          if (block && !isAirLike(block)) {
            blockedHeight = i + 1;
            break;
          }
        }

        if (blockedHeight === 0) {
          entity.runCommandAsync("event entity @s tc:front_clear");
        } else if (blockedHeight <= 2) {
          entity.runCommandAsync("event entity @s tc:front_block_high");
        } else {
          entity.runCommandAsync("event entity @s tc:front_block_low");
        }

      } catch (e) {
        console.warn("Detection failed for tc:creature:", e);
      }
    }
  }, 5);
});

function faceNearestPlayer(entity) {
  if (entity.getProperty("tc:sleeping") === true) return;
  if (entity.getProperty("tc:movement") !== 1) return;

  const viewRange = 60;
  const players = entity.dimension.getPlayers();

  let closestPlayer = null;
  let closestDistSq = Infinity;

  for (const player of players) {
    const dx = player.location.x - entity.location.x;
    const dy = (player.location.y + 1.6) - (entity.location.y + 1.6);
    const dz = player.location.z - entity.location.z;
    const distSq = dx * dx + dy * dy + dz * dz;

    if (distSq < closestDistSq && distSq < viewRange * viewRange) {
      closestDistSq = distSq;
      closestPlayer = player;
    }
  }

  if (!closestPlayer) return;

  const dx = closestPlayer.location.x - entity.location.x;
  const dz = closestPlayer.location.z - entity.location.z;
  const dy = (closestPlayer.location.y + 1.6) - (entity.location.y + 1.6);

  const yaw = -Math.atan2(dx, dz) * (180 / Math.PI);
  const pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)) * (180 / Math.PI);

  try {
    entity.setRotation({ x: pitch, y: yaw });
  } catch (err) {
    console.warn("Could not rotate entity:", err);
  }
}

system.runInterval(() => {
  for (const entity of world.getDimension("overworld").getEntities({ type: "tc:creature" })) {
    faceNearestPlayer(entity);
  }
}, 1);

const HUNTER_ENTITY_TYPE = "tc:creature";
const HUNTER_STAGE_EVENTS = [
  "tc:behavior_stage1",
  "tc:behavior_stage2",
  "tc:behavior_stage3",
  "tc:behavior_stage4",
  "tc:behavior_stage5",
];
const HUNTER_STAGE_INTERVAL_TICKS = 10;
const HUNTER_STAGE_DURATION_TICKS = 50000;

const overworld = world.getDimension("overworld");
const scoreboardstage = world.scoreboard;

let currentStageIndex = 0;
let elapsedTicks = 0;

function showStageBehaviorForm(player) {
  const stageOptions = ["Normal (Loop)", "Normal (No Loop)", "Forced Aggression"];
  const form = new ActionFormData()
    .title("Creature Stage Behavior")
    .body("Choose how The Hunter progresses over time:")
    .button(stageOptions[0])
    .button(stageOptions[1])
    .button(stageOptions[2])
    .button("Back to Settings");

  form.show(player).then(resp => {
    if (resp.canceled) return;
    if (resp.selection === 3) return showSettingsMenu(player);
    if (resp.selection >= 0 && resp.selection <= 2) showStageBehaviorExplanation(player, resp.selection);
  });
}

function showStageBehaviorExplanation(player, optionIndex) {
  const explanations = [
    "Normal Loop: The Hunter progresses through stages 1 → 5, then resets to 1.",
    "Normal No Loop: The Hunter progresses through stages 1 → 5, but stays at stage 5.",
    "Forced Aggression: The Hunter always starts and stays at stage 5."
  ];

  const form = new ActionFormData()
    .title("Stage Behavior Explanation")
    .body(explanations[optionIndex])
    .button("Apply This Option")
    .button("Back");

  form.show(player).then(resp => {
    if (resp.canceled) return;
    if (resp.selection === 0) {
      currentStageBehavior = optionIndex;
      const stageObj = scoreboardstage.getObjective("hunter_stage_behavior");
      stageObj.setScore("settings_stage_behavior", currentStageBehavior);
      player.sendMessage(`§aStage behavior set to: ${["Loop","No Loop","Forced"][optionIndex]}`);
      return showSettingsMenu(player);
    }
    showStageBehaviorForm(player);
  });
}

function showSettingsMenu(player) {
  const settingsMenu = new ActionFormData()
    .title("Settings")
    .body("The settings category lets you adjust different game mechanics!")
    .button("Spawn Rate")
    .button(blockBreakingEnabled ? "Disable Block Breaking" : "Enable Block Breaking")
    .button(birdsEnabled ? "Disable Birds" : "Enable Birds")
    .button("Stage Behavior");

  settingsMenu.show(player).then(response => {
    if (response.canceled) return;
    switch (response.selection) {
      case 0: showSpawnRateForm(player); break;
      case 1:
        blockBreakingEnabled = !blockBreakingEnabled;
        player.sendMessage(`§aBlock Breaking ${blockBreakingEnabled ? "Enabled" : "Disabled"}`);
        scoreboardstage.getObjective("block_breaking").setScore("settings_breaking", blockBreakingEnabled ? 1 : 0);
        break;
      case 2:
        birdsEnabled = !birdsEnabled;
        player.sendMessage(`§aBird System ${birdsEnabled ? "Enabled" : "Disabled"}`);
        scoreboardstage.getObjective("bird_system_enabled").setScore("settings_bird", birdsEnabled ? 1 : 0);
        break;
      case 3: showStageBehaviorForm(player); break;
    }
  });
}

function StageinitScoreboard() {
  if (!scoreboardstage.getObjective("tc_stage")) scoreboardstage.addObjective("tc_stage", "Current Stage Index");
  if (!scoreboardstage.getObjective("tc_ticks")) scoreboardstage.addObjective("tc_ticks", "Elapsed Ticks");
  if (!scoreboardstage.getObjective("hunter_stage_behavior")) scoreboardstage.addObjective("hunter_stage_behavior", "Stage Behavior Option");

  const participants = scoreboardstage.getParticipants();
  if (!participants.find(p => p.displayName === "global_stage")) scoreboardstage.getObjective("tc_stage").setScore("global_stage", 0);
  if (!participants.find(p => p.displayName === "global_ticks")) scoreboardstage.getObjective("tc_ticks").setScore("global_ticks", 0);
  if (!participants.find(p => p.displayName === "settings_stage_behavior")) scoreboardstage.getObjective("hunter_stage_behavior").setScore("settings_stage_behavior", currentStageBehavior);
}

function loadState() {
  const stageObj = scoreboardstage.getObjective("tc_stage");
  const tickObj = scoreboardstage.getObjective("tc_ticks");
  const behaviorObj = scoreboardstage.getObjective("hunter_stage_behavior");

  const stagePart = scoreboardstage.getParticipants().find(p => p.displayName === "global_stage");
  const tickPart = scoreboardstage.getParticipants().find(p => p.displayName === "global_ticks");
  const behaviorPart = scoreboardstage.getParticipants().find(p => p.displayName === "settings_stage_behavior");

  currentStageIndex = stagePart ? stageObj.getScore(stagePart) : 0;
  elapsedTicks = tickPart ? tickObj.getScore(tickPart) : 0;
  currentStageBehavior = behaviorPart ? behaviorObj.getScore(behaviorPart) : currentStageBehavior;
}

function saveState() {
  const stageObj = scoreboardstage.getObjective("tc_stage");
  const tickObj = scoreboardstage.getObjective("tc_ticks");
  const behaviorObj = scoreboardstage.getObjective("hunter_stage_behavior");

  stageObj.setScore("global_stage", currentStageIndex);
  tickObj.setScore("global_ticks", elapsedTicks);
  behaviorObj.setScore("settings_stage_behavior", currentStageBehavior);
}

function triggerCurrentStage() {
  const stageEvent = HUNTER_STAGE_EVENTS[currentStageIndex];
  const entities = overworld.getEntities({ type: HUNTER_ENTITY_TYPE });
  for (const entity of entities) {
    if (!entity.getProperty("tc:stageconf")) {
      entity.triggerEvent(stageEvent);
      entity.setProperty("tc:stageconf", true);
    }
  }
}

StageinitScoreboard();
loadState();
triggerCurrentStage();

system.runInterval(() => {
  elapsedTicks++;

  if (elapsedTicks % HUNTER_STAGE_INTERVAL_TICKS === 0) triggerCurrentStage();

  if (currentStageBehavior === 2) {
    currentStageIndex = HUNTER_STAGE_EVENTS.length - 1;
    elapsedTicks = 0;
  } else if (elapsedTicks >= HUNTER_STAGE_DURATION_TICKS) {
    if (currentStageBehavior === 1) {
      if (currentStageIndex < HUNTER_STAGE_EVENTS.length - 1) currentStageIndex++;
      elapsedTicks = 0;
    } else {
      currentStageIndex = (currentStageIndex + 1) % HUNTER_STAGE_EVENTS.length;
      elapsedTicks = 0;
    }
  }

  saveState();
}, 1);

const validFootstepBlocks = [
  "minecraft:podzol",
  "minecraft:dirt",
  "minecraft:grass_block",
  "minecraft:coarse_dirt",
  "minecraft:snow_layer",
  "minecraft:short_grass"
];

const intervalMs = 2500;

function spawnFootsteps() {
  const overworld = world.getDimension("overworld");

  for (const creature of overworld.getEntities({ type: "tc:creature" })) {
    const pos = creature.location;
    const x = Math.floor(pos.x);
    const feetY = Math.floor(pos.y);
    const z = Math.floor(pos.z);

    const blockAtFeet = overworld.getBlock({ x, y: feetY, z });
    const blockBelow = overworld.getBlock({ x, y: feetY - 1, z });

    let placeTarget = null;
    let footprintType = null;

    if (blockAtFeet && (blockAtFeet.typeId === "minecraft:snow_layer" || blockAtFeet.typeId === "minecraft:snow")) {
      placeTarget = blockAtFeet;
      footprintType = "tc:snowy_footstep";
    } else if (blockBelow && validFootstepBlocks.includes(blockBelow.typeId)) {
      if (blockAtFeet && blockAtFeet.isAir) {
        placeTarget = blockAtFeet;
        footprintType = "tc:footstep";
      }
    }

    if (placeTarget && footprintType) {
      try {
        placeTarget.setType(BlockTypes.get(footprintType));
        console.warn(
          `[DEBUG] Placed ${footprintType} at ${placeTarget.location.x}, ${placeTarget.location.y}, ${placeTarget.location.z}`
        );
      } catch (err) {
        console.warn("TC: Failed to place footprint:", err);
      }
    }
  }

  system.runTimeout(spawnFootsteps, intervalMs);
}

system.runTimeout(spawnFootsteps, intervalMs);

function monitorTrapsAndSpawnCreatures() {
  system.runInterval(() => {
    const dimension = world.getDimension("overworld");

    const traps1 = dimension.getEntities({ type: "tc:trap" });
    const traps2 = dimension.getEntities({ type: "tc:trap2" });

    const allTraps = [...traps1, ...traps2];

    for (const trap of allTraps) {
      const isAlert = trap.getProperty("tc:alert");

      if (isAlert === true) {
        console.warn(`[DEBUG] Trap triggered at ${trap.location.x}, ${trap.location.y}, ${trap.location.z}`);
        trySpawnNearTrap(trap);
        trap.setProperty("tc:alert", false);
      }
    }
  }, 10);
}

monitorTrapsAndSpawnCreatures();

function trySpawnNearTrap(trap) {
  const dimension = trap.dimension;
  if (countSpawnedEntities(dimension) >= HUNTER_MAX_ENTITIES) return;

  const allowedBlocks = new Set([
    "minecraft:grass_block",
    "minecraft:dirt",
    "minecraft:coarse_dirt",
    "minecraft:podzol",
    "minecraft:snow",
    "minecraft:snow_layer"
  ]);

  const maxAttempts = 20;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const minDistance = 18;
    const maxDistance = 28;
    const angle = Math.random() * Math.PI * 2;
    const distance = minDistance + Math.random() * (maxDistance - minDistance);
    const x = Math.floor(trap.location.x + Math.cos(angle) * distance);
    const z = Math.floor(trap.location.z + Math.sin(angle) * distance);

    let y = 320;
    let solidY = null;

    while (y > 0) {
      const block = dimension.getBlock({ x, y, z });
      if (block && !block.isAir) {
        solidY = y + 1;
        break;
      }
      y--;
    }

    if (solidY === null) continue;

    const groundBlock = dimension.getBlock({ x, y: solidY - 1, z });
    if (!groundBlock || !allowedBlocks.has(groundBlock.typeId)) continue;

    const spawnLocation = { x, y: solidY, z };
    const entity = dimension.spawnEntity(HUNTER_SPAWN_ENTITY_TYPE, spawnLocation);

    if (entity) {
      entity.addTag(HUNTER_SPAWN_TAG);
    }

    return;
  }
}

function removeDuplicateBirdEntities(player) {
  const playerId = player.id;
  const overworld = world.getDimension("overworld");

  const birds = overworld.getEntities({ type: "tc:bird_controller" });

  const matchingBirds = birds.filter(bird => {
    return bird.getProperty("ownerId") === playerId;
  });

  if (matchingBirds.length > 1) {
    for (let i = 1; i < matchingBirds.length; i++) {
      const extraBird = matchingBirds[i];
      if (extraBird?.isValid()) {
        extraBird.remove();
      }
    }
  }
}

system.runInterval(() => {
  for (const player of world.getPlayers()) {
    if (!playerEntityMap.has(player.id) && player.location) {
      spawnEntityForPlayer(player);
    }
  }
}, 10);

system.runInterval(() => {
  for (const player of world.getPlayers()) {
    removeDuplicateBirdEntities(player);
  }
}, 10);

system.runInterval(() => {
  const overworld = world.getDimension("overworld");
  const birds = overworld.getEntities({ type: "tc:bird_controller" });

  for (const bird of birds) {
    if (!entityOwnerMap.has(bird)) {
      bird.remove();
    }
  }
}, 100);

const trappedPlayers = new Map();

system.runInterval(() => {
  const currentPlayers = world.getPlayers();

  for (const [id, data] of trappedPlayers) {
    if (!currentPlayers.some(p => p.id === id)) {
      if (data.trapEntity?.isValid()) {
        data.trapEntity.triggerEvent("tc:vanish");
      }
      trappedPlayers.delete(id);
    }
  }

  for (const player of currentPlayers) {
    if (!player.hasTag("trappedorvat1")) continue;

    let data = trappedPlayers.get(player.id);

    if (!data) {
      const playerBlock = {
        x: Math.floor(player.location.x),
        y: Math.floor(player.location.y),
        z: Math.floor(player.location.z)
      };

      const trapEntity = player.dimension.getEntities({
        location: player.location,
        type: "tc:trap",
        maxDistance: 2
      })[0];

      if (!trapEntity) {
        player.removeTag("trappedorvat1");
        continue;
      }

      data = {
        trapLocation: playerBlock,
        trapEntity: trapEntity,
        jumpCount: 0,
        wasJumpingLastTick: false,
        timeTrappedTicks: 0,
        lastDamageTick: 0
      };

      trappedPlayers.set(player.id, data);

      player.runCommand("effect @s slowness 255 30 true");
      continue;
    }

    data.timeTrappedTicks++;
    const { trapLocation, trapEntity, timeTrappedTicks, lastDamageTick } = data;

    if (!trapEntity.isValid()) {
      player.removeTag("trappedorvat1");
      trappedPlayers.delete(player.id);
      player.runCommand("effect @s clear");
      continue;
    }

    const dx = player.location.x - trapLocation.x;
    const dy = player.location.y - trapLocation.y;
    const dz = player.location.z - trapLocation.z;
    const distSq = dx * dx + dy * dy + dz * dz;
    if (distSq > 25) {
      player.removeTag("trappedorvat1");
      trappedPlayers.delete(player.id);
      player.runCommand("effect @s clear");
      trapEntity.triggerEvent("tc:vanish");
      continue;
    }

    if (timeTrappedTicks >= 300) {
      player.removeTag("trappedorvat1");
      trappedPlayers.delete(player.id);
      player.runCommand("effect @s clear");
      trapEntity.triggerEvent("tc:vanish");
      continue;
    }

    if (timeTrappedTicks - lastDamageTick >= 10) {
      try {
        player.runCommand("damage @s 2");
      } catch (e) {
        console.warn(`Failed to damage ${player.name}:`, e);
      }
      data.lastDamageTick = timeTrappedTicks;
    }

    if (distSq > 0.5) {
      player.teleport(
        {
          x: trapLocation.x + 0.5,
          y: trapLocation.y,
          z: trapLocation.z + 0.5
        },
        player.dimension
      );
    }

    // Use isJumping
    if (player.isJumping && !data.wasJumpingLastTick) {
      data.jumpCount++;
      if (data.jumpCount >= 12) {
        player.removeTag("trappedorvat1");
        trappedPlayers.delete(player.id);
        player.runCommand("effect @s clear");
        player.runCommand("playsound random.break @s");
        trapEntity.triggerEvent("tc:vanish");
      }
    }
    data.wasJumpingLastTick = player.isJumping;

    if (timeTrappedTicks % 100 === 0) {
      player.runCommand("effect @s slowness 3 30 true");
    }
  }
}, 1);

world.afterEvents.playerLeave.subscribe(event => {
  const data = trappedPlayers.get(event.playerId);
  if (data) {
    if (data.trapEntity?.isValid()) {
      data.trapEntity.triggerEvent("tc:vanish");
    }
    trappedPlayers.delete(event.playerId);
  }
});
const glassCooldownPlayers = new Set();

function isNearGlass(player, radius = 2) {
  const dim = player.dimension;
  const { x: px, y: py, z: pz } = player.location;

  for (let x = -radius; x <= radius; x++) {
    for (let y = -radius; y <= radius; y++) {
      for (let z = -radius; z <= radius; z++) {
        const block = dim.getBlock({
          x: Math.floor(px + x),
          y: Math.floor(py + y),
          z: Math.floor(pz + z)
        });

        if (
          block &&
          block.typeId &&
          (
            block.typeId === "minecraft:glass" ||
            block.typeId === "minecraft:glass_pane"
          )
        ) {
          return true;
        }
      }
    }
  }

  return false;
}

const HUNTER_PROJECTILE_TYPE = "tc:rock_projectile";
const HUNTER_PROJECTILE_TAG = "hunter_rock";

const HUNTER_MIN_THROW_RANGE = 15;
const HUNTER_MAX_THROW_RANGE = 60;
const HUNTER_THROW_COOLDOWN_TICKS = 24 * 20;

const HUNTER_KB_HORIZONTAL = 3.0;
const HUNTER_KB_VERTICAL = 0.6;

const HUNTER_THROW_COOLDOWNS = new Map();

function length3(v) { return Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z); }
function normalize3(v) {
  const len = length3(v);
  return len < 1e-6 ? {x:0,y:0,z:0} : {x:v.x/len, y:v.y/len, z:v.z/len};
}
function cross(a,b){return {x:a.y*b.z-a.z*b.y, y:a.z*b.x-a.x*b.z, z:a.x*b.y-a.y*b.x};}

const throwReadyHunters = new Set();

system.runInterval(() => {
  try {
    currentTick++;

    const overworld = world.getDimension(MinecraftDimensionTypes.overworld);
    const players = world.getPlayers().filter(p => p.dimension === overworld);
    if (!players.length) return;

    const hunters = overworld.getEntities({ type: HUNTER_ENTITY_TYPE })
      .filter(h => Boolean(h.getProperty?.("tc:chasing")));

    if (!hunters.length) return;

    for (const hunter of hunters) {
      const last = HUNTER_THROW_COOLDOWNS.get(hunter.id) ?? 0;
      if (currentTick - last < HUNTER_THROW_COOLDOWN_TICKS) continue;

      let target = null;
      let minDist = Infinity;
      for (const p of players) {
        const dx = p.location.x - hunter.location.x;
        const dy = p.location.y - hunter.location.y;
        const dz = p.location.z - hunter.location.z;
        const d = Math.sqrt(dx*dx + dy*dy + dz*dz);
        if (d >= HUNTER_MIN_THROW_RANGE && d <= HUNTER_MAX_THROW_RANGE && d < minDist) {
          minDist = d;
          target = p;
        }
      }
      if (!target) continue;

      throwReadyHunters.add(hunter.id);

      HUNTER_THROW_COOLDOWNS.set(hunter.id, currentTick);
      hunter.setProperty("tc:throw_rock", true);
      hunter.runCommandAsync("effect @s slowness 5 255 true");
      hunter.triggerEvent?.("tc:throw_rock_prep");

      system.runTimeout(() => {
        if (!hunter.isValid() || !target.isValid()) return;
        throwRockAt(hunter, target);

        system.runTimeout(() => {
          if (!hunter.isValid()) return;
          hunter.setProperty("tc:throw_rock", false);
          hunter.runCommandAsync("effect @s clear");
          hunter.triggerEvent?.("tc:throw_rock");

          throwReadyHunters.delete(hunter.id);
        }, 15);
      }, 50);
    }

  } catch(e){
    console.warn("[HunterThrow] Interval Error:", e);
  }
}, 20);

function throwRockAt(hunter,target){
  const forward=hunter.getViewDirection();
  const up={x:0,y:1,z:0};
  let left=normalize3(cross(up,forward));

  const spawnPos = {
    x: hunter.location.x + forward.x*2 + left.x*1,
    y: hunter.location.y + 1.5,
    z: hunter.location.z + forward.z*2 + left.z*1
  };

  const targetPos = {
    x: target.location.x,
    y: target.location.y + 1.3,
    z: target.location.z
  };

  const dx = targetPos.x - spawnPos.x;
  const dy = targetPos.y - spawnPos.y;
  const dz = targetPos.z - spawnPos.z;

  const distXZ = Math.sqrt(dx*dx + dz*dz);
  const t = Math.max(10, Math.min(40, distXZ*0.5));
  const seconds = t/20;

  const g = 0.05*20;

  const vx = dx/seconds;
  const vz = dz/seconds;
  const vy = (dy/seconds) + 0.5*g*seconds;

  const proj = hunter.dimension.spawnEntity(HUNTER_PROJECTILE_TYPE, spawnPos);
  proj.addTag(HUNTER_PROJECTILE_TAG);
  proj.applyImpulse({x:vx, y:vy, z:vz});

  const intervalId = system.runInterval(()=>{
    if(!proj.isValid()){ system.clearRun(intervalId); return; }
    const block = proj.dimension.getBlock(proj.location);
    if(block && !block.isAir){
      try{ proj.kill?.(); }catch{}
      system.clearRun(intervalId);
      playHitSound(proj.dimension);
    }
  },1);

  system.runTimeout(()=>{
    if(proj.isValid()) proj.kill?.();
    system.clearRun(intervalId);
  }, 200);
}

world.afterEvents.entityHitEntity.subscribe(ev=>{
  try{
    const {damagingEntity, hitEntity} = ev;
    if(!damagingEntity || !hitEntity) return;
    if(!damagingEntity.hasTag?.(HUNTER_PROJECTILE_TAG)) return;
    if(hitEntity.typeId !== "minecraft:player") return;

    const dx = hitEntity.location.x - damagingEntity.location.x;
    const dz = hitEntity.location.z - damagingEntity.location.z;
    const len = Math.hypot(dx,dz)||1;
    hitEntity.applyKnockback(dx/len, dz/len, HUNTER_KB_HORIZONTAL, HUNTER_KB_VERTICAL);

    playHitSound(hitEntity.dimension);

  }catch(e){ console.warn("[HunterThrow] Hit Handler Error:", e);}
});

function playHitSound(dimension){
  const players = world.getPlayers().filter(p=>p.dimension===dimension);
  for(const p of players){
    p.runCommandAsync("playsound mob.creature.rock_hit @s");
  }
}

function checkForGlassScratchingAmbient() {
  const players = world.getPlayers();
  if (players.length === 0) {
    system.runTimeout(checkForGlassScratchingAmbient, 1000);
    return;
  }

  for (const player of players) {
    if (!player || !player.isValid() || glassCooldownPlayers.has(player.id)) continue;

    if (isNearGlass(player)) {
      const chance = Math.random();

      if (chance < 0.5) {
        player.runCommandAsync("playsound mob.creature.window @s");

        glassCooldownPlayers.add(player.id);

        system.runTimeout(() => {
          glassCooldownPlayers.delete(player.id);
        }, 9200);
      }
    }
  }

  system.runTimeout(checkForGlassScratchingAmbient, 600);
}
system.run(() => {
  checkForGlassScratchingAmbient();
});

function monitorCreaturesAndScratchLogs() {
  const overworld = world.getDimension("overworld");
  const creatures = overworld.getEntities({ type: "tc:creature" });

  for (const creature of creatures) {
    tryScratchLogsAroundCreature(creature);
  }

  system.runTimeout(monitorCreaturesAndScratchLogs, 400);
}

system.run(() => {
  monitorCreaturesAndScratchLogs();
});

const logTypes = new Set([
  "minecraft:oak_log",
  "minecraft:spruce_log",
  "minecraft:birch_log",
  "minecraft:jungle_log",
  "minecraft:acacia_log",
  "minecraft:dark_oak_log",
  "minecraft:mangrove_log",
  "minecraft:cherry_log",
  "tc:redwood_log"
]);

const faceOffsets = [
  { x: 1, y: 0, z: 0 },
  { x: -1, y: 0, z: 0 },
  { x: 0, y: 1, z: 0 },
  { x: 0, y: -1, z: 0 },
  { x: 0, y: 0, z: 1 },
  { x: 0, y: 0, z: -1 }
];

const scratchCooldown = new Set();

function getBlockFaceFromOffset(offset) {
  if (offset.x === 1) return "west";
  if (offset.x === -1) return "east";
  if (offset.y === 1) return "down";
  if (offset.y === -1) return "up";
  if (offset.z === 1) return "north";
  if (offset.z === -1) return "south";
  return null;
}

const stageScratchChances = [0.0, 0.3, 0.5, 0.7, 0.9]; 

function tryScratchLogsAroundCreature(entity) {
  if (!entity || !entity.isValid()) return;
  if (scratchCooldown.has(entity.id)) return;

  const chance = stageScratchChances[currentStageIndex] ?? 0;
  if (Math.random() > chance) return;

  const dim = entity.dimension;
  const { x: ex, y: ey, z: ez } = entity.location;

  for (let dx = -1; dx <= 3; dx++) {
    for (let dy = 0; dy <= 3; dy++) {
      for (let dz = -1; dz <= 1; dz++) {
        const bx = Math.floor(ex + dx);
        const by = Math.floor(ey + dy);
        const bz = Math.floor(ez + dz);

        const block = dim.getBlock({ x: bx, y: by, z: bz });
        if (!block || !logTypes.has(block.typeId)) continue;

        for (const offset of faceOffsets) {
          const faceX = bx + offset.x;
          const faceY = by + offset.y;
          const faceZ = bz + offset.z;

          const neighbor = dim.getBlock({ x: faceX, y: faceY, z: faceZ });
          if (neighbor && neighbor.typeId === "minecraft:air") {
            neighbor.setType("tc:log_scratch");

            dim.runCommand(
              `execute at @e[type=tc:creature] run playsound mob.creature.log_scratch @a[r=40]`
            );

            const blockFace = getBlockFaceFromOffset(offset);
            if (blockFace) {
              const perm = neighbor.permutation.withState("minecraft:block_face", blockFace);
              neighbor.setPermutation(perm);
            }

            scratchCooldown.add(entity.id);
            system.runTimeout(() => {
              scratchCooldown.delete(entity.id);
            }, 1500);

            return;
          }
        }
      }
    }
  }
}

const HUNTER_IGNORE_DISTANCE = 35;
const HUNTER_SOUND_RADIUS = 50;
const HUNTER_ENTITY_ID = "tc:creature";

const hunterIgnoreTimers = new WeakMap();
const hunterTeleportTimers = new WeakMap();
const hunterFakeStepTimers = new WeakMap();

const TELEPORT_COOLDOWN_TICKS = 90 * 20;
const HUNTER_FAKESTEP_INTERVAL = 20 * 60;
const WATCH_WINDOW_TICKS = 30 * 20;

function hunterChanceForStage(stage) {
  switch (stage) {
    case 1: return 0.0;
    case 2: return 0.1;
    case 3: return 0.2;
    case 4: return 0.4;
    case 5: return 0.5;
    default: return 0;
  }
}

const hunterIgnoreEventMap = {
  1: "tc:move_closer_stage1_step1",
  2: "tc:move_closer_stage2_step1",
  3: "tc:move_closer_stage3_step1",
  4: "tc:move_closer_stage4_step1",
  5: "tc:move_closer_stage5_step1",
};

function hunterGetStage(entity) {
  if (entity.getProperty("tc:stage5")) return 5;
  if (entity.getProperty("tc:stage4")) return 4;
  if (entity.getProperty("tc:stage3")) return 3;
  if (entity.getProperty("tc:stage2")) return 2;
  if (entity.getProperty("tc:stage1")) return 1;
  return 1;
}

function hunterDistanceBetween(a, b) {
  const dx = a.x - b.x;
  const dy = a.y - b.y;
  const dz = a.z - b.z;
  return Math.sqrt(dx * dx + dy * dy + dz * dz);
}

function hunterFindNearestPlayer(entity, overworld) {
  const players = overworld.getPlayers();
  let closest = null;
  let minDist = Infinity;
  for (const p of players) {
    const d = hunterDistanceBetween(entity.location, p.location);
    if (d < minDist) {
      minDist = d;
      closest = p;
    }
  }
  return closest;
}

function isPlayerLookingAtCreatureFOV(player, creature) {
  const eyeLoc = player.getHeadLocation();
  const viewDir = player.getViewDirection();

  const dx = creature.location.x - eyeLoc.x;
  const dy = (creature.location.y + 1.0) - eyeLoc.y;
  const dz = creature.location.z - eyeLoc.z;

  const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
  if (dist > 80) return false;

  const tx = dx / dist;
  const ty = dy / dist;
  const tz = dz / dist;

  const dot = tx * viewDir.x + ty * viewDir.y + tz * viewDir.z;
  const FOV_THRESHOLD = 0.15;

  return dot >= FOV_THRESHOLD;
}

function teleportCreatureNearPlayer(creature, player) {
  const dimension = creature.dimension;

  const tpAllowedBlocks = new Set([
    "minecraft:grass_block",
    "minecraft:dirt",
    "minecraft:coarse_dirt",
    "minecraft:podzol",
    "minecraft:snow",
    "minecraft:bush",
    "minecraft:tall_grass"
  ]);

  const tpIgnoredBlocks = new Set([
    "minecraft:oak_leaves",
    "minecraft:spruce_leaves",
    "minecraft:birch_leaves",
    "minecraft:jungle_leaves",
    "minecraft:acacia_leaves",
    "minecraft:dark_oak_leaves",
    "minecraft:mangrove_leaves",
    "minecraft:azalea_leaves",
    "minecraft:flowering_azalea_leaves",
    "minecraft:snow_layer",
    "minecraft:cherry_leaves",
    "tc:redwood_leaves"
  ]);

  const maxAttempts = 80;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const minDistance = 14;
    const maxDistance = 18;
    const angle = Math.random() * Math.PI * 2;
    const distance = minDistance + Math.random() * (maxDistance - minDistance);

    const x = Math.floor(player.location.x + Math.cos(angle) * distance);
    const z = Math.floor(player.location.z + Math.sin(angle) * distance);

    let y = 320;
    let solidY = null;

    while (y > 0) {
      const block = dimension.getBlock({ x, y, z });
      if (block && !block.isAir) {
        if (tpIgnoredBlocks.has(block.typeId)) {
          y--;
          continue;
        }
        solidY = y + 1;
        break;
      }
      y--;
    }

    if (solidY === null) continue;

    const groundBlock = dimension.getBlock({ x, y: solidY - 1, z });
    if (!groundBlock || !tpAllowedBlocks.has(groundBlock.typeId)) continue;

    creature.teleport({ x, y: solidY, z }, { dimension });
    return true;
  }

  return false;
}

function performTeleport(hunter, nearest) {
  hunter.triggerEvent("tc:become_invisible");
  let attempts = 0;
  const maxTeleportAttempts = 10;

  const tryTeleport = () => {
    attempts++;
    const visible = isPlayerLookingAtCreatureFOV(nearest, hunter);

    if (!visible && teleportCreatureNearPlayer(hunter, nearest)) {
      system.runTimeout(() => {
        const stillVisible = isPlayerLookingAtCreatureFOV(nearest, hunter);
        if (!stillVisible) {
          console.warn(`[HunterTeleport] Teleport successful.`);
          hunter.triggerEvent("tc:turn_off_invisible");
          hunterTeleportTimers.set(hunter, system.currentTick);
        } else {
          if (attempts < maxTeleportAttempts) {
            system.runTimeout(tryTeleport, 10);
          } else {
            console.warn(`[HunterTeleport] Max attempts reached (caught looking). Reappearing.`);
            hunter.triggerEvent("tc:turn_off_invisible");
            hunterTeleportTimers.set(hunter, system.currentTick);
          }
        }
      }, 5);
      return;
    }

    if (attempts < maxTeleportAttempts) {
      system.runTimeout(tryTeleport, 10);
    } else {
      console.warn(`[HunterTeleport] Max attempts reached, teleport failed. Reappearing.`);
      hunter.triggerEvent("tc:turn_off_invisible");
      hunterTeleportTimers.set(hunter, system.currentTick);
    }
  };

  tryTeleport();
}

function runFakeStep(hunter, nearest, stage, chain = false) {
  const dx = nearest.location.x - hunter.location.x;
  const dy = (nearest.location.y + 1) - hunter.location.y;
  const dz = nearest.location.z - hunter.location.z;
  const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);

  if (dist > 0) {
    const stepStrength = 0.25;
    hunter.applyImpulse({
      x: (dx / dist) * stepStrength,
      y: 0.05,
      z: (dz / dist) * stepStrength
    });

    console.warn(`[HunterFakeStep] Stage ${stage}, fake step triggered${chain ? " (chain)" : ""}.`);

    if (Math.random() < 0.5) {
      system.runTimeout(() => {
        runFakeStep(hunter, nearest, stage, true);
      }, 40);
    }
  }
}

system.runInterval(() => {
  const overworld = world.getDimension("overworld");
  const hunters = overworld.getEntities({ type: HUNTER_ENTITY_ID });

  for (const hunter of hunters) {
    const nearest = hunterFindNearestPlayer(hunter, overworld);
    if (!nearest) continue;

    const stage = hunterGetStage(hunter);

    if (
      hunterDistanceBetween(hunter.location, nearest.location) >= HUNTER_IGNORE_DISTANCE &&
      hunter.getProperty("tc:no_range") === false &&
      hunter.getProperty("tc:movement") === 1
  ) {
      const lastIgnore = hunterIgnoreTimers.get(hunter);
  
      if (lastIgnore === undefined) {
          hunterIgnoreTimers.set(hunter, system.currentTick);
  
          if (Math.random() < hunterChanceForStage(stage)) {
              hunter.triggerEvent(hunterIgnoreEventMap[stage]);
              console.warn(`[HunterIgnore] Stage ${stage}, ignore triggered!`);
              for (const player of overworld.getPlayers()) {
                  if (hunterDistanceBetween(hunter.location, player.location) <= HUNTER_SOUND_RADIUS) {
                      try { player.runCommand(`playsound mob.creature.spawn @s`); } catch {}
                  }
              }
          } else {
              console.warn(`[HunterIgnore] Stage ${stage}, roll failed → cannot roll again until respawn.`);
          }
      }
  }

  if (hunter.getProperty("tc:movement") !== 1) continue;

  if (!hunterTeleportTimers.has(hunter)) {
    hunterTeleportTimers.set(hunter, system.currentTick - TELEPORT_COOLDOWN_TICKS);
  }
  
  const lastTeleport = hunterTeleportTimers.get(hunter);
  if (system.currentTick - lastTeleport >= TELEPORT_COOLDOWN_TICKS) {
    const chance = hunterChanceForStage(stage);
  
    if (Math.random() >= chance) {
      console.warn(`[HunterTeleport] Stage ${stage}, teleport roll failed. Cooldown resets.`);
      hunterTeleportTimers.set(hunter, system.currentTick);
    } else {
      const visible = isPlayerLookingAtCreatureFOV(nearest, hunter);
  
      if (!visible) {
        performTeleport(hunter, nearest);
      } else {
        console.warn(`[HunterTeleport] Player is looking, starting watch window...`);
        const startTick = system.currentTick;
  
        const watchLoop = () => {
          const elapsed = system.currentTick - startTick;
  
          if (elapsed > WATCH_WINDOW_TICKS) {
            console.warn(`[HunterTeleport] Watch window expired, doing nothing.`);
            hunterTeleportTimers.set(hunter, system.currentTick);
            return;
          }
  
          const stillLooking = isPlayerLookingAtCreatureFOV(nearest, hunter);
          if (!stillLooking) {
            console.warn(`[HunterTeleport] Player looked away → teleporting.`);
            performTeleport(hunter, nearest);
            return;
          }
  
          system.runTimeout(watchLoop, 20);
        };
  
        watchLoop();
      }
    }
  }

    if (!hunterFakeStepTimers.has(hunter)) {
      hunterFakeStepTimers.set(hunter, system.currentTick - HUNTER_FAKESTEP_INTERVAL);
    }

    const lastFakeStep = hunterFakeStepTimers.get(hunter);
    if (system.currentTick - lastFakeStep >= HUNTER_FAKESTEP_INTERVAL) {
      hunterFakeStepTimers.set(hunter, system.currentTick);

      if (Math.random() < hunterChanceForStage(stage)) {
        runFakeStep(hunter, nearest, stage);
      } else {
        console.warn(`[HunterFakeStep] Stage ${stage}, roll failed`);
      }
    }
  }
}, 20);

const PANIC_FRAMES = ["panic_frame1","panic_frame2","panic_frame3","panic_frame4","panic_frame5"];
const LOOP_FRAMES = ["panic_frame4","panic_frame5"];
const PANIC_FAINT_FRAMES = ["panic_faint_1","panic_faint_2","panic_faint_3","panic_faint_4","panic_faint_5"];

const TICK_INTERVAL = 3;
const LOOP_INTERVAL = 7;
const SOUND_INTERVAL = 40;
const PANIC_DISTANCE = 15;
const PROXIMITY_RANGE = 7;
const MAX_CLOSE_SCORE = 6;
const FAINT_FRAME_UP_DURATION = 7;
const FAINT_HOLD_DURATION = 80;
const FAINT_FRAME_DOWN_DURATION = 7;
const PANIC_FAINT_TELEPORT_MIN = 10;
const PANIC_FAINT_TELEPORT_MAX = 16;
const PANIC_FAINT_SLOWNESS_LEVEL = 6;

const creatureTriggers = new Map();
const runningPlayers = new Map();
const faintPlayers = new Map();
const triggerEnabled = new Map();
const playerScores = new Map();
const proximityCheckIntervals = new Map();
const CREATURE_TYPE = "tc:creature";


const HUNT_PROXIMITY_RANGE = 10;
const HUNT_TRIGGER_COOLDOWN = 100;
const huntProximityIntervals = new Map();
const huntLastTrigger = new Map();

function startHuntProximityCheck(creature){
  if(!creature || !creature.isValid() || huntProximityIntervals.has(creature.id)) return;

  const interval = system.runInterval(()=>{
    if(!creature || !creature.isValid()){
      system.clearRun(interval);
      huntProximityIntervals.delete(creature.id);
      huntLastTrigger.delete(creature.id);
      return;
    }

    let hunting = false;
    try { hunting = !!creature.getProperty("tc:hunting"); } catch(e) { hunting = false; }

    if(!hunting){
      system.clearRun(interval);
      huntProximityIntervals.delete(creature.id);
      huntLastTrigger.delete(creature.id);
      console.warn(`[HuntProx] Stopped monitoring creature ${creature.id} (tc:hunting false).`);
      return;
    }

    const players = creature.dimension.getPlayers();
    for(const p of players){
      if(!p || !p.isValid()) continue;
      const dx = p.location.x - creature.location.x;
      const dy = p.location.y - creature.location.y;
      const dz = p.location.z - creature.location.z;
      const distSq = dx*dx + dy*dy + dz*dz;

      if(distSq <= (HUNT_PROXIMITY_RANGE * HUNT_PROXIMITY_RANGE)){
        const last = huntLastTrigger.get(creature.id) ?? -999999;
        if(system.currentTick - last >= HUNT_TRIGGER_COOLDOWN){
          try {
            creature.triggerEvent("tc:behavior_hunt_player_close");
            console.warn(`[HuntProx] Triggered tc:behavior_hunt_player_close for creature ${creature.id} (player ${p.name})`);
          } catch(e) {
            console.warn("[HuntProx] Failed to trigger hunt_player_close:", e);
          }
          huntLastTrigger.set(creature.id, system.currentTick);
        }
        break;
      }
    }
  }, 10);

  huntProximityIntervals.set(creature.id, interval);
}

function sendFrame(player, frame) {
    try { player.runCommand(`titleraw @s actionbar {"rawtext":[{"text":"${frame}"}]}`); } catch(e){}
}

function getNearestCreature(player){
    const creatures = world.getDimension("overworld").getEntities({type:CREATURE_TYPE});
    if(creatures.length===0) return null;
    let nearest = creatures[0], minDist = Infinity;
    for(const c of creatures){
        const dx = player.location.x - c.location.x;
        const dy = player.location.y - c.location.y;
        const dz = player.location.z - c.location.z;
        const dist = Math.sqrt(dx*dx + dy*dy + dz*dz);
        if(dist < minDist){ minDist=dist; nearest=c; }
    }
    return nearest;
}

function startPanic(player){
    if(!player||!player.isValid()||runningPlayers.has(player.id)) return;

    let stage = "intro";
    let index = 0;
    let loopInterval = null;
    let soundInterval = null;

    const interval = system.runInterval(()=>{
        if(!player||!player.isValid()||faintPlayers.has(player.id)) { startFade(player); return; }

        const nearestCreature = getNearestCreature(player);
        if(!nearestCreature) { startFade(player); return; }

        const dx = player.location.x - nearestCreature.location.x;
        const dy = player.location.y - nearestCreature.location.y;
        const dz = player.location.z - nearestCreature.location.z;
        const dist = Math.sqrt(dx*dx + dy*dy + dz*dz);
        if(dist > PANIC_DISTANCE){ startFade(player); return; }

        if(stage === "intro"){
            sendFrame(player, PANIC_FRAMES[index]);
            index++;
            if(index >= PANIC_FRAMES.length){
                stage = "loop"; index = 0;

                loopInterval = system.runInterval(()=>{
                    if(!player||!player.isValid()||faintPlayers.has(player.id)) return;
                    sendFrame(player, LOOP_FRAMES[index % LOOP_FRAMES.length]);
                    index++;
                }, LOOP_INTERVAL);

                soundInterval = system.runInterval(()=>{
                    if(!player||!player.isValid()||faintPlayers.has(player.id)) return;
                    try { player.runCommand(`playsound mob.creature.panic @s`); } catch(e){}
                }, SOUND_INTERVAL);
            }
        }

        runningPlayers.set(player.id,{interval,loopInterval,soundInterval,stage,setStage:s=>{stage=s==="fade"?PANIC_FRAMES.length-1:0;}});
    }, TICK_INTERVAL);

    runningPlayers.set(player.id,{interval,loopInterval,soundInterval,stage,setStage:s=>{stage=s==="fade"?PANIC_FRAMES.length-1:0;}});
}

function startFade(player){
  const data = runningPlayers.get(player.id);
  if(!data || data.stage==="fade") return;

  data.setStage("fade");
  if(data.loopInterval) { system.clearRun(data.loopInterval); data.loopInterval=null; }
  if(data.soundInterval) { system.clearRun(data.soundInterval); data.soundInterval=null; }
  if(data.interval) { system.clearRun(data.interval); data.interval=null; }

  let index = PANIC_FRAMES.length-1;
  const fadeInterval = system.runInterval(()=>{
      if(!player||!player.isValid()){ 
          runningPlayers.delete(player.id); 
          system.clearRun(fadeInterval); 
          return; 
      }
      sendFrame(player, PANIC_FRAMES[index]);
      index--;
      if(index < 0){ 
          runningPlayers.delete(player.id); 
          system.clearRun(fadeInterval); 
          sendFrame(player, "panic_type_ender");
      }
  }, TICK_INTERVAL);

  runningPlayers.set(player.id,{interval:fadeInterval,loopInterval:null,soundInterval:null,stage:"fade",setStage:s=>{}});
}

function triggerPanicFaint(player, creature, aggressive=false){
  if(!player||!player.isValid()) return;

  const dimension = player.dimension;
  const playersNearby = dimension.getPlayers().filter(p => {
      if(!p||!p.isValid()) return false;
      const dx = p.location.x - player.location.x;
      const dy = p.location.y - player.location.y;
      const dz = p.location.z - player.location.z;
      const dist = Math.sqrt(dx*dx + dy*dy + dz*dz);
      return dist <= 45;
  });

  for(const p of playersNearby){
      if(faintPlayers.has(p.id)) continue;

      faintPlayers.set(p.id,{aggressive,stage:"start"});

      if(runningPlayers.has(p.id)){
          const data = runningPlayers.get(p.id);
          if(data.loopInterval) system.clearRun(data.loopInterval);
          if(data.soundInterval) system.clearRun(data.soundInterval);
          if(data.interval) system.clearRun(data.interval);
          runningPlayers.delete(p.id);
      }

      const totalTicks = FAINT_FRAME_UP_DURATION + FAINT_HOLD_DURATION + FAINT_FRAME_DOWN_DURATION;
      try{ p.runCommandAsync(`effect @s slowness ${PANIC_FAINT_SLOWNESS_LEVEL} ${Math.ceil(totalTicks/20)} true`); } catch(e){}

      let index = 0;
      const upInterval = system.runInterval(()=>{
          if(!p||!p.isValid()){ system.clearRun(upInterval); faintPlayers.delete(p.id); return; }
          sendFrame(p,PANIC_FAINT_FRAMES[index]);
          index++;
          if(index >= PANIC_FAINT_FRAMES.length){ system.clearRun(upInterval); holdFrame(p,creature,aggressive); }
      }, Math.floor(FAINT_FRAME_UP_DURATION/PANIC_FAINT_FRAMES.length));
  }
}

function holdFrame(player, creature, aggressive){
  if(!player||!player.isValid()){ faintPlayers.delete(player.id); return; }
  sendFrame(player, PANIC_FAINT_FRAMES[PANIC_FAINT_FRAMES.length-1]);

  if(!aggressive) teleportPlayerRandomNearby(player);
  if(!aggressive && creature && creature.isValid()) creature.triggerEvent("tc:start_digging_away_step1");

  if(Math.random() < 0.5){
      try { player.runCommand("time set night"); } catch(e){}
  }

  system.runTimeout(()=>reversePanicFaint(player,creature,aggressive), FAINT_HOLD_DURATION);
}

function reversePanicFaint(player, creature, aggressive){
  if(!player||!player.isValid()){ faintPlayers.delete(player.id); return; }
  let index = PANIC_FAINT_FRAMES.length-1;
  const downInterval = system.runInterval(()=>{
      if(!player||!player.isValid()){ 
          system.clearRun(downInterval); 
          faintPlayers.delete(player.id); 
          return; 
      }
      sendFrame(player,PANIC_FAINT_FRAMES[index]);
      index--;
      if(index<0){
          system.clearRun(downInterval);
          if(aggressive && creature && creature.isValid()) creature.triggerEvent("tc:blinding_vanish");
          faintPlayers.delete(player.id);
          sendFrame(player, "panic_type_ender");
      }
  }, Math.floor(FAINT_FRAME_DOWN_DURATION/PANIC_FAINT_FRAMES.length));
}

function teleportPlayerRandomNearby(player){
    if(!player||!player.isValid()) return;
    const dimension = player.dimension;
    const allowed = new Set(["minecraft:grass_block","minecraft:dirt","minecraft:coarse_dirt","minecraft:podzol","minecraft:snow","minecraft:snow_layer","minecraft:bush","minecraft:tall_grass"]);
    const ignored = new Set(["minecraft:oak_leaves","minecraft:spruce_leaves","minecraft:birch_leaves","minecraft:jungle_leaves","minecraft:acacia_leaves","minecraft:dark_oak_leaves","minecraft:mangrove_leaves","minecraft:azalea_leaves","minecraft:flowering_azalea_leaves","minecraft:cherry_leaves","tc:redwood_leaves"]);

    for(let attempt=0; attempt<50; attempt++){
        const angle = Math.random()*Math.PI*2;
        const dist = PANIC_FAINT_TELEPORT_MIN + Math.random()*(PANIC_FAINT_TELEPORT_MAX-PANIC_FAINT_TELEPORT_MIN);
        const x = Math.floor(player.location.x + Math.cos(angle)*dist);
        const z = Math.floor(player.location.z + Math.sin(angle)*dist);
        let y=320, solidY=null;
        while(y>0){
            const block = dimension.getBlock({x,y,z});
            if(block && !block.isAir && !ignored.has(block.typeId)){ solidY=y+1; break; }
            y--;
        }
        if(solidY===null) continue;
        const ground = dimension.getBlock({x,y:solidY-1,z});
        if(!ground || !allowed.has(ground.typeId)) continue;
        player.teleport({x,y:solidY,z},{dimension}); break;
    }
}

function isTriggerEnabled(creature) {
  return creatureTriggers.get(creature.id) ?? false;
}

function setTriggerEnabled(creature, state) {
  creatureTriggers.set(creature.id, state);
}

function startProximityCheck(creature){
  if(!creature||!creature.isValid()||proximityCheckIntervals.has(creature.id)) return;

  const interval = system.runInterval(()=>{
      if(!creature||!creature.isValid()){ 
          system.clearRun(interval); 
          proximityCheckIntervals.delete(creature.id); 
          return; 
      }

      const players = creature.dimension.getPlayers();
      for(const p of players){
          if(!p||!p.isValid()) continue;

          triggerEnabled.set(p.id, true);

          const dx = p.location.x - creature.location.x;
          const dy = p.location.y - creature.location.y;
          const dz = p.location.z - creature.location.z;
          const dist = Math.sqrt(dx*dx + dy*dy + dz*dz);

          if(dist <= PROXIMITY_RANGE){
              const score = playerScores.get(p.id) ?? 0;
              handleProximityEvent(p, creature, score);
          }
      }
  }, 10);

  proximityCheckIntervals.set(creature.id, interval);
}

function handleProximityEvent(player, creature, score) {
  if (faintPlayers.has(player.id)) return;
  if (!isTriggerEnabled(creature)) return;

  if (score >= 1 && score <= 2) {
    triggerPanicFaint(player, creature, false);
  } else if (score >= 3 && score <= 4) {
    const events = ["tc:aggressive_panic_faint","tc:blinding_vanish","tc:defensive_step1"];
    const chosen = events[Math.floor(Math.random() * events.length)];
    if (chosen === "tc:aggressive_panic_faint") {
      triggerPanicFaint(player, creature, true);
    } else {
      creature.triggerEvent(chosen);
    }
  } else if (score >= 5 && score <= 6) {
    const events = ["tc:attack_step1","tc:defensive_step1"];
    const chosen = events[Math.floor(Math.random() * events.length)];
    creature.triggerEvent(chosen);
  }

  setTriggerEnabled(creature, false);
}

system.afterEvents.scriptEventReceive.subscribe(ev => {
  const { id, sourceEntity } = ev;
  if (!sourceEntity || !sourceEntity.location) return;
  const dimension = sourceEntity.dimension;

  if (id === "tc:panic_close_check") {
    const players = dimension.getPlayers();
    for (const p of players) {
      const score = playerScores.get(p.id) ?? 0;
      playerScores.set(p.id, Math.min(score + 1, MAX_CLOSE_SCORE));
    }

    const creatures = dimension.getEntities({ type: CREATURE_TYPE });
    for (const c of creatures) {
      if (!proximityCheckIntervals.has(c.id)) {
        startProximityCheck(c);
      }
      setTriggerEnabled(c, true);
    }
  }
});

system.runInterval(() => {
  const dimension = world.getDimension("overworld");
  const creatures = dimension.getEntities({ type: CREATURE_TYPE });
  if (creatures.length === 0) return;

  for (const creature of creatures) {
      const players = dimension.getPlayers();
      for (const player of players) {
          if (!player || !player.isValid()) continue;
          const dx = player.location.x - creature.location.x;
          const dy = player.location.y - creature.location.y;
          const dz = player.location.z - creature.location.z;
          const dist = Math.sqrt(dx*dx + dy*dy + dz*dz);

          if (dist <= PANIC_DISTANCE && !faintPlayers.has(player.id)) {
              startPanic(player);
          }
      }

      let hunting = false;
      try { hunting = !!creature.getProperty("tc:hunting"); } catch(e) { hunting = false; }

      if (hunting) {
        if (!huntProximityIntervals.has(creature.id)) {
          console.warn(`[HuntProx] Starting hunt proximity monitor for creature ${creature.id}`);
          startHuntProximityCheck(creature);
        }
      } else {
        if (huntProximityIntervals.has(creature.id)) {
          const id = huntProximityIntervals.get(creature.id);
          try { system.clearRun(id); } catch(e){}
          huntProximityIntervals.delete(creature.id);
          huntLastTrigger.delete(creature.id);
          console.warn(`[HuntProx] Disabled hunt proximity monitor for creature ${creature.id}`);
        }
      }
  }
}, 20);

const TENSION_COOLDOWN_TICKS = 1200;
let lastTensionTick = -TENSION_COOLDOWN_TICKS;

system.afterEvents.scriptEventReceive.subscribe((event) => {
  if (event.id === "tc:roll_tension_sound") {
    const now = system.currentTick;

    if (now - lastTensionTick < TENSION_COOLDOWN_TICKS) {
      console.warn("[TensionSound] On cooldown, skipping.");
      return;
    }

    if (Math.random() < 0.20) {
      try {
        world.getDimension("overworld")
          .runCommand("execute at @e[type=tc:creature] run playsound mob.creature.tension @a[r=60]");
        console.warn("[TensionSound] Command executed (10% chance passed).");

        lastTensionTick = now;
      } catch (e) {
        console.warn("[TensionSound] Command failed:", e);
      }
    } else {
      console.warn("[TensionSound] 10% roll failed, no sound played.");
    }
  }
});

const HEART_ITEM_ID = "tc:heart_of_the_forest";
const HEART_COOLDOWN_CATEGORY = "tc_heart_of_forest";

world.afterEvents.itemUse.subscribe(event => {
  const { itemStack, source } = event;

  if (!source || !source.isValid() || source.typeId !== "minecraft:player") return;
  if (!itemStack || itemStack.typeId !== HEART_ITEM_ID) return;

  const cooldownComp = itemStack.getComponent("minecraft:cooldown");
  if (!cooldownComp) {
    console.warn(`TC: Item ${HEART_ITEM_ID} has no cooldown component (or component not supported).`);
    return;
  }

  if (!cooldownComp.isCooldownCategory(HEART_COOLDOWN_CATEGORY)) {
    console.warn(`TC: Item cooldown component category mismatch. Expected ${HEART_COOLDOWN_CATEGORY}, got ${cooldownComp.cooldownCategory}`);
    return;
  }

  const ticksLeft = cooldownComp.getCooldownTicksRemaining(source);
  if (ticksLeft > 0) {
    return;
  }

  try {
    source.runCommand(`effect @s resistance 60 1 true`);
    source.runCommand(`effect @s regeneration 60 1 true`);
    source.runCommand(`effect @s strength 60 1 true`);
    source.runCommand(`effect @s speed 60 1 true`);
  } catch(err) {
    console.warn("TC: error applying effects or sound:", err);
  }

  try {
    const invComp = source.getComponent("minecraft:inventory");
    if (invComp && invComp.container) {
      const inv = invComp.container;
      if (itemStack.amount > 1) {
        itemStack.amount -= 1;
        inv.setItem(source.selectedSlot, itemStack);
      } else {
        inv.setItem(source.selectedSlot, undefined);
      }
    }
  } catch(err) {
    console.warn("TC: error consuming item:", err);
  }

  try {
    cooldownComp.startCooldown(source);
  } catch(err) {
    console.warn("TC: error starting cooldown:", err);
  }
});

const leafBlocks = [
  "minecraft:oak_leaves",
  "minecraft:birch_leaves",
  "minecraft:spruce_leaves",
  "minecraft:jungle_leaves",
  "minecraft:acacia_leaves",
  "minecraft:dark_oak_leaves",
  "minecraft:mangrove_leaves",
  "minecraft:cherry_leaves",
  "minecraft:azalea_leaves",
  "minecraft:flowering_azalea_leaves"
];

const birdParticles = ["tc:bird1_flee", "tc:bird2_flee", "tc:bird3_flee"];

system.afterEvents.scriptEventReceive.subscribe((event) => {
  if (event.id !== "tc:bird_flee") return;

  const overworld = world.getDimension("overworld");

  for (const creature of overworld.getEntities({ type: "tc:creature" })) {
    const basePos = creature.location;

    let spawnedAny = false;

    for (let dx = -15; dx <= 15; dx++) {
      for (let dy = -15; dy <= 15; dy++) {
        for (let dz = -15; dz <= 15; dz++) {
          const block = overworld.getBlock({
            x: Math.floor(basePos.x) + dx,
            y: Math.floor(basePos.y) + dy,
            z: Math.floor(basePos.z) + dz
          });

          if (!block || !leafBlocks.includes(block.typeId)) continue;

          if (Math.random() < 0.10) {
            const particle = birdParticles[Math.floor(Math.random() * birdParticles.length)];
            overworld.spawnParticle(particle, block.center());
            spawnedAny = true;
          }
        }
      }
    }

    if (spawnedAny) {
      try {
        overworld.runCommand(
          "execute at @e[type=tc:creature] run playsound mob.bird_controller.bird_flee1 @a[r=60]"
        );
      } catch (err) {
        console.warn("TC: Failed to play bird_flee sound:", err);
      }
    }
  }
});

const FOREST_SETTINGS_OBJECTIVE = "forest_settings_tracker";

function ensureForestObjective() {
  let obj = world.scoreboard.getObjective(FOREST_SETTINGS_OBJECTIVE);
  if (!obj) {
    try {
      world.scoreboard.addObjective(FOREST_SETTINGS_OBJECTIVE, "Forest Settings Tracker");
      obj = world.scoreboard.getObjective(FOREST_SETTINGS_OBJECTIVE);
    } catch (e) {
      console.warn("[ForestSettings] Failed to create objective:", e);
    }
  }
  return obj;
}

function hasForestItemBeenGiven() {
  const obj = ensureForestObjective();
  if (!obj) return false;
  const part = obj.getParticipants().find(p => p.displayName === "forestGift");
  return part ? obj.getScore(part) === 1 : false;
}

function markForestItemGiven() {
  const obj = ensureForestObjective();
  if (!obj) return;
  obj.setScore("forestGift", 1);
}

world.afterEvents.playerSpawn.subscribe(ev => {
  if (!ev.initialSpawn) return;
  const ply = ev.player;
  if (!ply || !ply.isValid()) return;

  if (!hasForestItemBeenGiven()) {
    try { ply.sendMessage("§aYou have been granted the Call of the Forest Settings item."); } catch {}
    ply.runCommandAsync(`give @s tc:call_of_the_forest_settings 1`).catch(() => {});
    markForestItemGiven();
  }
});

function testSpawnEvent() {
  const players = world.getPlayers();
  if (!players.length) return console.warn("[SpawnDebug] No players online to test spawns.");
  
  const player = players[Math.floor(Math.random() * players.length)];
  const roll = Math.random();

  if (roll < 0.25) {
    console.warn("[SpawnDebug] Test Event: Normal spawn triggered.");
    trySpawnNearPlayer(player);
  } else if (roll < 0.5) {
    console.warn("[SpawnDebug] Test Event: Silent observe spawn triggered.");
    spawnCreatureSilent(player, "tc:behavior_observe");
  } else if (roll < 0.75) {
    console.warn("[SpawnDebug] Test Event: Silent hunt spawn triggered.");
    spawnCreatureSilent(player, "tc:behavior_hunt");
  } else {
    console.warn("[SpawnDebug] Test Event: Dirt patch + fire ritual triggered.");
    spawnDirtPatchWithFire(player);
  }
}

system.afterEvents.scriptEventReceive.subscribe((event) => {
  if (event.id === "tc:test_spawn") {
    console.warn("[SpawnDebug] /scriptevent test_spawn received.");
    testSpawnEvent();
  }
});

system.afterEvents.scriptEventReceive.subscribe(ev => {
  if (ev.id !== "tc:animal_close") return;

  const creature = ev.sourceEntity;
  if (!creature) {
    console.warn("[HuntDebug] Event has no source entity.");
    return;
  }
  if (creature.typeId !== "tc:creature") {
    console.warn(`[HuntDebug] Event source is not tc:creature (was ${creature.typeId}).`);
    return;
  }
  if (!creature.dimension) {
    console.warn("[HuntDebug] Creature has no dimension (invalid).");
    return;
  }

  let nearby = [];
  try {
    nearby = creature.dimension.getEntities({
      location: creature.location,
      maxDistance: 4
    });
  } catch (e) {
    console.warn("[HuntDebug] getEntities failed:", e);
    return;
  }

  const HUNTABLE_ANIMALS = new Set([
    "minecraft:sheep",
    "minecraft:cow",
    "minecraft:wolf",
    "minecraft:chicken",
    "minecraft:horse",
    "minecraft:fox",
    "minecraft:pig"
  ]);

  const candidates = nearby.filter(ent => ent && ent.isValid && ent.isValid() && HUNTABLE_ANIMALS.has(ent.typeId));

  if (!candidates.length) {
    console.warn("[HuntDebug] No huntable animals found within range.");
    return;
  }

  let target = candidates[0];
  let minDistSq = (target.location.x - creature.location.x) ** 2
                + (target.location.y - creature.location.y) ** 2
                + (target.location.z - creature.location.z) ** 2;

  for (let i = 1; i < candidates.length; i++) {
    const a = candidates[i];
    const dx = a.location.x - creature.location.x;
    const dy = a.location.y - creature.location.y;
    const dz = a.location.z - creature.location.z;
    const d2 = dx*dx + dy*dy + dz*dz;
    if (d2 < minDistSq) {
      minDistSq = d2;
      target = a;
    }
  }

  if (!target || !target.isValid || !target.isValid()) {
    console.warn("[HuntDebug] Chosen target invalid.");
    return;
  }

  try {
    target.addTag("tc:orvat_sn");
    console.warn(`[HuntDebug] Tagged target ${target.typeId} at [${target.location.x.toFixed(1)}, ${target.location.y.toFixed(1)}, ${target.location.z.toFixed(1)}]`);
  } catch (e) {
    console.warn("[HuntDebug] Failed to add tag to target:", e);
  }

  const outcomeRoll = Math.random();

  try {
    creature.runCommand("effect @s slowness 3 5 true");
    console.warn("[HuntDebug] Gave creature slowness for the 2.5s window.");
  } catch (e) {
    console.warn("[HuntDebug] Could not give slowness via command:", e);
  }

  system.runTimeout(() => {
    try {
      creature.runCommand("effect @s clear slowness");
      console.warn("[HuntDebug] Cleared creature slowness.");
    } catch (e) {
      console.warn("[HuntDebug] Could not clear slowness via command:", e);
    }

    if (!target || !target.isValid || !target.isValid()) {
      console.warn("[HuntDebug] Target invalid at outcome time — aborting.");
      return;
    }

    if (outcomeRoll < 0.10) {
      try {
        target.runCommand("effect @s weakness 60 1 true");
        target.runCommand("effect @s slowness 60 2 true");
        target.runCommand("effect @s poison 60 1 true");
      } catch (e) {
        console.warn("[HuntDebug] Failed to apply effects to target:", e);
      }

      try { target.removeTag("tc:orvat_sn"); } catch (e) { console.warn("[HuntDebug] Failed to remove tag:", e); }

      try {
        creature.runCommand("execute as @e[type=tc:creature] run ride @e[tag=tc:orvat_sn] stop_riding");
      } catch (e) {
        console.warn("[HuntDebug] ride stop_riding command failed:", e);
      }

      try { creature.triggerEvent("tc:run_away_vanish"); } catch (e) { console.warn("[HuntDebug] Failed to trigger vanish event:", e); }
      console.warn("[HuntDebug] Outcome: INFECT + VANISH (10%).");
      return;
    }

    if (outcomeRoll < 0.60) {
      try {
        if (typeof target.kill === "function") target.kill();
        else target.runCommand("kill @s");
      } catch (e) {
        console.warn("[HuntDebug] Failed to kill target via API, trying command:", e);
        try { target.runCommand("kill @s"); } catch (ee) { console.warn("[HuntDebug] kill command failed too:", ee); }
      }
      try { creature.triggerEvent("tc:behavior_hunt_decider"); } catch (e) { console.warn("[HuntDebug] Failed to trigger hunt_decider:", e); }
      console.warn("[HuntDebug] Outcome: KILL + HUNT_DECIDER (50%).");
      return;
    }

    try { creature.triggerEvent("tc:run_away_vanish"); } catch (e) { console.warn("[HuntDebug] Failed to trigger abduct event:", e); }
    console.warn("[HuntDebug] Outcome: ABDUCT (30%).");

  }, 50);
});

function triggerStageEvent(eventName) {
  const entities = world.getDimension("overworld").getEntities({
    type: HUNTER_SPAWN_ENTITY_TYPE,
  });

  if (entities.length === 0) {
    console.warn(`[StageTrigger] No creature found to trigger ${eventName}`);
    return;
  }

  const creature = entities[0];
  creature.triggerEvent(eventName);
  console.warn(`[StageTrigger] Triggered ${eventName} on ${creature.typeId}`);
}

system.afterEvents.scriptEventReceive.subscribe(ev => {
  if (ev.id === "tc:stage3_step2") {
    triggerStageEvent("tc:stalk_stage3_step2");
  }
});

system.afterEvents.scriptEventReceive.subscribe(ev => {
  if (ev.id === "tc:stage4_step2") {
    triggerStageEvent("tc:stalk_stage4_step2");
  }
});

system.afterEvents.scriptEventReceive.subscribe(ev => {
  if (ev.id === "tc:stage5_step2") {
    triggerStageEvent("tc:stalk_stage5_step2");
  }
});