/* 
Feel free to take a look at the code (it's a mess i dont recommend copying it)

If u have questions on how stuff is made you can dm me on discord @juliusscizzor (i may or may not see it tho)
*/

import { world, system, ItemStack, Player, EntityDamageCause, BlockVolume, MolangVariableMap, Entity, WeatherType, GameMode, Block, InputMode } from "@minecraft/server";

import { MAPS, ITEM_LIST, EMOTE_LIST, BLOCKS, ENTITIES, DEATH_CAUSES, DEATH_BLOCKS, COMPANY_MOODS, SOUNDS, OUTDOOR_SPAWNABLE_BLOCKS, MOON_LOOT_TABLE } from "./data";
import { updateProfitDisplay, exitTerminal, credits1, credits2, weather } from "./terminal";

import { uiManager } from "@minecraft/server-ui";

import { UiManager } from './ui'

import { Vector3, Vector2 } from "./VectorMath/index";

import "./shipSystems";
import "./terminal";
import "./generation/index";
import { damagePlayer } from "./initialize";

import { broadcastNoise } from "./noise";
import { companyRuntime } from "./company";
import "./ui"
import { SPAWNABLE_BLOCKS } from "./generation/tileSets";

// Declare variables at the top
export let OVERWORLD;
export let SOUND_OPTIONS = { volume: 1, pitch: 1 };
const COOLDOWN_DURATION = 10;
export let main;
export let loading;
export let creditsScoreboard;
export let worldPlayers

export let shipEntity = null;

let allPlayersAreSpectators = false;
let spectatablePlayers;
let spectators;
let fired = false;

world.afterEvents.worldLoad.subscribe(() => {

  // Set exported variables after world loads
  OVERWORLD = world.getDimension("overworld");
  SOUND_OPTIONS = { volume: 1, pitch: 1 };
  main = world.scoreboard.getObjective("main") ?? world.scoreboard.addObjective("main");
  loading = world.scoreboard.getObjective("loading") ?? world.scoreboard.addObjective("loading");
  creditsScoreboard = world.scoreboard.getObjective("credits") ?? world.scoreboard.addObjective("credits");

  // Company Initialization
  const companyEntity = OVERWORLD.getEntities({ type: "js:company" });
  for (let company of companyEntity) {
    companyRuntime(company);
  }

  // Monster Initialization
  const monsters = OVERWORLD.getEntities({ families: ["monster"] });
  for (let monster of monsters) {
    monster.openingDoor = false;
  }

  const shipEntities = OVERWORLD.getEntities({ type: "js:ship" });
  for (let ship of shipEntities) {
    ship.runCommand(`scriptevent js:stopsound_ship_ambience`);
    ship.runCommand(`scriptevent js:playsound_ship_ambience`);
  }

  system.runInterval(() => {
    //console.warn(shipEntity)
    if (shipEntity == null || shipEntity == undefined) {
      findShip();
    } else {
      //console.warn(JSON.stringify(shipEntity.location))
    }
  });
  
  
  
  //Player Initialization
  worldPlayers = world.getPlayers();
  
  const timeDisplay = display12HourTime(world.getDynamicProperty("time"));
  for (let player of worldPlayers) {
    player.uiManager = new UiManager(player)
    player.uiManager.updateTimeDisplay(timeDisplay)
    initializePlayer(player);
    console.warn(`Registered ${player.nameTag} with index of ${worldPlayers.indexOf(player)}`)
  }
  
  main.setScore("alivePlayers", worldPlayers.length);
  
  if (worldPlayers && worldPlayers.length > 0 && shipEntity) {
    updateShipVol();
    updateProfitDisplay();
  }

  

  moveBarber();

})



world.afterEvents.playerSpawn.subscribe((ev) => {
  const player = ev.player;

  player.addEffect("weakness", 20000000, { amplifier: 255, showParticles: false });

  if (ev.initialSpawn) {
    player.uiManager = new UiManager(player)
    initializePlayer(player);

    worldPlayers.push(player);

  }
});
world.beforeEvents.playerLeave.subscribe(({ player }) => worldPlayers.splice(worldPlayers.indexOf(player), 1));



world.afterEvents.entityLoad.subscribe((ev) => {
  const entity = ev.entity;
  if (entity.typeId == "js:company") {
    companyRuntime(entity);
  }
});

world.afterEvents.entitySpawn.subscribe((ev) => {
  const entity = ev.entity;

  if (entity.typeId == "js:bunker_spider" || entity.typeId == "js:bunker_spider_dead") {
    const arachnophobiaMode = world.getDynamicProperty('arachnophobiaMode') 
    if (arachnophobiaMode == true) {
      entity.setProperty('js:type', 1)
    } else {
      entity.setProperty('js:type', 0)
    }
  }

  if (entity.typeId == "js:tree" || entity.typeId == "js:alive_tree") {
    const pos1 = {
      x: entity.location.x,
      y: entity.location.y + 1,
      z: entity.location.z
    }
    const pos2 = {
      x: entity.location.x,
      y: entity.location.y + 10,
      z: entity.location.z
    }
    const fillArea = new BlockVolume(pos1, pos2)
    entity.dimension.fillBlocks(fillArea, 'ding:tree_collision', {blockFilter: {includeTypes: ["minecraft:air"]}})
  }

  if (entity.typeId == "js:company") {
    companyRuntime(entity);

    const day = /** @type {number} */ (world.getDynamicProperty("day")) ?? 0;
    if (day == 4) {
      entity.triggerEvent("sell30");
    } else if (day == 3) {
      entity.triggerEvent("sell53");
    } else if (day == 2) {
      entity.triggerEvent("sell77");
    } else if (day <= 1) {
      entity.triggerEvent("sell100");
    }
  }

  if (entity.typeId == "minecraft:lightning_bolt") {
    try {
      OVERWORLD.spawnParticle("ding:landmine_impact", entity.location);
      OVERWORLD.spawnParticle("ding:landmine_flames", entity.location);
      OVERWORLD.spawnParticle("ding:old_bird_missile_smoke", entity.location);

      const hit = OVERWORLD.getEntities({
        location: entity.location,
        maxDistance: 4,
        excludeFamilies: ["scrap", "inanimate"],
      });

      for (let entityHit of hit) {
        if (entityHit.typeId == "js:old_bird" || entityHit.typeId == "js:earth_leviathan" || entityHit.typeId == "js:forest_keeper") continue;

        if (entityHit instanceof Player) {
          lightingKnockback(entityHit, entity)
          damagePlayer(entityHit, 100, entity, EntityDamageCause.lightning);
        } else {
          entityHit.applyDamage(100, {
            cause: EntityDamageCause.void,
          });
        }
      }
    } catch { }
  }
});



function lightingKnockback(player, entity) {
  console.warn(player.typeId)
  const maxRange = 4;
  let knockbackResistance = 0.9;
  let knockbackMultiplier = 1 / (1 - knockbackResistance); // Multiplier to nullify resistance
  if (player.typeId !== "minecraft:player") {
    knockbackMultiplier = 1
  }
  // Get the positions of the player and entity
  const playerPos = player.location
  let entityPos = entity.location
  if (player.typeId == "js:body_ent") {
    entity.location.x -= 0.1
  }

  // Calculate the distance between the player and the entity
  const dx = entityPos.x - playerPos.x + (Math.random() * 6 - 3);
  const dy = entityPos.y - playerPos.y;
  const dz = entityPos.z - playerPos.z + (Math.random() * 6 - 3);
  const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

  // If the entity is within the max range, calculate knockback
  if (distance <= maxRange) {
    // Determine the knockback strength based on the distance
    const strengthFactor = (maxRange - distance) / maxRange;
    const baseHoriStrength = 8;  // Base horizontal knockback strength
    const baseVertStrength = 4;  // Base vertical knockback strength
  
    // Calculate the scaled knockback strengths
    const horiStrength = baseHoriStrength * strengthFactor * knockbackMultiplier;
    const vertStrength = baseVertStrength * strengthFactor * knockbackMultiplier;
  
    // Normalize the direction vector (dx, dz)
    const directionLength = Math.sqrt(dx * dx + dz * dz);
    const horiDirectionX = dx / directionLength;
    const horiDirectionZ = dz / directionLength;
  
    // Calculate final horizontal force
    const horizontalForce = {
      x: horiDirectionX * horiStrength,
      z: horiDirectionZ * horiStrength
    };
  
    // Apply the knockback to the entity
    player.applyKnockback(horizontalForce, vertStrength);
  }
  
}





export function findShip() {
  const findShip = system.runInterval(() => {
    const shipEntities = OVERWORLD.getEntities({ type: "js:ship" });
    if (shipEntities.length > 0) {
      shipEntity = shipEntities[0];

      //  console.warn(`Ship found: ${shipEntity.id}, anim state ${shipEntity.getProperty('js:animation')}`)

      system.clearRun(findShip);

      updateShipVol();
      updateProfitDisplay();
    }
  });
}



export function updateShipVol() {
  const inOrbit = world.getDynamicProperty("inOrbit");
  if (!shipEntity) return;
  const shipLocation = shipEntity.location;
  let topLeftCorner = {
    x: shipLocation.x + 4,
    y: shipLocation.y + 3,
    z: shipLocation.z + 1,
  };
  let bottomLeftCorner = {
    x: shipLocation.x - 4,
    y: shipLocation.y,
    z: shipLocation.z - 12,
  };

  if (inOrbit == true) {
    topLeftCorner.y += 44;
    bottomLeftCorner.y += 44;

    topLeftCorner.z -= 40;
    bottomLeftCorner.z -= 40;
  }
  const shipVol = new BlockVolume(topLeftCorner, bottomLeftCorner);
    // console.warn(`${JSON.stringify(shipVol.getMin())} ${JSON.stringify(shipVol.getMax())}`)
  return shipVol;
}



/*
const findHost = system.runInterval(() => {
    const players = world.getAllPlayers()
    if (players.length > 0 && !players[0].hasTag("Host")) {
        players[0].addTag("Host")
        console.warn(`Host ${players[0].nameTag}`)
        
        system.clearRun(findHost)
    }
})
*/




/**
 * @param {Player} player
 */
export function initializePlayer(player) {
  player.stamina = 100;
  player.setProperty("js:player_exhaustion", false);
  loading.setScore(player, 0);

  player.fear = 0;
  player.insanity = 0;
  player.fearCooldown = 0;
  player.textCooldown = 0;
  player.injuries = 0;
  player.laziness = 0;
  player.paranoia = 0;
  player.profitability = 0;
  player.state = "alive";
  player.wasCriticallyInjured = false

  player.isInteracting = false;

  player.causeOfDeath = "unknown";

  player.spectateCooldown = COOLDOWN_DURATION;

  player.runCommand(`function playerInitialize`);

  const scaledScore = Math.min(Math.floor((loading.getScore(player) ?? 0) / 10), 100);
  player.uiManager.updateLoading(scaledScore)

  updateWeight(player);

  player.addEffect("weakness", 20000000, { amplifier: 255, showParticles: false });
  // player.removeEffect("jump_boost");
  player.runCommand(`inputpermission set @s jump enabled`)
  player.triggerEvent("not_tired");
  player.triggerEvent("not_under_flood");
  player.setProperty("js:flashlight_state", 0);
  player.setProperty("js:energy", 0);
  player.triggerEvent("reset");
  player.removeTag('cutscene')

  const weather = world.getDynamicProperty('currentWeather') 
  const inOrbit = world.getDynamicProperty("inOrbit")
  if (inOrbit == false) {
      const water = player.dimension.getEntities({name:player.nameTag,type:'ding:water'})
      if (water) {
        for (let waterEnt of water) {
          waterEnt.remove()
        }
      }
  }

  system.runTimeout(() => {
    if (
      inOrbit == false &&
      !player.hasTag("inGame") &&
      !player.hasTag("spectator") &&
      player.state == "alive"
    ) {
      player.setGameMode(GameMode.adventure);
      damagePlayer(player, 100, null, EntityDamageCause.void);
    }
    if (inOrbit == true) {
      if (player.hasTag('spectator')) {
        player.removeTag("spectator")
        player.setGameMode(GameMode.adventure);
      }
      let teleportLocation = shipEntity.location;
      teleportLocation.x += 0.5;
      teleportLocation.y += 45;
      teleportLocation.z -= 44.5;
      if (!player.hasTag("debug")) {
        player.teleport(teleportLocation);
      }

      player.removeTag("inFacility");
      player.state = "alive";
      if (!player.hasTag("debug")) {
        player.setGameMode(GameMode.adventure);
      }
    }
  }, 10);
}

export function getDistance(firstCoordinate, secondCoordinate) {
  return Math.sqrt(
    Math.pow(firstCoordinate.x - secondCoordinate.x, 2) +
    Math.pow(firstCoordinate.y - secondCoordinate.y, 2) +
    Math.pow(firstCoordinate.z - secondCoordinate.z, 2)
  );
}


const playerData = {};

function updatePlayerParanoia(player) {
  const currentRotation = player.getRotation();
  const playerId = player.id;

  if (!playerData[playerId]) {
    playerData[playerId] = {
      previousRotation: currentRotation,
    };
    // console.warn(`Initialized data for player ${playerId}`);
  }

  const previousRotation = playerData[playerId].previousRotation;

  if (currentRotation.x !== previousRotation.x || currentRotation.y !== previousRotation.y) {
    player.paranoia++;

    playerData[playerId].previousRotation = currentRotation;
    player.addTag('turning')
    //console.warn(`Player ${playerId} rotated. New paranoia: ${playerData[playerId].paranoia}`);
  } else {
    //console.warn(`Player ${playerId} did not rotate.`);
    player.removeTag('turning')
  }
}

system.runInterval(() => {
  const inOrbit = world.getDynamicProperty("inOrbit");

  if (inOrbit == false) {
    for (let player of worldPlayers) {
      let hallucinationChance = 0;
      if (player.insanity >= 10) {
        hallucinationChance = 15;
      }
      if (player.insanity >= 20) {
        hallucinationChance = 25;
      }
      if (player.insanity >= 40) {
        hallucinationChance = 40;
      }

      const random = Math.floor(Math.random() * 101);

      if (random < hallucinationChance) {
        if (player.nameTag !== "Thebonniestar") {
          if (player.insanity < 20) {
            player.playSound('game.ambient.strange_noise', {volume: 0.5, pitch: 1})
          }
          if (player.insanity >= 20 && player.insanity < 30) {
            player.playSound('game.ambient.strange_noise', {volume: 1, pitch: 1})
          }
          if (player.insanity >= 30) {
            player.playSound('game.ambient.strange_noise_insane', {volume: 1, pitch: 1})
          }
        } else {
          if (player.insanity < 20) {
            player.playSound('game.ambient.bonnie_noise', {volume: 0.5, pitch: 1})
          }
          if (player.insanity >= 20 && player.insanity < 30) {
            player.playSound('game.ambient.bonnie_noise', {volume: 1, pitch: 1})
          }
        }
      }
    }
  }
}, 13 * 20);



function isPlayerNearShip(player, shipMiddleLocation, radius = 15) {
  const playerLocation = player.location;

  const distance = Math.sqrt(
    Math.pow(playerLocation.x - shipMiddleLocation.x, 2) +
    Math.pow(playerLocation.y - shipMiddleLocation.y, 2) +
    Math.pow(playerLocation.z - shipMiddleLocation.z, 2)
  );

  return distance <= radius;
}


let interval = 0;

system.runInterval(() => {
  const moonId = world.getDynamicProperty("moon");
  const weather = world.getDynamicProperty('currentWeather')
  
  const inOrbit = world.getDynamicProperty("inOrbit");
  let shipLocation;
  let mapName;
  for (const key of Object.keys(MAPS)) {
    if (MAPS[key].id === moonId) {
      mapName = key;
      shipLocation = MAPS[key].shipLocation;
      break;
    }
  }

  // if (inOrbit == true) {
  //   worldPlayers.forEach((player) => {
  //       //  player.addEffect("instant_health", 2, { amplifier: 255, showParticles: false });
  //       // player.addEffect("resistance", 2, { amplifier: 255, showParticles: false });
  //   });
  // }

  let highestSusMeterPlayer
  let seen

  for (let player of worldPlayers) {
    // const command = player.runCommand(`fill ~-24 ~-6 ~-24 ~24 ~6 ~24 air replace ding:tree_collision`)
    // console.warn(command.successCount)

    updatePlayerParanoia(player);
  
    const vel = player.getVelocity();
  
    const isMoving = vel.x != 0 || vel.y != 0 || vel.z != 0;
  
    const water = player.dimension.getEntities({name:player.nameTag,type:'ding:water'})
    // flood weather
    if (weather == 3 && inOrbit == false && water) {

      water[0].teleport({x:player.location.x,y:60,z:player.location.z})
      
      water[0].playAnimation('animation.water_scale',{controller:'water',blendOutTime:999999,players:[player]})

      const minHeight = MAPS[mapName].floodMinHeight
      const maxHeight = MAPS[mapName].floodMaxHeight

      const currentTime = world.getDynamicProperty("time");
      const waterIncrement = (maxHeight - minHeight)/540
      const newHeight = minHeight+(waterIncrement*currentTime)

      const light = Math.min(Math.max((15-((currentTime/985)*15))+2,0),15)
      water[0].setProperty('ding:light',light)


      if (newHeight<=maxHeight) {
        water[0].setProperty('ding:y_level',newHeight)

      } else {
        water[0].setProperty('ding:y_level',maxHeight)
      }

      if (((player.getHeadLocation().y-0.2)<=(water[0].getProperty('ding:y_level')-1)) && !player.hasTag('underFlood') && !player.hasTag('inFacility')) {
        player.triggerEvent('under_flood')
        player.addEffect('slow_falling',9999999,{amplifier:254,showParticles:false})
        player.playSound('player.fall.water',{location:player.location})

        let vv = ""
        if (player.graphicsMode == "Deferred") vv = "_vv"
        player.runCommand(`fog @s push ding:flood_fog${vv} flood`);

        player.camera.fade({
          fadeColor: {red:66/255, green:83/255, blue:87/255}, 
          fadeTime: {fadeInTime:0.1, holdTime:0.5, fadeOutTime:3}
        })

        player.addTag('underFlood')
        player.isUnderwater = true
      }


      if ((player.getHeadLocation().y-0.2)>(water[0].getProperty('ding:y_level')-1) || player.hasTag('inFacility')) {
        player.removeEffect('slow_falling')
        player.runCommand(`fog @s remove flood`);
        player.triggerEvent('not_under_flood')
        player.removeTag('underFlood')
        player.isUnderwater = false
      }
    }

    if (interval % 10 == 0) {
      if (!player.hasTag("spectator")) {
        const exhaustion = player.getProperty("js:player_exhaustion");

        if (isMoving && player.isSprinting && exhaustion == false) {
          const range = SOUNDS["sprint"].range;
          const volume = SOUNDS["sprint"].volume;
          broadcastNoise("sprint", player.location, range, volume);
        }
        if (isMoving && !player.isSprinting && !player.isSneaking) {
          const range = SOUNDS["walk"].range;
          const volume = SOUNDS["walk"].volume;
          broadcastNoise("walk", player.location, range, volume);
        }
      }

      if (inOrbit == false && (moonId == 7 || moonId == 9) && !player.hasTag("spectator") && !player.hasTag('inFacility')) {
        player.spawnParticle('js:rend_fog', player.location)
        player.spawnParticle('js:rend_snow', player.location)
      }
      if (inOrbit == false && (weather == 2 || weather == 3 || weather == 4) && !player.hasTag("cutscene") && !player.hasTag("spectator") && !player.hasTag('inFacility')) {
        player.spawnParticle('ding:rain', player.location)
      }

      let shipMiddleLocation = {
        x: shipEntity.location.x,
        y: shipEntity.location.y,
        z: shipEntity.location.z - 6
      }
      const isNearShip = isPlayerNearShip(player, shipMiddleLocation, 15)
      if (isNearShip) {
        player.addTag('nearShip')
      } else {
        player.removeTag('nearShip')
      }
    }

    if (player.hasTag('giantView')) {
      seen = true
    } else {
      seen = false
    }

    if (interval % 20 == 0) {

      //Clamp(Time.deltaTime / (distanceToPlayer * .21) * visibilityModifier, 0, 1)
      const giants = OVERWORLD.getEntities({ type:'js:forest_keeper', location: player.location, closest: 1 })
      if (giants && giants.length > 0) {
        const distanceToPlayer = getDistance(player.location, giants[0].location)
        let visibilityModifider = 1

        if (player.isSneaking) {
          visibilityModifider = 1
        } else {
          visibilityModifider = 2
        }
  
        if (isMoving) {
          visibilityModifider += 1
        }
        if (player.hasTag('giantView')) {
          // 0.21
          let susValue = Math.min(Math.max(1 / (distanceToPlayer * 0.21) * visibilityModifider, 0), 1);
          // console.warn(`yeee ${susValue}`)

          let newSusValue = player.getProperty('js:sus_meter') + susValue
          if (newSusValue > 10) {
            newSusValue = 10
          }
          player.setProperty('js:sus_meter', newSusValue)
          changeFear(player, 0.2, true);

          for (let player2 of worldPlayers) {
            if (player2 !== player) {
              let newSusValue2 = player.getProperty('js:sus_meter') - 0.33
              if (newSusValue2 < -10) {
                newSusValue2 = -10
              }
              player2.setProperty('js:sus_meter', newSusValue2)
            }
          }
        }
        // player.onScreenDisplay.setActionBar(`
        //   Sus ${player.getProperty('js:sus_meter')}
        //   Dist ${distanceToPlayer} 
        //   Mod ${visibilityModifider}
        //   Seen ${seen}
        // `)
      }

      if (player.getProperty('js:sus_meter') > -Infinity) {
        highestSusMeterPlayer = player
        // console.warn(highestSusMeterPlayer.nameTag)
      }
   
      if (player == highestSusMeterPlayer) {
        player.addTag('giantHighestTarget')
      } else {
        player.removeTag('giantHighestTarget')
      }

      player.fear = player.fear - 0.055;

      const currentTime = world.getDynamicProperty("time");
      if (inOrbit == false) {
        if (worldPlayers.length > 1) {
          const nearPlayers = OVERWORLD.getPlayers({
            location: player.location,
            maxDistance: 7
          });

          if (nearPlayers.length <= 1) {
            if (player.hasTag("inFacility")) {
              player.insanity = player.insanity + 0.8;
            } else {
              if (currentTime < 570) {
                player.insanity = player.insanity + 0.3;
              } else {
                player.insanity = player.insanity + 0.5;
              }
            }

            if (player.nameTag == "Thebonniestar") {
              player.insanity = player.insanity + 0.3;
            }
          } else {
            player.insanity = player.insanity - 3;
          }
        } else {
          if (player.hasTag("inFacility")) {
            player.insanity = player.insanity + 0.5;
          } else {
            player.insanity = player.insanity - 2;
          }
        }

        const shotgunRandom = Math.random()
        if (shotgunRandom < 0.1) {
          shotgunWieldedMisfire(player)
        }
      }
      
      if (player.location.y < -64) {
        if (inOrbit == false) {
          if (player.hasTag("inFacility")) {
            let teleportLocation = {
              x: shipLocation.x + 3.99,
              y: shipLocation.y - 60 - 30,
              z: shipLocation.z + 1.8,
            };

            if (world.getDynamicProperty('facilityType') == "mansion") {
              teleportLocation = {
                x: shipLocation.x + 12.0,
                y: shipLocation.y - 74 - 30,
                z: shipLocation.z + 1.5,
              };
            }
            player.teleport(teleportLocation, { rotation: { x: 0, y: 0 } });
          } else {
            damagePlayer(player, 100, null, EntityDamageCause.override)
          }
        }
      }
    }

    if (player.insanity > 50) {
      player.insanity = 50;
    }
    if (player.insanity < 0) {
      player.insanity = 0;
    }

    if (player.fearCooldown > 0) {
      player.fearCooldown--;
    }
    if (player.fearCooldown < 0) {
      player.fearCooldown = 0;
    }
    
    if (player.textCooldown > 0) {
      player.textCooldown--;
    }
    if (player.textCooldown < 0) {
      player.textCooldown = 0;
    }

    const timerIcon = getTimePeriod(world.getDynamicProperty("time"));
    if (player.hasTag("underground") || moonId == 0) {
      player.uiManager.updateTimerIcon(0)
    } else {
      player.uiManager.updateTimerIcon(timerIcon)
    }


    if (player.hasTag("spectator")) {
      player.uiManager.updateHud(0)
      player.addEffect("invisibility", 6, { showParticles: false });

      if (worldPlayers[player.spectatorIndex]) {
        player.onScreenDisplay.setActionBar(
          `Now Spectating: ${worldPlayers[player.spectatorIndex].nameTag}\n(Sprint to spectate other players)`
        );
        
      }

    } else {
      player.uiManager.updateHud(1)
      const movement = player.getComponent("minecraft:movement");

      const speed = (Math.sqrt(vel.x ** 2 + vel.z ** 2) * 20).toFixed(1);

      if (isMoving) {
        player.laziness++;
      }
      const exhaustion = player.getProperty("js:player_exhaustion");
      if ((!player.isSprinting && isMoving) || (player.isSprinting && isMoving && exhaustion == true)) {
        player.stamina += 7 / 20;
      }
      if (!player.isSprinting && !isMoving) {
        player.stamina += 11 / 20;
      }
      if (player.isSprinting && exhaustion == false) {
        const decrementAmount = 15 * (player.totalWeight / 105 + 1);
        player.stamina -= decrementAmount / 20;
      }

      if (player.criticallyInjured == true) {
        player.stamina = 0;
      }

      const block = OVERWORLD.getBlock({ x: player.location.x, y: player.location.y, z: player.location.z });
      if (player.stamina <= 20 && block.typeId == "minecraft:water") {
        player.applyKnockback({ x: 0, z: 0 }, -0.1);
      }
      if (block.typeId == "minecraft:water") {
        player.stamina -= 1;
      }
      if (weather == 3 && water[0] && ((player.getHeadLocation().y-0.2)<=(water[0].getProperty('ding:y_level')-1)) && !player.hasTag('inFacility')) {
        player.stamina -= 1
      }

      player.stamina = Math.min(Math.max(player.stamina, 0), 100);
      //  const unclampedStaminaLevel = Math.ceil((player.stamina / 100) * 8);
      player.setProperty("js:stamina", Math.min(Math.max(player.stamina, 0), 100));

      if (exhaustion == false && player.stamina <= 0) {
        player.setProperty("js:player_exhaustion", true);
        // player.triggerEvent("nojump");
        player.runCommand(`inputpermission set @s[m=!c] jump disabled`)
        player.triggerEvent("tired");
      }

      if (exhaustion == true && player.stamina >= 20) {
        player.setProperty("js:player_exhaustion", false);
        // player.removeEffect("jump_boost");
        player.runCommand(`inputpermission set @s jump enabled`)
        player.triggerEvent("not_tired");
      }

      let movementSpeed = 0.083;
      if (player.totalWeight > 0) {
        //  console.warn( (1 - (((33 / 80) * player.totalWeight + (207 / 16)) / 100)) )

        movementSpeed = 0.08 * (1 - ((33 / 80) * player.totalWeight + 207 / 16) / 100);
      }
      if (exhaustion == false && player.isSprinting && player.stamina > 0) {
        movementSpeed *= 2;
      }
      if (player.isSneaking) {
        movementSpeed += 0.04
      }

      if (player.isUnderwater || (!player.hasTag('inFacility') && water[0] && (player.location.y <= (water[0].getProperty('ding:y_level') - 1)))) {
        movementSpeed -= 0.025
      }
      movement.setCurrentValue(movementSpeed);

      if (exhaustion == true || player.criticallyInjured == true) {
        player.setProperty("js:tired", true);
      } else {
        player.setProperty("js:tired", false);
      }
      const blockUnder = OVERWORLD.getBlock({ x: player.location.x, y: player.location.y - 0.6, z: player.location.z });
      if (blockUnder.typeId == "minecraft:air" && vel.y < 0) {
        player.fallDistance += 0.75;
      } else {
        player.fallDistance = 0;
      }
      if (player.fallDistance > 0) {
        player.fell = player.fallDistance;
      }
      if (player.fallDistance == 0 && player.fell > 0 && blockUnder.typeId !== "minecraft:air") {
        // console.warn(`${player.nameTag} fell ${player.fell}`)
        const block = OVERWORLD.getBlock({ x: player.location.x, y: player.location.y - 0.1, z: player.location.z });

        const range = SOUNDS["fall"].range;
        const volume = SOUNDS["fall"].volume;
        broadcastNoise("fall", player.location, range, volume);

        if (DEATH_BLOCKS.includes(block.typeId)) {
          damagePlayer(player, 100, null, EntityDamageCause.fallingBlock);
        }
        if (player.fell >= 15 && player.fell < 20) {
          damagePlayer(player, 30, null, EntityDamageCause.fallingBlock);
        } else if (player.fell >= 20 && player.fell < 25) {
          damagePlayer(player, 50, null, EntityDamageCause.fallingBlock);
        } else if (player.fell >= 25 && player.fell < 30) {
          damagePlayer(player, 80, null, EntityDamageCause.fallingBlock);
        } else if (player.fell >= 30) {
          damagePlayer(player, 100, null, EntityDamageCause.fallingBlock);
        }
        player.fell = 0;
      }

      // const orbit = world.getDynamicProperty("inOrbit");
      // const day = world.getDynamicProperty("day");
      // const daysSurvived = world.getDynamicProperty("daysSurvived");
      // const scrapSold = world.getDynamicProperty("scrapSold");
      // const quota = world.getDynamicProperty("quota");
      // const quotasFulfilled = world.getDynamicProperty("quotasFulfilled");
      //  player.onScreenDisplay.setActionBar(`${speed} ${movementSpeed} ${player.stamina} ${player.getComponent("health").currentValue} | Moon: ${moonId} | Orbit: ${orbit} | Day: ${day} | Survived: ${daysSurvived}\nScrap Sold: ${scrapSold} | Quota: ${quota} | Fulfilled: ${quotasFulfilled} | Time: ${world.getDynamicProperty('time')} | FallDist : ${player.fallDistance} ${player.fell}\n| Current Rooms: ${world.getDynamicProperty('currentRooms')} | paranoia ${player.paranoia} profit ${player.profitability} lazi ${player.laziness} injur ${player.injuries} state ${player.state} \nPower ${world.getDynamicProperty('currentPowerLevel')} out ${world.getDynamicProperty('currentOutdoorPowerLevel')} Gen ${world.getDynamicProperty('generationPercentage')} Possible ${world.getDynamicProperty('totalScrapPossible')} Coll ${world.getDynamicProperty('scrapCollected')} fear ${player.fear}`)
    }


    const inventory = player.getComponent("inventory").container;
    const mainhand = inventory.getItem(player.selectedSlotIndex ?? 0);
    const handsFilled = player.getProperty("js:hands_filled");
    if (mainhand && ITEM_LIST[mainhand.typeId] && handsFilled == false) {
      player.setProperty("js:item", ITEM_LIST[mainhand.typeId].id);
      player.setProperty("js:item_type", ITEM_LIST[mainhand.typeId].item_type);
    } else if (!mainhand && handsFilled == false) {
      player.setProperty("js:item", 0);
      player.setProperty("js:item_type", 0);
    }

    if (interval % 20 == 0 && mainhand && mainhand.typeId == "js:robot_toy") {
      const range = SOUNDS["robot_toy"].range;
      const volume = SOUNDS["robot_toy"].volume;
      broadcastNoise("robot_toy", player.location, range, volume);
    }

    if (mainhand && EMOTE_LIST[mainhand.typeId]) {
      player.setProperty("js:emote", EMOTE_LIST[mainhand.typeId].id);
    } else {
      player.setProperty("js:emote", 0);
    }

    if (mainhand && (mainhand.typeId == "js:flashlight" || mainhand.typeId == "js:pro_flashlight")) {
      player.flashlightSlot = player.selectedSlotIndex;
    }
    if (mainhand && mainhand.typeId == "js:boombox") {
      player.boomboxSlot = player.selectedSlotIndex;
    }
    if (mainhand && mainhand.typeId == "js:walkie") {
      player.walkieSlot = player.selectedSlotIndex;
    }

    if (!player.hasTag("spectator")) {
      const blockRaycast = player.getBlockFromViewDirection({ maxDistance: 5 });
      const block = blockRaycast?.block;
      let middleText = "@";
      if (block && world.getDynamicProperty("generationPercentage") == 0) {
        if (block.typeId == "ding:door_buttons_block") {
          middleText = `Toggle Door`;
        }
        if (block.typeId == "ding:employee_of_the_month_marker" || block.typeId == "ding:employee_of_the_month_marker2" || block.typeId == "ding:employee_of_the_month_marker3" || block.typeId == "ding:employee_of_the_month_marker4") {
          middleText = `Obtained Highest Quota in v2\n(5329)`;
        }
        if (block.typeId == "ding:ship_speaker_collision") {
          middleText = "Disable Speaker";
        }
        if (block.typeId == "ding:terminal_block") {
          middleText = `Access Terminal`;
        }
        if (block.typeId == "ding:ship_lever") {
          middleText = "Start Ship";
        }
        if (block.typeId == "ding:teleporter" && world.getDynamicProperty('js:teleporter') == true) {
          const teleporterCooldown = world.getDynamicProperty('js:teleporter_cooldown')
          if (teleporterCooldown > 0) {
            middleText = `Use Teleporter\n[On Cooldown: ${teleporterCooldown}s]`;
          } else {
            middleText = `Use Teleporter`
          }
        }
        if (block.typeId == "ding:inverse_teleporter" && world.getDynamicProperty('js:inverse_teleporter') == true) {
          const inverseTeleporterCooldown = world.getDynamicProperty('js:inverse_teleporter_cooldown')
          if (inverseTeleporterCooldown > 0) {
            middleText = `Use Inverse Teleporter\n[On Cooldown: ${inverseTeleporterCooldown}s]`;
          } else {
            middleText = `Use Inverse Teleporter`
          }
        }
        if (block.typeId == "js:facility_door") {
          middleText = "Use Door";
        }
        if (block.typeId == "js:main_door" || block.typeId == "js:fire_exit_door") {
          if (player.hasTag('inFacility')) {
            middleText = "Exit"
          } else {
            middleText = "Enter"
          }
        }
        if (
          block.typeId == "ding:ship_charger" &&
          mainhand &&
          ITEM_LIST[mainhand.typeId] &&
          ITEM_LIST[mainhand.typeId].battery_life
        ) {
          middleText = "Charge Item";
        }
        if (
          block.typeId == "ding:ship_charger" &&
          !(mainhand && ITEM_LIST[mainhand.typeId] && ITEM_LIST[mainhand.typeId].battery_life)
        ) {
          middleText = "(Requires Battery-Powered Item)";
        }
        if (
          block.typeId == "js:facility_door" &&
          block.permutation.getState("js:locked") == true &&
          ((mainhand && mainhand.typeId !== "js:key") || !mainhand)
        ) {
          middleText = "Locked";
        }
        if (
          block.typeId == "js:facility_door" &&
          block.permutation.getState("js:locked") == true &&
          mainhand &&
          mainhand.typeId == "js:key"
        ) {
          middleText = "Use Key";
        }
        player.uiManager.updateMiddleText(middleText)
      }
      
      if (middleText && !block && world.getDynamicProperty("generationPercentage") == 0) {
        middleText = "@";
        player.uiManager.updateMiddleText(middleText)
      }

      if (loading.getScore(player) > 0 && mainhand && mainhand.typeId == "js:reset") {
        middleText = "HOLD TO RESET MAP";
        player.uiManager.updateMiddleText(middleText)
      }
      
      const itemId = player.getProperty("js:item");
      let toolTipText = "@";
      if (itemId !== 0) {
        if (itemId == 40) {
          const safety = mainhand.getDynamicProperty('safetyOn')
          let itemKeyValue
          for (const itemKey in ITEM_LIST) {
            if (ITEM_LIST.hasOwnProperty(itemKey)) {
              if (ITEM_LIST[itemKey].id === itemId) {
                itemKeyValue = itemKey
                break;
              }
            }
          }
          if (safety == true) {
            toolTipText = ITEM_LIST[itemKeyValue].tool_tip_text1;
          } else {
            toolTipText = ITEM_LIST[itemKeyValue].tool_tip_text2;
          }
        } else {
          for (const itemKey in ITEM_LIST) {
            if (ITEM_LIST.hasOwnProperty(itemKey)) {
              if (ITEM_LIST[itemKey].id === itemId) {
                toolTipText = ITEM_LIST[itemKey].tool_tip_text;
                break;
              }
            }
          }
        }
      }
      player.uiManager.updateTooltipText(toolTipText)
    }

    if (player.hasTag("spectator") && allPlayersAreSpectators == false) {
      spectatorSystem(player);
      if (player.spectatorIndex >= 0) {
        player.runCommand(
          `execute positioned as @a[name="${worldPlayers[player.spectatorIndex].nameTag}",c=1] as @a[name="${player.nameTag
          }"] anchored eyes run camera @s set minecraft:free ease 0.2 in_out_quad pos ~ ~1.5 ~ rot ~ ~`
        );

        //console.warn(`${worldPlayers[player.spectatorIndex].nameTag} ${worldPlayers[player.spectatorIndex].location.x} ${worldPlayers[player.spectatorIndex].location.y - 2}  ${worldPlayers[player.spectatorIndex].location.z} `)
      }
    }
  }

  updateCooldown(worldPlayers);

  interval++;

  if (fired == true) {
    let orbitLocation = {
      x: shipLocation.x + 0.5,
      y: shipLocation.y + 44,
      z: shipLocation.z - 4 + 0.5 - 41,
    };
    let suckLocation = {
      x: shipLocation.x + 0.5,
      y: shipLocation.y + 157,
      z: shipLocation.z - 4 + 0.5 - 41 + 500,
    };
    if (moonId == 0) {
      suckLocation = {
        x: shipLocation.x + 0.5,
        y: shipLocation.y + 187,
        z: shipLocation.z - 4 + 0.5 - 41 + 500,
      };
    }

    const entities = OVERWORLD.getEntities({ location: orbitLocation, maxDistance: 500});
    for (let entity of entities) {
      if (entity.typeId == "js:ship") continue;
      const directionX = suckLocation.x - entity.location.x;
      const directionY = suckLocation.y - entity.location.y;
      const directionZ = suckLocation.z - entity.location.z;

      const magnitude = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ);
      // console.warn(magnitude);
      if (magnitude > 1) {
        const normalizedDirectionX = directionX / magnitude;
        const normalizedDirectionZ = directionZ / magnitude;
      
        let horizontalStrength = 3;
        let verticalStrength = 0.7;
      
        if (directionY < 0) {
          verticalStrength = -0.7;
        }
      
        const horizontalForce = {
          x: normalizedDirectionX * horizontalStrength,
          z: normalizedDirectionZ * horizontalStrength
        };
      
        entity.applyKnockback(horizontalForce, verticalStrength);
      }
      
    }
  }

  // for (let entity of OVERWORLD.getEntities({type: 'js:turret'})) {
    
  //   if (entity.typeId == "js:turret") {
  //     debugRaycast(entity, entity.getViewDirection(), 4)
  //   }
  
  // }

  for (let entity of OVERWORLD.getEntities({ families: ["monster"] })) {
 
    if (entity.typeId == "js:thumper") {
      const thumper = entity;
      if (!thumper.spawned) {
        thumper.spawned = true;
        thumper.speedTimer = 0;
      }
      const currentState = thumper.getProperty("js:state");

      //   thumper.nameTag = `Thumper ${currentState} ${thumper.speedTimer} ${Math.floor(thumper.speedTimer / 40)}`

      if (currentState == 2) {
        const entityPosition = thumper.location;
        const entityViewDirection = thumper.getViewDirection();

        /*
                const impulse = {
                    x: entityViewDirection.x * 0.2, 
                    y: 0.02,               
                    z: entityViewDirection.z * 0.2 
                };
                thumper.applyImpulse(impulse);
                */

        const inFrontPos = {
          x: entityPosition.x + entityViewDirection.x * 2,
          y: entityPosition.y + 0.5,
          z: entityPosition.z + entityViewDirection.z * 2,
        };

        const block = OVERWORLD.getBlock(inFrontPos);
        // world.sendMessage(block.typeId)

        const vel = thumper.getVelocity();
        const isMoving = vel.x != 0 || vel.z != 0;

        const WALLBLOCKS = [
          "ding:concrete_wall_lined",
          "ding:concrete_wall",
          "ding:concrete_brick_wall",
          "ding:concrete_brick_wall2",
          "ding:concrete_floor",
          "ding:sheet_wall",
        ];

        if (isMoving && !WALLBLOCKS.includes(block.typeId)) {
          thumper.speedTimer++;
          thumper.triggerEvent("moving");
        }
        if (isMoving && thumper.speedTimer > 20 && WALLBLOCKS.includes(block.typeId)) {
          thumper.speedTimer = 0;
          thumper.triggerEvent("stunned");
          thumper.playAnimation('animation.thumper.stunned')
          thumper.addEffect("slowness", 0.88 * 20, { showParticles: false, amplifier: 20 });
        }

        if (thumper.speedTimer > 0) {
          const amplifier = Math.floor(thumper.speedTimer / 40);
          thumper.addEffect("speed", 2, { showParticles: false, amplifier: amplifier });
        }
      } else {
        thumper.speedTimer = 0;
      }
    }

    // if (entity.typeId == "js:earth_leviathan") {
    //   // OVERWORLD.spawnParticle("ding:landmine_flames", entity.location);
    // }

    // if (entity.typeId == "js:old_bird") {
    //   const state = entity.getProperty('js:state')
    //   entity.nameTag= `${state}`
    // }

    if (entity.typeId == "js:hoarding_bug") {
      const hoardingBug = entity;
      if (!hoardingBug.spawned) {
        const spawnLocation = hoardingBug.location;
        hoardingBug.spawned = true;
        hoardingBug.guardTimer = 300;
        //    console.warn(`New hoarding bug spawn position at ${spawnLocation.x} ${spawnLocation.y} ${spawnLocation.z}`)
      }
      const entitiesInSight = hoardingBug.getEntitiesFromViewDirection({ maxDistance: 8 });
      const currentState = hoardingBug.getProperty("js:state");
      const looked = hoardingBug.getProperty("js:looked");
      if (entitiesInSight.length > 0 && currentState !== 3) {
        if (entitiesInSight[0].typeId == "minecraft:player") {
          const distance = getDistance(hoardingBug.location, spawnLocation);
          if (distance <= 10) {
            hoardingBug.triggerEvent("return");
            console.warn(`RETURNING TO GO GUARD NOW`);
          }
        }
      }

      if (currentState == 3 && !looked) {
        hoardingBug.guardTimer--;
        if (hoardingBug.guardTimer == 0) {
          hoardingBug.triggerEvent("roaming");
          hoardingBug.guardTimer = 300;
        }
      }
      // hoardingBug.nameTag=`Hoarding Bug ${currentState} ${hoardingBug.guardTimer}`
    }
    if (entity.typeId == "js:jester") {
      
      const jester = entity;
      if (!jester.spawned) {
        jester.spawned = true;
        jester.speedTimer = 0;
      }
      const currentState = jester.getProperty("js:state");
      let amplifier = 0


      if (currentState == 3) {
        jester.speedTimer++
        
        if (jester.speedTimer > 0) {
          amplifier = Math.max(0, Math.min(4, Math.floor(jester.speedTimer / 120)));
          jester.addEffect("speed", 2, { showParticles: false, amplifier: amplifier });
        }
      } else {
        jester.speedTimer = 0;
        jester.removeEffect('speed');
      }

      // entity.nameTag=`State ${currentState} Amp ${amplifier}`
    }

    // if (entity.typeId == "js:forest_keeper") {
    //   let target = undefined
    //   if (entity.target) {
    //     target = entity.target.nameTag
    //   }

    //   entity.nameTag=`State ${entity.getProperty('js:state')} Target ${target}`
    // }



    // if (entity.typeId == "js:nutcracker") {
      // entity.nameTag=`State ${entity.getProperty('js:state')} BUllets ${entity.getProperty('js:bullets')}`

      // if (entity.target) {
      //  console.warn(`${entity.target.nameTag}`)
      // }

      // debugRaycast(entity, entity.getViewDirection(), 4)

    // }


    if (entity.typeId == "js:bracken") {
      if (!entity) {
        if (bracken.spawnLocation) {
          const loc1 = bracken.spawnLocation;
          const loc2 = {
            x: bracken.spawnLocation.x,
            y: bracken.spawnLocation.y + 1,
            z: bracken.spawnLocation.z,
          };
          const fillVol = new BlockVolume(loc1, loc2);
          OVERWORLD.fillBlocks(fillVol, "air", { blockFilter: { includeTypes: [`js:bracken_home_marker`] } });
        }
        return;
      }
      const bracken = entity;
      if (!bracken.spawned) {
        bracken.spawnLocation = bracken.location;
        bracken.spawned = true;
        bracken.retreatTimer = 460;
      }
      const currentState = bracken.getProperty("js:state");
      const looked = bracken.getProperty("js:looked");
      const anger = bracken.getProperty("js:anger");

      const isSaddled = entity.hasComponent("minecraft:is_saddled");
      const isSheared = entity.hasComponent("minecraft:is_sheared");

      if (!isSaddled && currentState == 1 && looked == 1) {
        bracken.triggerEvent("retreating");
      }
      if (currentState == 2 && looked == 0) {
        bracken.retreatTimer--;
        if (bracken.retreatTimer == 0) {
          bracken.triggerEvent("hunting");
          bracken.retreatTimer = 460;
        }
      }
      if (currentState == 2 && looked == 1) {
        const newAnger = Math.min(anger + 1, 500);
        bracken.setProperty("js:anger", newAnger);

        if (!isSaddled && anger == 0) {
          bracken.triggerEvent("retreating");
        }
      }
      if (currentState == 3) {
        const newAnger = Math.max(anger - 1, 0);
        bracken.setProperty("js:anger", newAnger);
        if (!isSaddled && anger == 0) {
          bracken.triggerEvent("retreating");
        }
      }
      if (currentState !== 3 && anger >= 150) {
        const chance = (anger - 150) / (500 - 150); // Normalizes anger to a value between 0 and 1
        if (Math.random() < chance) {
          bracken.triggerEvent("aggro");
        }
      }
      // bracken.nameTag=`Bracken ${currentState} RT ${bracken.retreatTimer} ANG ${anger}`
    }

    if (entity.typeId == "js:eyeless_dog") {
      const dog = entity;
      if (!dog.spawned) {
        const spawnLocation = dog.location;
        dog.spawned = true;
        dog.growl = 0;
        dog.growlCooldown = 4 * 20;
      }

      const currentState = dog.getProperty("js:state");
      const suspicion = dog.getProperty("js:suspicion");
      const isSheared = entity.hasComponent("minecraft:is_sheared");

      if (isSheared == false && currentState < 3 && dog.growl == 1 && dog.growlCooldown == 0) {
        dog.playAnimation('animation.eyeless_dog.suspicious')
        dog.growl = 0;
        dog.growlCooldown = 80;
      }

      if (dog.growlCooldown > 0) {
        dog.growlCooldown = dog.growlCooldown > 0 ? dog.growlCooldown - 1 : 0;
      }

      //dog.nameTag=`Dog ${currentState} Sus ${suspicion} cd ${dog.growlCooldown} growl ${dog.growl}`
    }

    // if (interval == 10 && entity.typeId !== "js:hygrodere") {
    //   openDoors(entity);
    // }
  }

  if (interval >= 20) {
    interval = 0;
  }
});



function changeFear(player, fearAmount, set) {
  if (set == false) {
    player.fear = player.fear + fearAmount;
  } else {
    player.fear = fearAmount;
  }
  if (player.fearCooldown <= 0) {
    if (player.fear > 0.1 && player.fear <= 0.4) {
      //low action
      player.playSound('ambient.low_action')
    } else if (player.fear > 0.4 && player.fear <= 0.67) {
      //high action
      player.playSound('ambient.high_action1')
      player.addEffect("nausea", 5 * 20, { showParticles: false });
    } else if (player.fear > 0.67) {
      //high action2
      player.playSound('ambient.high_action2')
      player.addEffect("nausea", 10 * 20, { showParticles: false });
    }
  }

  if (set == false) {
    player.fearCooldown = 1 * 20;
  } else {
    player.fearCooldown = 15 * 20;
  }

  if (player.fear > 1) {
    player.fear = 1;
  }
}


let deliveryTimer = 0;
let actualTimePassed = 0;
let shipLeavingSoonTriggered = false;

system.runInterval(() => {
  let currentTime = world.getDynamicProperty("time");
  // console.warn(`${currentTime}`)

  const moonId = world.getDynamicProperty("moon");
  const inOrbit = world.getDynamicProperty("inOrbit");
  const deliveryShipQueue = world.getDynamicProperty("deliveryShipQueue");

  const shipLeavingSoonTime = 860;

  if (moonId !== 0 && currentTime >= 4 && currentTime <= 5) {
    entitySpawns(currentTime);
  }

  if (inOrbit == false) {
    actualTimePassed += 4.5;
  }

  if (moonId !== 0 && actualTimePassed >= 120) {
    entitySpawns(currentTime);
    actualTimePassed = 0;
  }

  if (currentTime > 720) {
    const daytimeEntities = OVERWORLD.getEntities({ families: ["daytime"] });

    for (let entity of daytimeEntities) {
      if (entity.typeId !== "js:circuit_bees") {
        entity.remove();
      }
    }
  }

  if (inOrbit == false) {
    if (moonId == 0) {
      if (deliveryShipQueue == 1 && deliveryTimer <= 25) {
        deliveryTimer = deliveryTimer + 5;
      }
    } else {
      if (deliveryShipQueue == 1 && currentTime > 25 && deliveryTimer <= 25) {
        deliveryTimer = deliveryTimer + 5;
      }
    }
  }
  if (
    deliveryTimer > 0 &&
    deliveryTimer % 30 == 0 &&
    deliveryShipQueue == 1 &&
    OVERWORLD.getEntities({ type: "js:dropship" }).length <= 0
  ) {
    let deliveryShipLocation;
    for (const key of Object.keys(MAPS)) {
      if (MAPS[key].id === moonId) {
        deliveryShipLocation = MAPS[key].deliveryShipLocation;
        break;
      }
    }
    const dropship = OVERWORLD.spawnEntity("js:dropship", deliveryShipLocation);

    const storedDeliveryShipContentsString = world.getDynamicProperty("deliveryShip");
    let deliveryShipContents = JSON.parse(storedDeliveryShipContentsString);

    dropship.setDynamicProperty("deliveryShipContents", storedDeliveryShipContentsString);

    deliveryShipContents = [];
    const deliveryShipContentsString = JSON.stringify(deliveryShipContents);
    world.setDynamicProperty("deliveryShip", deliveryShipContentsString);

    world.setDynamicProperty("deliveryShipQueue", 0);
    deliveryTimer = 0;
  }
  // console.warn(`${currentTime} ${deliveryTimer} ${deliveryShipQueue}`)

  if (currentTime >= shipLeavingSoonTime - 2 && currentTime <= shipLeavingSoonTime + 2 && !shipLeavingSoonTriggered) {
    //   world.sendMessage(`§b> WARNING!!! The autopilot ship will leave at midnight. Please return quickly.`)

    worldPlayers.forEach((player) => {
      player.sendMessage(`dingUI:pilot_____The autopilot ship will leave at midnight. Please return quickly.`)
      player.playSound('misc.beep')
    });
    shipLeavingSoonTriggered = true;
  }

  if (currentTime > 980) {
    shipLeavingSoonTriggered = false;
  }

  if (currentTime <= 979 && moonId !== 0 && inOrbit == false) {
    const addAmount = Math.random() < 0.5 ? 4 : 5;

    // const newTime = currentTime;
    const newTime = currentTime + addAmount;
    world.setDynamicProperty("time", newTime);

    let ticks
    if (moonId >= 7 && moonId <= 9) {
      //Snowy Moons
      ticks = convertToTicks2(newTime);
    } else { 
      ticks = convertToTicks(newTime);
    }

    
    world.setTimeOfDay(ticks);
  }
  if (currentTime > 979 && currentTime < 985 && (!shipEntity.shipLeaving)) {
    shipEntity.runCommand(`scriptevent js:shipLeave 1 midnight`);
    world.setDynamicProperty("time", 985);
    currentTime = world.getDynamicProperty("time");
    shipEntity.shipLeaving = true
  }

  // console.warn(world.getDynamicProperty('time'))

  const timeDisplay = display12HourTime(world.getDynamicProperty("time"));
  for (let player of worldPlayers) {
    player.uiManager.updateTimeDisplay(timeDisplay)
  }
}, 60);



const minBarberChargeTime = 1.25;
const decrementPerHour = 0.1;

system.runInterval(() => {
  const inOrbit = world.getDynamicProperty("inOrbit");
  if (inOrbit == false) {
    let barberChargeTimer = world.getDynamicProperty("barberChargeTimer") || 2.75;
    const currentTime = world.getDynamicProperty("time");

    if (currentTime % 60 == 0) {
      const hoursPassed = Math.floor(currentTime / 60);
      barberChargeTimer = Math.max(minBarberChargeTime, 2.75 - hoursPassed * decrementPerHour);
      world.setDynamicProperty("barberChargeTimer", barberChargeTimer);
      //    console.warn(`Barber Charge Timer updated: ${barberChargeTimer} seconds`);
    }
  }
}, 60);

export function moveBarber() {
  const inOrbit = world.getDynamicProperty("inOrbit");
  if (inOrbit == false) {
    const barbers = OVERWORLD.getEntities({ type: "js:barber" });
    let barberChargeTimer = world.getDynamicProperty("barberChargeTimer") || 2.75;

    system.runTimeout(() => {
      for (let barber of barbers) {
        if (!barber.target) {
          const position = barber.location;

          const directions = {
            north: OVERWORLD.getBlock({ x: position.x, y: position.y, z: position.z - 1 }),
            south: OVERWORLD.getBlock({ x: position.x, y: position.y, z: position.z + 1 }),
            east: OVERWORLD.getBlock({ x: position.x + 1, y: position.y, z: position.z }),
            west: OVERWORLD.getBlock({ x: position.x - 1, y: position.y, z: position.z }),
          };

          function isBlocked(block) {
            const isAir = block.typeId === "minecraft:air";
            const hasKaiDoublePermutation = block.permutation.getState("kai:double") !== undefined;
            return !isAir && !hasKaiDoublePermutation;
          }

          const blocked = {
            north: isBlocked(directions.north),
            south: isBlocked(directions.south),
            east: isBlocked(directions.east),
            west: isBlocked(directions.west),
          };

          //  console.warn(JSON.stringify(blocked))

          const rand = Math.random();
          let randRot;

          let chance;
          if (blocked.north || blocked.south || blocked.east || blocked.west) {
            chance = 1;
          } else {
            chance = 0.65;
          }

          if (rand < chance) {
            if (blocked.north && !blocked.south) {
              randRot = 0 + Math.random() * 45;
            } else if (blocked.south && !blocked.north) {
              randRot = 180 + Math.random() * 45;
            } else if (blocked.east && !blocked.west) {
              randRot = 90 + Math.random() * 45;
            } else if (blocked.west && !blocked.east) {
              randRot = 270 + Math.random() * 45;
            } else {
              randRot = Math.random() * 360;
            }
  
            barber.setRotation({ x: 0, y: randRot });
          }
        }
        const direction = barber.getViewDirection();
        const impulse = {
          x: direction.x * 0.8,
          y: 0.08,
          z: direction.z * 0.8,
        };
        barber.applyImpulse(impulse);
        barber.setProperty("js:state", barber.getProperty("js:state") === 1 ? 2 : 1);

        system.runTimeout(() => {
          barber.runCommand(`stopsound @a[r=10] monster.barber.snare`);
          barber.clearVelocity();

          system.runTimeout(() => {
            OVERWORLD.playSound("monster.barber.snare", barber.location, SOUND_OPTIONS);
          }, barberChargeTimer * 10);
        }, 6);
      }

      system.run(moveBarber);
    }, barberChargeTimer * 20);
  }
}

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




const DEATH_ENTITIES = [
  "js:bunker_spider", "js:eyeless_dog", "js:thumper", "js:hoarding_bug", "js:snare_flea", "js:nutcracker", "js:forest_keeper"
]
world.afterEvents.entityDie.subscribe((ev) => {
  const entity = ev.deadEntity;

  if (entity.typeId == "js:tree" || entity.typeId == "js:alive_tree") {
    const pos1 = {
      x: entity.location.x,
      y: entity.location.y + 1,
      z: entity.location.z
    }
    const pos2 = {
      x: entity.location.x,
      y: entity.location.y + 15,
      z: entity.location.z
    }
    const fillArea = new BlockVolume(pos1, pos2)
    entity.dimension.fillBlocks(fillArea, 'minecraft:air', {blockFilter: {includeTypes: ["ding:tree_collision"]}})
  }


  if (Object.keys(ENTITIES).includes(entity.typeId)) {
    const moonId = world.getDynamicProperty("moon");
    let mapName;
    for (const key of Object.keys(MAPS)) {
      if (MAPS[key].id === moonId) {
        mapName = key;
        break;
      }
    }
    const type = ENTITIES[entity.typeId].type;
    const powerLevel = ENTITIES[entity.typeId].power_level;
    if (powerLevel) {
      if (type == "indoor") {
        world.setDynamicProperty("currentPowerLevel", world.getDynamicProperty("currentPowerLevel") - powerLevel);
      } else {
        world.setDynamicProperty(
          "currentOutdoorPowerLevel",
          world.getDynamicProperty("currentOutdoorPowerLevel") - powerLevel
        );
      }
    }

    if (entity.typeId == "js:bracken") {
      if (entity.spawnLocation) {
        const loc1 = entity.spawnLocation;
        const loc2 = {
          x: entity.spawnLocation.x,
          y: entity.spawnLocation.y + 1,
          z: entity.spawnLocation.z,
        };
        const fillVol = new BlockVolume(loc1, loc2);
        OVERWORLD.fillBlocks(fillVol, "air", { blockFilter: { includeTypes: [`js:bracken_home_marker`] } });
      }
      entity.triggerEvent('death')
    }
    if (DEATH_ENTITIES.includes(entity.typeId)) {
      entity.triggerEvent('death')
    }
  }
});

function getFrontLocation(entity) {
    const { x, y, z } = entity.location;
    const rot = entity.getRotation().y; // yaw
    
    // Normalize yaw to (-180, 180]
    let yaw = ((rot % 360) + 360) % 360;
    if (yaw > 180) yaw -= 360;

    // Round yaw to nearest cardinal direction
    let dx = 0, dz = 0;
    if (yaw >= -45 && yaw < 45) {
        // South
        dz = 1;
    } else if (yaw >= 45 && yaw < 135) {
        // West
        dx = -1;
    } else if (yaw >= -135 && yaw < -45) {
        // East
        dx = 1;
    } else {
        // North (yaw between 135–180 or -180–-135)
        dz = -1;
    }

    return {
        x: x + dx,
        y: y,
        z: z + dz
    };
}


system.afterEvents.scriptEventReceive.subscribe((ev) => {
  const entity = ev.sourceEntity;
  const block = ev.sourceBlock;
  const message = ev.id;

  if (message == "js:spike_trap_spawn") {
    const modeRoll = Math.random() * 100;
    if (modeRoll < 80) {
      entity.setProperty("js:mode", 0);

      // roll timer chance
      const roll = Math.random() * 100;
      let min, max;

      if (roll < 81) {
        min = 0.8; max = 10.8;
      } else if (roll < 91) {
        min = 0.8; max = 26.2;
      } else {
        min = 0.8; max = 2.1;
      }

      const seconds = min + Math.random() * (max - min);
      const ticks = Math.floor(seconds * 20);

      entity.setProperty("js:timer", ticks);
      console.warn(`Spawned mode 0, timer=${ticks}`);
    } else {
      entity.setProperty("js:mode", 1);
    }

    // === Direction selection based on surroundings ===
    const pos = entity.location;
    const dirOffsets = {
      north: { x: 0, z: -1 },
      south: { x: 0, z: 1 },
      west: { x: -1, z: 0 },
      east: { x: 1, z: 0 }
    };

    const counts = {};
    for (const [dir, offset] of Object.entries(dirOffsets)) {
      let solidCount = 0;
      for (let i = 1; i <= 2; i++) {
        const bx = Math.floor(pos.x) + offset.x * i;
        const by = Math.floor(pos.y);
        const bz = Math.floor(pos.z) + offset.z * i;
        const block = entity.dimension.getBlock({ x: bx, y: by, z: bz });
        if (block) {
          const id = block.typeId ?? "";
          if (id !== "ding:water_puddle" && !id.includes("tiny_pipe")) {
            // count as solid if block isn't air
            if (id !== "minecraft:air") {
              solidCount++;
            }
          }
        }
      }
      counts[dir] = solidCount;
    }

    // pick direction with fewest solids
    const minCount = Math.min(...Object.values(counts));
    let candidates = Object.keys(counts).filter(dir => counts[dir] === minCount);

    if (candidates.length === 0) {
      // all blocked, fallback random
      candidates = Object.keys(dirOffsets);
    }
    const dirToYaw = {
        south: 0,
        west: 90,
        north: 180,
        east: -90
    };

    const chosenDir = candidates[Math.floor(Math.random() * candidates.length)];
    entity.setRotation({ x: 0, y: dirToYaw[chosenDir] });

    console.warn(`Spike trap facing chosen: ${chosenDir} (solid counts: ${JSON.stringify(counts)})`);
  }



  if (message == "js:spike_trap_slam") {
    
    const frontLoc = getFrontLocation(entity)
    const inRadius = entity.dimension.getEntities({location: frontLoc, maxDistance: 3.5, excludeFamilies: ["inanimate","spike_trap", "turret", "scrap"]})

    let hasMonster = false
    let hasPlayer = false
    for (let victim of inRadius) {
      const family = victim.getComponent('minecraft:type_family')
      if (family.hasTypeFamily("monster")) {
        hasMonster = true
      }
      if (victim.typeId == "minecraft:player") {
        hasPlayer = true
      }
    }
    if (hasPlayer == true || hasMonster == false) {
      entity.playAnimation('animation.spike_trap.crush')
      system.runTimeout(() => {
        for (let victim of inRadius) {
          const distance = getDistance(victim.location, frontLoc)
          if (distance <= 2) {
            if (victim.typeId == "minecraft:player" && victim.getGameMode() !== "Spectator") {
              damagePlayer(victim, 100, entity, EntityDamageCause.anvil)
            } else {
              victim.applyDamage(1000, {
                cause: EntityDamageCause.entityAttack,
                damagingEntity: entity,
              });
            }
          }
        }
      }, 3)
    }

  }

  if (message == "js:spike_trap_player_cross") {
    if (entity.cooldown === true) {
      return;
    }

    // get entity block position (rounded to nearest block center)
    const ex = Math.floor(entity.location.x + 0.5);
    const ey = Math.floor(entity.location.y + 0.5);
    const ez = Math.floor(entity.location.z + 0.5);

    // determine facing (dx, dz) snapped to cardinal directions
    const rot = entity.getRotation().y;
    let yaw = ((rot % 360) + 360) % 360;
    if (yaw > 180) yaw -= 360;

    let dx = 0, dz = 0;
    if (yaw >= -45 && yaw < 45) {
      dz = 1; // South
    } else if (yaw >= 45 && yaw < 135) {
      dx = -1; // West
    } else if (yaw >= -135 && yaw < -45) {
      dx = 1; // East
    } else {
      dz = -1; // North
    }

    // three blocks: entity block, one in front, two in front
    const positions = [
      { x: ex, y: ey, z: ez },
      { x: ex + dx, y: ey, z: ez + dz }, // <-- "in front 1"
      { x: ex + dx * 2, y: ey, z: ez + dz * 2 }
    ];

    // get nearby players
    const nearbyPlayers = entity.dimension.getEntities({
      location: entity.location,
      maxDistance: 4,
      type: "minecraft:player"
    });

    let playerCrossed = false;
    for (const player of nearbyPlayers) {
      const px = Math.floor(player.location.x + 0.5);
      const py = Math.floor(player.location.y + 0.5);
      const pz = Math.floor(player.location.z + 0.5);

      for (const pos of positions) {
        if (px === pos.x && py === pos.y && pz === pos.z) {
          playerCrossed = true;
          break;
        }
      }
      if (playerCrossed) break;
    }

    if (playerCrossed) {
      entity.cooldown = true;
      entity.playAnimation("animation.spike_trap.crush");

      // delay a little so animation syncs
      system.runTimeout(() => {
        const frontLoc = positions[1]; // the block in front of the trap
        const victims = entity.dimension.getEntities({
          location: frontLoc,
          maxDistance: 2,
          excludeFamilies: ["inanimate","spike_trap", "turret", "scrap"]
        });

        for (const victim of victims) {
          console.warn(victim.typeId)
          if (victim.typeId == "minecraft:player" && victim.getGameMode() !== "Spectator") {
            damagePlayer(victim, 100, entity, EntityDamageCause.anvil);
          } else {
            victim.applyDamage(1000, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: entity,
            });
          }
        }
      }, 3);

      // reset cooldown after ~1.25s (25 ticks)
      system.runTimeout(() => {
        entity.cooldown = false;
      }, 25);
    }
  }


  
  if (message == "js:water") {
    const water = entity.dimension.spawnEntity('ding:water', entity.location)
    water.nameTag=entity.nameTag
    water.setDynamicProperty('owner',entity.nameTag)
    water.setProperty('ding:light', 15)

    const moonId = world.getDynamicProperty("moon");

    let mapName;
    for (const key of Object.keys(MAPS)) {
      if (MAPS[key].id === moonId) {
        mapName = key;
        break;
      }
    }

    water.setProperty('ding:y_level', MAPS[mapName].floodMinHeight)

    system.runTimeout(()=>{
      water.playAnimation('animation.water_scale',{controller:'water',blendOutTime:999999,players:[entity]})
    },2)
  }

  if (message == "js:player_initialize") {
    updateShipVol();
    updateProfitDisplay();

    system.runTimeout(() => {
      if (!entity.hasTag("intro_done")) {
        entity.runCommand(`scriptevent ding:intro`);
      } else {
        entity.runCommand(`function hudOverlays/systemsOnline`);
      }
    }, 1 * 20)

  }

  if (message == "js:jetpack_initialize") {
    console.warn(`init`)
    const player = entity
    const inventory = player.getComponent("inventory").container
    const item = inventory.getItem(player.selectedSlotIndex)
    const handsFilled = player.getProperty("js:hands_filled");
    player.fallDistance = 0

    if (item.getDynamicProperty("battery") == undefined) {
      let batteryLife = ITEM_LIST["js:jetpack"].battery_life;

      item.setDynamicProperty("battery", batteryLife);
      inventory.setItem(player.selectedSlotIndex, item);

        console.warn(`New Battery ${item.getDynamicProperty('battery')} initialised`)
    }
    
    if (item.getDynamicProperty("jetpackBroken") == undefined) {
      item.setDynamicProperty("jetpackBroken", false);
      inventory.setItem(player.selectedSlotIndex, item);
    }

    if (handsFilled == false && item.getDynamicProperty("battery") > 0 && item.getDynamicProperty('jetpackBroken') == false) {
      OVERWORLD.playSound('items.jetpack.thrust_start', player.location, SOUND_OPTIONS)
      player.jetpackOverheat = 0
      player.jetpackTime = 0
      player.jetPackSlot = player.selectedSlotIndex
      player.playSound('items.jetpack.warning_beep', SOUND_OPTIONS)
      player.playSound('items.jetpack.thrust', SOUND_OPTIONS)
      player.beeped = false

      player.currentEnergy = item.getDynamicProperty('battery')
      console.warn(player.currentEnergy)
    }
    if (item.getDynamicProperty('jetpackBroken') == true) {
      OVERWORLD.playSound('items.jetpack.broken', player.location, SOUND_OPTIONS)
    }
  }

  if (message == 'js:jetpack_stop') {
    console.warn(`stop`)
    const player = entity
    const inventory = player.getComponent("inventory").container
    const item = inventory.getItem(player.jetPackSlot)
    const handsFilled = player.getProperty("js:hands_filled");

    player.setProperty('js:jetpack_fly', false)
    player.runCommand(`stopsound @s items.jetpack.warning_beep`)
    player.runCommand(`stopsound @s items.jetpack.warning_beep2`)
    player.runCommand(`stopsound @s items.jetpack.thrust`)

    if (player.currentEnergy) {
      item.setDynamicProperty("battery", player.currentEnergy);
      inventory.setItem(player.jetPackSlot, item);
      console.warn(player.currentEnergy)
    }

  }

  if (message == "js:jetpack") {
    console.warn(`runtime`)
    const player = entity
    const inventory = player.getComponent("inventory").container
    const item = inventory.getItem(player.selectedSlotIndex)
    const handsFilled = player.getProperty("js:hands_filled");

    if (handsFilled == false && item.getDynamicProperty("battery") > 0 && item.getDynamicProperty('jetpackBroken') == false) {
        player.setProperty('js:jetpack_fly', true)
        let unclampedEnergyLevel = Math.ceil(
          (player.currentEnergy / ITEM_LIST[item.typeId].battery_life) * 100
        );
        player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
        player.setProperty("js:energy", player.energyLevel);

        player.currentEnergy = player.currentEnergy > 0 ? player.currentEnergy - 1 : 0;

        unclampedEnergyLevel = Math.ceil((player.currentEnergy / ITEM_LIST[item.typeId].battery_life) * 100);
        player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
        player.setProperty("js:energy", player.energyLevel);

        if (unclampedEnergyLevel <= 15 && player.beep == false) {
          player.playSound('items.jetpack.warning_beep2', SOUND_OPTIONS)
          player.beep = true
        }

        player.jetpackTime++

        let overheatIncrement = 0.001 * player.jetpackTime ** 2

        player.jetpackOverheat += overheatIncrement

        player.runCommand(`fill ~ ~ ~ ~ ~1 ~ js:light_block2 replace air`)
        const velocity = player.getVelocity();
        const velX = velocity.x;
        const velZ = velocity.z;
        const velocityXZ = Math.sqrt(velX ** 2 + velZ ** 2);
        
        // const velY = Math.max(player.getVelocity().y, 0);
        
        const moveY = 0.000045 * player.jetpackTime ** 2;
        const moveXZ = 0.00008 * player.jetpackTime ** 2;
        
        console.warn(moveY, moveXZ);
        
        let forceX = velX * (0.85 + velocityXZ + moveXZ);
        let forceZ = velZ * (0.85 + velocityXZ + moveXZ);
        
        // Clamp horizontal force to max length of 2
        const forceLength = Math.sqrt(forceX ** 2 + forceZ ** 2);
        const maxLength = 1;
        
        if (forceLength > maxLength) {
          const scale = maxLength / forceLength;
          forceX *= scale;
          forceZ *= scale;
        }
        
        const horizontalForce = {
          x: forceX,
          z: forceZ
        };
        
        player.applyKnockback(horizontalForce, 0.2 + moveY);
        


        const speed = Math.sqrt(velocity.x ** 2 + velocity.y ** 2 + velocity.z ** 2)
        const blockAbove = player.dimension.getBlock({ x: player.location.x, y: player.getHeadLocation().y + 0.5, z: player.location.z}) 
        
        const blockSide1 = player.dimension.getBlock({ x: player.location.x + 0.5, y: player.location.y + 0.5, z: player.location.z})
        const blockSide2 = player.dimension.getBlock({ x: player.location.x - 0.5, y: player.location.y + 0.5, z: player.location.z})
        const blockSide3 = player.dimension.getBlock({ x: player.location.x, y: player.location.y + 0.5, z: player.location.z + 0.5})
        const blockSide4 = player.dimension.getBlock({ x: player.location.x, y: player.location.y + 0.5, z: player.location.z - 0.5})

        const NOT_KILL_BLOCKS = ["minecraft:air", "js:light_block2"]
        const inOrbit = world.getDynamicProperty('inOrbit')
        if (inOrbit == false && (player.jetpackOverheat >= 200 || ((!NOT_KILL_BLOCKS.includes(blockAbove.typeId) || !NOT_KILL_BLOCKS.includes(blockSide1.typeId) || !NOT_KILL_BLOCKS.includes(blockSide2.typeId) || !NOT_KILL_BLOCKS.includes(blockSide3.typeId) || !NOT_KILL_BLOCKS.includes(blockSide4.typeId)) && speed > 1.5))) {
          let dimension = player.dimension
          item.setDynamicProperty('jetpackBroken', true)

          inventory.setItem(player.selectedSlotIndex, item)
          inventory.setItem(player.jetPackSlot, item)

          console.warn(item.getDynamicProperty('jetpackBroken'))

          dimension.playSound("misc.mine.detonate", player.location, SOUND_OPTIONS);
          dimension.spawnParticle("ding:landmine_impact", player.location);
          dimension.spawnParticle("ding:landmine_flames", player.location);

          const dead = dimension.getEntities({ location: player.location, type: "minecraft:player", maxDistance: 5 });
          const injured = dimension.getEntities({
            location: player.location,
            type: "minecraft:player",
            minDistance: 5,
            maxDistance: 9.1,
          });

          for (let player of dead) {
            damagePlayer(player, 100, null, EntityDamageCause.blockExplosion);
          }
          for (let player of injured) {
            damagePlayer(player, 50, null, EntityDamageCause.blockExplosion);
          }

          const entityKill = dimension.getEntities({ location: player.location, families: ["monster"], maxDistance: 5 });

          for (let entity of entityKill) {
            if (entity.typeId == "js:hygrodere" || entity.typeId == "js:coil_head") continue;

            entity.applyDamage(50, {
              cause: EntityDamageCause.blockExplosion,
            });
          }

          player.jetpackOverheat = 0
          player.jetpackTime = 0

        }

        // player.onScreenDisplay.setActionBar(`
        //   Time ${player.jetpackTime}
        //   Overheat ${player.jetpackOverheat}
        //   PE ${player.currentEnergy}
        //   IE ${item.getDynamicProperty('battery')}
        //   Vel ${Math.sqrt(velocity.x ** 2 + velocity.y ** 2 + velocity.z ** 2)}
        // `)

    } 
  }


  if (message == "js:spore_lizard_smoke") {
    entity.playAnimation('animation.spore_lizard.smoke')
    entity.dimension.spawnParticle('js:spore_lizard_fart', entity.location)
    entity.addEffect('slowness', 1.25 * 20, { amplifier: 255, showParticles: false })
  }

  if (message == "js:bunkerSpiderModel") {
    const arachnophobiaMode = world.getDynamicProperty('arachnophobiaMode') 
    if (arachnophobiaMode == true) {
      entity.setProperty('js:type', 1)
    } else {
      entity.setProperty('js:type', 0)
    }
  }

  if (message == "js:fog") {
    const command = ev.message;
    const player = entity;

    player.runCommand(`fog @s remove facility`);
    player.runCommand(`fog @s remove mansion`);
    player.runCommand(`fog @s remove company`);
    player.runCommand(`fog @s remove experimentation`);
    player.runCommand(`fog @s remove experimentation_foggy`);
    player.runCommand(`fog @s remove assurance`);
    player.runCommand(`fog @s remove assurance_foggy`);
    player.runCommand(`fog @s remove march`);
    player.runCommand(`fog @s remove march_foggy`);
    player.runCommand(`fog @s remove rend`);
    player.runCommand(`fog @s remove rend_landing`);
    player.runCommand(`fog @s remove titan`);
    player.runCommand(`fog @s remove titan_landing`);
    player.runCommand(`fog @s remove titan_foggy`);
    player.runCommand(`fog @s remove artifice`);
    player.runCommand(`fog @s remove embrion`);
    player.runCommand(`fog @s remove embrion_foggy`);
    player.runCommand(`fog @s remove flood`);
    
    const moonId = world.getDynamicProperty("moon");
    const inOrbit = world.getDynamicProperty("inOrbit");
    const weather = world.getDynamicProperty("currentWeather");
    let vv = ""
    if (player.graphicsMode == "Deferred") {
      vv = "_vv"
    }

    if (command == "rend_landing") {
      player.runCommand(`fog @s push js:rend_landing${vv} rend_landing`);
      system.runTimeout(() => {
        player.runCommand(`fog @s remove rend_landing`);
        player.runCommand(`fog @s push js:rend${vv} rend`);
      }, 11 * 20)
    }
    if (command == "rend_taking_off") {
      player.runCommand(`fog @s remove rend`);
      player.runCommand(`fog @s push js:rend_landing${vv} rend_landing`);
    } 
    
    if (command == "titan_landing") {
      player.runCommand(`fog @s push js:titan_landing${vv} titan_landing`);
      system.runTimeout(() => {
        player.runCommand(`fog @s remove titan_landing`);
        if (weather == 1) {
          player.runCommand(`fog @s push js:titan_foggy${vv} titan_foggy`);
        } else {
          player.runCommand(`fog @s push js:titan${vv} titan`);
        }
      }, 11 * 20)
    }
    if (command == "titan_taking_off") {
      player.runCommand(`fog @s push js:titan_landing${vv} titan_landing`);
      player.runCommand(`fog @s remove titan`);
      player.runCommand(`fog @s remove titan_foggy`);
    }

    if (command == "outside") {
      if (inOrbit == false) {
        if (moonId == 0) {
          player.runCommand(`fog @s push js:company${vv} company`);
        }
        if (moonId == 1) {
          if (weather == 1) {
            player.runCommand(`fog @s push js:experimentation_foggy${vv} experimentation_foggy`);
          } else {
            player.runCommand(`fog @s push js:experimentation${vv} experimentation`);
            console.warn(`fog @s push js:experimentation${vv} experimentation`)
          }
        }
        if (moonId == 2) {
          if (weather == 1) {
            player.runCommand(`fog @s push js:assurance_foggy${vv} assurance_foggy`);
          } else {
            player.runCommand(`fog @s push js:assurance${vv} assurance`);
          }
        }
        if (moonId == 5) {
          if (weather == 1) {
            player.runCommand(`fog @s push js:march_foggy${vv} march_foggy`);
          } else {
            player.runCommand(`fog @s push js:march${vv} march`);
          }
        }
        if (moonId == 7) {
          player.runCommand(`fog @s push js:rend${vv} rend`);
        }
        if (moonId == 9) {
          if (weather == 1) {
            player.runCommand(`fog @s push js:titan_foggy${vv} titan_foggy`);
          } else {
            player.runCommand(`fog @s push js:titan${vv} titan`);
          }
        }
        if (moonId == 10) {
          if (weather == 1) {
            player.runCommand(`fog @s push js:embrion_foggy${vv} embrion_foggy`);
          } else {
            player.runCommand(`fog @s push js:embrion${vv} embrion`);
          }
        }
        if (moonId == 11) {
          player.runCommand(`fog @s push js:artifice${vv} artifice`);
        }
      }
    }

    if (command == "facility") {
      if (player.hasTag("inFacility")) {
        if (world.getDynamicProperty('facilityType') == "facility") {
          player.runCommand(`fog @s push js:facility${vv} facility`);
        } else {
          player.runCommand(`fog @s push js:mansion${vv} mansion`);
        }
      }
    }
  }

  if (message == "js:credits") {
    credits1(entity);
  }

  if (message == "ding:intro") {

    const moonId = world.getDynamicProperty("moon");
    let shipLocation = { x: 0, y: -60, z: 0 };
    for (const key of Object.keys(MAPS)) {
      if (MAPS[key].id === moonId) {
        shipLocation = MAPS[key].shipLocation;
        break;
      }
    }
    const tpLocation = {
      x: shipLocation.x + 2 + 0.5,
      y: shipLocation.y + 47,
      z: shipLocation.z - 41.5 + 0.5,
    };

    entity.teleport(tpLocation);

    entity.inputPermissions.movementEnabled = false
    entity.inputPermissions.cameraEnabled = false

    entity.addTag("intro_done");

    const randomChance = Math.random() * 100;

    if (randomChance >= 5) {
      entity.sendMessage(`dingUI:intro_____`)
  }
    if (randomChance < 5) {
        entity.sendMessage(`dingUI:intro2____`)
    }

    system.runTimeout(() => {
      entity.playSound('game.profit.days_left')
    }, 2 * 20);

    system.runTimeout(() => {
      entity.playSound('julius_intro')
    }, 6 * 20);

    system.runTimeout(() => {
      entity.playSound('dinger_intro')
    }, 11 * 20);

    system.runTimeout(() => {
      entity.playSound('yay_intro')
    }, 16 * 20);

    system.runTimeout(() => {
      entity.playSound('bootup')
    }, 20 * 20);

    system.runTimeout(() => {
      if (entity.clientSystemInfo.platformType == "Desktop") {
        entity.runCommand(`scriptevent js:ui 4`);
      } else {
        entity.runCommand(`scriptevent js:ui 3`);
      }
    }, 23.5 * 20);
  }

  if (message == "js:entitySpawns") {
    const num = ev.message;
    entitySpawns(num);
  }
  if (message == "js:b") {
    world.setDynamicProperty("currentPowerLevel", 0);
  }
  if (message == "js:d") {
    system.runTimeout(() => {
      entity.runCommand(`event entity @e[type=js:old_bird] activate`);
    }, 5 * 20);
  }

  if (message == "js:circuit_bee_damage") {
    const players = OVERWORLD.getEntities({ location: entity.location, maxDistance: 2, type: "minecraft:player" });
    for (let player of players) {
      damagePlayer(player, 10, entity, EntityDamageCause.lightning);
    }
    OVERWORLD.playSound("monster.circuit_bees.zap", entity.location, SOUND_OPTIONS);
  }

  if (message == "js:turret") {
    const command = ev.message;
    if (command == "initializeShoot") {
      const entityViewDirection = entity.getViewDirection();
      const entityRotation = entity.getRotation();

      entity.savedViewDirection = entityViewDirection
      entity.savedRotation = entityRotation

    }

    if (command == "shoot") {
      entity.runCommand(`fill ~~~ ~~~ js:light_block2 replace air`);
      const entityPosition = entity.getHeadLocation();
      const entityViewDirection = entity.savedViewDirection
      const entityRotation = entity.savedRotation

      const spawnPosition = {
        x: entityPosition.x + entityViewDirection.x * 1,
        y: entityPosition.y + entityViewDirection.y * 1,
        z: entityPosition.z + entityViewDirection.z * 1,
      };

      const bullet = OVERWORLD.spawnEntity("js:bullet", spawnPosition);
      bullet.setRotation({ x: entityRotation.x, y: entityRotation.y });
      const projectile = bullet.getComponent("minecraft:projectile");
      const magnitudeMultiplier = 4;
      const scaledDirection = {
        x: entityViewDirection.x * magnitudeMultiplier,
        y: entityViewDirection.y * magnitudeMultiplier,
        z: entityViewDirection.z * magnitudeMultiplier,
      };
      projectile.shoot(scaledDirection, { uncertainty: 14 });
    }

    if (command == "hit") {
      OVERWORLD.playSound("misc.turret.hit", entity.location, SOUND_OPTIONS);
      entity.remove();
    }
  }

  if (message == "js:barber") {
    const barber = entity
    if (!barber.spawn) {
      barber.spawn = barber.location;
    }
    const block = OVERWORLD.getBlock({ x: barber.location.x, y: barber.location.y - 0.5, z: barber.location.z });
    if (block && DEATH_BLOCKS[block.typeId]) {
      barber.teleport(barber.spawn);
    }

    if (entity.cooldown > 0) {
      entity.cooldown--;
    }
    const entityViewDirection = entity.getViewDirection();
    const entityPosition = entity.location; // Assuming this is provided

    const boxLength = 2;

    const boxCenter = {
      x: entityPosition.x + entityViewDirection.x * (boxLength / 2),
      y: entityPosition.y + 1,
      z: entityPosition.z + entityViewDirection.z * (boxLength / 2),
    };

    const players = OVERWORLD.getEntities({
      location: boxCenter,
      maxDistance: 1.7,
      type: "minecraft:player",
      excludeTags: ["spectator"],
    });
    if (players.length > 0 && entity.cooldown == 0) {
      entity.playAnimation("animation.barber.attack");
      entity.cooldown = 20;
    }

    for (let player of players) {
      damagePlayer(player, 100, entity, EntityDamageCause.soulCampfire);
    }
  }

  if (message == "js:jester") {
    const command = ev.message;

    if (command == "startRoamingCountdown") {
      if (worldPlayers.length <= 1) {
        entity.triggerEvent('roaming_countdown_singleplayer')
      } else {
        entity.triggerEvent('roaming_countdown_multiplayer')
      }
    }

    if (command == "resetSpeed") {
      entity.speedTimer = 0
      entity.removeEffect('speed')
    }
    
    if (command == "detect") {
      console.warn(`detect`)
      let playersInFacility = 0
      for (let player of worldPlayers) {
        if (player.hasTag('inFacility')) {
          playersInFacility++
        }
      }
      if (playersInFacility == 0) {
        entity.triggerEvent('roaming')
      }
    }
  }
  
  if (message == "js:earth_leviathan") {
    const command = ev.message;

    if (command == "in_water") {
     const moonId = world.getDynamicProperty('moon')
     if (moonId == 5)  {
      entity.teleport({x:5980, y:61, z:12})
     }
    }

    if (command == "emerge_attempt") {
      console.warn(`Emerge Attempt`)
      const rand = Math.random() * 100
      if (rand <= 17) {
        entity.triggerEvent('emerging')
        OVERWORLD.playSound('monster.earth_leviathan.emerging_soon', entity.location, SOUND_OPTIONS)
        OVERWORLD.playSound('monster.earth_leviathan.emerging_soon2', entity.location, SOUND_OPTIONS)
      }
    }
    if (command == "emerge") {


      const randomValue = Math.floor(Math.random() * 21) - 10;
      const randomValue2 = Math.floor(Math.random() * 21) - 10;
      const yawDegrees = Math.atan2(randomValue2, randomValue) * (180 / Math.PI);
      
      const yawRadians = yawDegrees * (Math.PI / 180); // Convert degrees to radians
      const directionVector = {
        x: Math.cos(yawRadians),
        y: 0,
        z: Math.sin(yawRadians),
      };
      
      const entityPosition = entity.location;
      const newPosition = {
        x: entityPosition.x + directionVector.x,
        y: entityPosition.y,
        z: entityPosition.z + directionVector.z,
      };
      
      entity.teleport(entity.location, { facingLocation: newPosition });
      
      system.runTimeout(() => {
        const horizontalForce = {
          x: randomValue,
          z: randomValue2
        };
        entity.applyKnockback(horizontalForce, 0.025);
      }, 30);
      

      entity.runCommand(`stopsound @a[r=16] monster.earth_leviathan.emerging_soon`)
      entity.runCommand(`stopsound @a[r=16] monster.earth_leviathan.emerging_soon2`)

      system.runTimeout(() => {
        const entitiesInRange = OVERWORLD.getEntities({location: entity.location, maxDistance: 6, excludeFamilies: ["scrap", "equipment"]}) 
        for (let entityHit of entitiesInRange) {
          if (entityHit.typeId == "js:ship" || entityHit.typeId == "js:earth_leviathan" || entityHit.typeId == "js:tree" || entityHit.typeId == "js:alive_tree") continue;

          if (entityHit.typeId == "minecraft:player") {
            damagePlayer(entityHit, 100, entity, EntityDamageCause.override);
          } else {
            entityHit.remove()
          }
        }
      }, 0.7 * 20)

      system.runTimeout(() => {
        const entitiesInRange2 = OVERWORLD.getEntities({location: entity.location, maxDistance: 6, excludeFamilies: ["scrap", "equipment"]}) 
        for (let entityHit of entitiesInRange2) {
          if (entityHit.typeId == "js:ship" || entityHit.typeId == "js:earth_leviathan" || entityHit.typeId == "js:tree" || entityHit.typeId == "js:alive_tree") continue;
  
          if (entityHit.typeId == "minecraft:player") {
            damagePlayer(entityHit, 100, entity, EntityDamageCause.override);
          } else {
            entityHit.remove()
          }
        }
      }, 4.6 * 20)
    }
  }
  
  if (message == "js:forest_keeper") {
    const command = ev.message;

    if (command == "fall") {
      if (entity.typeId == "minecraft:player") {
        damagePlayer(entity, 100, entity, EntityDamageCause.anvil);
      }
    }

    if (command == "chase") {
      system.runTimeout(() => {
        entity.target.fearCooldown = 0
        changeFear(entity.target, 0.9, true);
      },10)
    }

    if (command == "attack") {
      const victim = OVERWORLD.getPlayers({location: entity.location, maxDistance: 3, closest: 1})[0]

      if (victim) {
        victim.inputPermissions.movementEnabled = false
        victim.inputPermissions.cameraEnabled = false
        const entityYRot = entity.getRotation().y
        let yRot  
        if (entityYRot < 0) {
          yRot = entityYRot - 180
        } else {
          yRot = entityYRot + 180
        }

        victim.setRotation({ x: entity.getRotation().x, y: yRot })

        victim.playAnimation('animation.forest_keeper.attack_employee')

        console.warn(victim.nameTag);
        victim.runCommand(`execute positioned as @e[type=js:forest_keeper,r=12,c=1] as @a[name="${victim.nameTag}"] anchored eyes run camera @s set minecraft:free pos ~ ~6.5 ~7 facing @s`)
        // victim.runCommand(`execute positioned as @e[type=js:forest_keeper,c=1] rotated as @e[type=js:forest_keeper,c=1] positioned ^ ^ ^5 run camera @a[name="${victim.nameTag}"] set minecraft:free pos ~ ~10.5 ~ facing @s`)
        
  
        const rideComponent = entity.getComponent("minecraft:rideable");
        const riders = rideComponent.getRiders();
  
        // victim.setRotation({ x: entity.getRotation().x, y: yRot })
        
        const work = rideComponent.addRider(victim);
        console.warn(`thing ${work}`)
        
        console.warn(`before `, victim.attackedTimeout)
        victim.attackedTimeout = system.runTimeout(() => {
          damagePlayer(victim, 100, entity, EntityDamageCause.override);
          victim.setProperty('js:sus_meter', 0)
          victim.triggerEvent('not_in_facility')
          system.runTimeout(() => {
            victim.inputPermissions.movementEnabled = true
            victim.inputPermissions.cameraEnabled = true
          }, 5);
        }, 5.0 * 20);
        
        console.warn(`attacked`, victim.attackedTimeout)
      } else {
        console.warn(`No player found`)
      }
    }

  }
  
  function generateOffset(inputLocation) {
    const getRandomOffset = () => {
      let offset = 0;
  
      // Keep generating until the offset is at least 0.5 but not more than 1.5 in absolute value
      while (Math.abs(offset) < 0.5 || Math.abs(offset) > 1) {
        offset = (Math.random() * 2) - 1; // Random between -1.5 and 1.5
      }
      return offset;
    };
  
    const offsetX = getRandomOffset();
    const offsetZ = getRandomOffset();
  
    // Apply the offsets to the original input location
    const newLocation = {
      x: inputLocation.x + offsetX,
      y: inputLocation.y + 0.5,
      z: inputLocation.z + offsetZ,
    };
  
    return newLocation;
  }
  
  if (message == "js:nutcracker") {
    const command = ev.message;

    if (command == "death") {
      const offset1 = generateOffset(entity.location);
      const offset2 = generateOffset(entity.location);
      OVERWORLD.spawnEntity('js:shotgun_shell_ent', offset1, {initialRotation: Math.random() * 360})
      OVERWORLD.spawnEntity('js:shotgun_shell_ent', offset2, {initialRotation: Math.random() * 360})
      OVERWORLD.playSound('items.shotgun_shell.drop', offset1, SOUND_OPTIONS)
      OVERWORLD.playSound('items.shotgun_shell.drop', offset2, SOUND_OPTIONS)
    }

    if (command == "attempt_inspect") {
      const nearbyPlayers = OVERWORLD.getPlayers({location: entity.location, maxDistance: 27})
      if (nearbyPlayers && nearbyPlayers.length > 0) {
        entity.triggerEvent('inspect')
        entity.timesShot = 0
      } else {
        const random = Math.random()
        if (random <= 0.4) {
          entity.triggerEvent('inspect')
          entity.timesShot = 0
        }
      }
    }

    if (command == "prepare_shoot1") {
      entity.playAnimation('animation.nutcracker.shoot1')
      entity.addEffect('slowness', 2.35 * 20, { showParticles: false, amplifier: 255 })
    }
    
    if (command == "prepare_shoot2") {
      entity.playAnimation('animation.nutcracker.shoot2')
      entity.addEffect('slowness', 1.83 * 20, { showParticles: false, amplifier: 255 })
    }

    if (command == "prepare_shoot3") {
      entity.playAnimation('animation.nutcracker.shoot3')
      entity.addEffect('slowness', 1.083 * 20, { showParticles: false, amplifier: 255 })
    }
    if (command == "reload") {
      entity.setProperty('js:reloading', true)
      if (entity.timesShot > 2) {
        entity.addEffect('slowness', 2.84 * 20, { showParticles: false, amplifier: 2 })
      } else {
        entity.addEffect('slowness', 2.84 * 20, { showParticles: false, amplifier: 255 })
      }
      system.runTimeout(() => {
        entity.setProperty('js:bullets', 2)
        entity.setProperty('js:reloading', false)
      },2.84 * 20)
    }


    if (command == "shoot") {
      entity.setProperty('js:bullets', entity.getProperty('js:bullets') - 1)
      shotgunShoot(entity)
      if (!entity.timesShot) {
        entity.timesShot = 0
      }
      entity.timesShot++
    }
  }


  //ghost girl runtime
  if (message == "js:ghost_girl") {
    
    for (let player of worldPlayers) {
      if (player.hasTag('ghostGirlTarget')) {
        entity.targettedPlayer = player
      }
    }
    if (!entity.targettedPlayer || (entity.targettedPlayer && entity.targettedPlayer.hasTag("spectator"))) {
      for (let player of worldPlayers) {
        player.removeTag('ghostGirlTarget')
      }
      entity.triggerEvent('minecraft:entity_spawned')
      entity.runCommand(`stopsound @a monster.ghost_girl.breathing`)
      entity.setDynamicProperty('timesSeen', 0)
      entity.setDynamicProperty('timesNotSeen', 0)
      entity.targettedPlayer = ghostGirlSelectTarget()
      if (entity.targettedPlayer !== null) {
        entity.targettedPlayer.addTag('ghostGirlTarget')
        console.warn(`Selected target: ${entity.targettedPlayer.nameTag}`)
      }
    }
    if (entity.targettedPlayer !== null) {
      entity.playAnimation('animation.ghost_girl.scale_target', { players: [ entity.targettedPlayer ] })
    } 

    const state = entity.getProperty('js:state')
    if (state == 1) {
      if (!entity.timer) {
        if (entity.location.y < 40) {
          entity.timer = 15 * 20
        } else {
          entity.timer = 25 * 20
        }
      }
      
      const directionVector = calculateDirectionVector(entity.targettedPlayer.location, entity.location);
      const lineOfSightBlock = OVERWORLD.getBlockFromRay(entity.targettedPlayer.location, directionVector, { maxDistance: 16 });
  
      if (!lineOfSightBlock) {
        entity.timer -= 3
      } else if (entity.lookedAt == true) {
        entity.timer -= 1.5
      } else {
        entity.timer -= 1
      }

      if (entity.timer <= 0) {
        entity.triggerEvent('disappear')
        if (entity.stared == false) {
          entity.setDynamicProperty('timesNotSeen', entity.getDynamicProperty('timesNotSeen') + 1)
        }
        entity.timer = 999
      }

    }
  }
  if (message == "js:ghost_girl_player_close") {
    if (entity.playerClose == false) {
      const targetLocation = entity.targettedPlayer.location
      const entityLocation = entity.location
      const distance = getDistance(targetLocation, entityLocation)
      if (distance <= 5 && distance > 3 && entity.getDynamicProperty('timesSeen') <= 1) {
        entity.setDynamicProperty('timesSeen', entity.getDynamicProperty('timesSeen') + 1)

        const random = Math.random()
        if (random < 0.25) {
          entity.triggerEvent('disappear')
          entity.runCommand(`stopsound @a monster.ghost_girl.breathing`)
        }
        entity.stared = true
        entity.playerClose = true
      } else if (distance <= 3 && entity.getDynamicProperty('timesSeen') >= 2) {
        entity.setDynamicProperty('timesSeen', entity.getDynamicProperty('timesSeen') + 1)
  
        const random = Math.random()
        if (random < 0.85) {
          const random2 = Math.random()
          if (random2 < 0.64) {
            entity.triggerEvent('disappear')
            entity.runCommand(`stopsound @a monster.ghost_girl.breathing`)
          } else {
            entity.triggerEvent('chase')
          }
        }
        entity.stared = true
        entity.playerClose = true
      } else {
        entity.triggerEvent('disappear')
        entity.runCommand(`stopsound @a monster.ghost_girl.breathing`)
      }
    }

  }

  if (message == "js:ghost_girl_reset_seens") {
     entity.setDynamicProperty('timesSeen', 0)
     entity.setDynamicProperty('timesNotSeen', 0)
  }
  
  if (message == "js:ghost_girl_move") {
    ghostGirlSelectTargetLocation(entity, entity.targettedPlayer, false)
  }
  if (message == "js:ghost_girl_teleport") {
    if (entity.lookAt == false) {
      ghostGirlSelectTargetLocation(entity, entity.targettedPlayer, true)
    }
  }
  if (message == "js:ghost_girl_staring") {
    if (entity.location.y < 40) {
      entity.timer = 15 * 20
    } else {
      entity.timer = 25 * 20
    }
    entity.stared = false
    entity.playerClose = false

    entity.seensRequired = 3
    const random = Math.random() 
    if (random < 0.5) {
      entity.seensRequired = 4
    }
  }
  if (message == "js:ghost_girl_looked") {
    entity.lookedAt = true
    entity.setProperty('js:looked', true)

    const state = entity.getProperty('js:state')
    if (state == 1 && entity.stared == false) {
      entity.setDynamicProperty('timesSeen', entity.getDynamicProperty('timesSeen') + 1)

      if (entity.getDynamicProperty('timesSeen') >= entity.seensRequired || entity.getDynamicProperty('timesNotSeen') >= 3) {
        const random = Math.random()
        if (random < 0.85) {
          entity.triggerEvent('chase')
          entity.runCommand(`stopsound @a monster.ghost_girl.breathing`)
        }

      }
      entity.stared = true
    }
  }
  if (message == "js:ghost_girl_not_looked") {
    entity.lookedAt = false
    entity.setProperty('js:looked', false)

  }


  if (message == "js:old_bird") {
    const command = ev.message;

    // if (command == "target_fly") {
    //   system.runJob(oldBirdRaycast(entity));

    //   function* oldBirdRaycast(entity) {
    //     const dist = 60;
    //     const horizontalSpreadAngle = 65; // Total horizontal spread from -32.5 to 32.5 degrees
    //     const verticalSpreadAngle = 65; // Total vertical spread
    //     const rayCount = 16; // Number of rays in each direction

    //     // Get the entity's direction and adjust it for a 55-degree downward angle
    //     const baseDirection = entity.getViewDirection();

    //     // Convert 55 degrees to radians
    //     const angleInRadians = 55 * (Math.PI / 180);
    //     const newYComponent = -Math.sin(angleInRadians); // Negative for looking downward
    //     const horizontalMagnitude = Math.cos(angleInRadians); // Horizontal component scaling

    //     // Adjust the base direction for vertical tilt
    //     const adjustedDirection = {
    //       x: baseDirection.x * horizontalMagnitude,
    //       y: newYComponent,
    //       z: baseDirection.z * horizontalMagnitude,
    //     };

    //     // Adjust the head location 53 blocks upwards
    //     const adjustedHeadLocation = {
    //       x: entity.getHeadLocation().x,
    //       y: entity.getHeadLocation().y + 53,
    //       z: entity.getHeadLocation().z,
    //     };

    //     // Calculate step angles for horizontal and vertical scanning
    //     const stepAngleHorizontal = horizontalSpreadAngle / (rayCount - 1);
    //     const stepAngleVertical = verticalSpreadAngle / (rayCount - 1);

    //     const hitEntitiesSet = new Set();

    //     for (let i = 0; i < rayCount; i++) {
    //       yield;
    //       for (let j = 0; j < rayCount; j++) {
    //         yield;

    //         // Calculate horizontal and vertical angles for each ray
    //         const horizontalAngle = -horizontalSpreadAngle / 2 + i * stepAngleHorizontal;
    //         const verticalAngle = -verticalSpreadAngle / 2 + j * stepAngleVertical;

    //         // Calculate the direction based on the angles
    //         const spreadDirection = calculateSpreadDirection(adjustedDirection, horizontalAngle, verticalAngle);
    //         const sourceDir = {
    //           x: spreadDirection.x,
    //           y: spreadDirection.y,
    //           z: spreadDirection.z,
    //         };

    //         // Spawn particles for visual debugging (optional)
    //         // spawnConeRayParticles(entity.dimension, adjustedHeadLocation, sourceDir, dist);

    //         // Perform raycasting
    //         const hitEntities = entity.dimension
    //           .getEntitiesFromRay(adjustedHeadLocation, sourceDir, { maxDistance: dist })
    //           .map((result) => result.entity)
    //           .filter((hitEntity) => hitEntity !== entity);

    //         // Process hit entities
    //         hitEntities.forEach((hitEntity) => {
    //           if (!hitEntitiesSet.has(hitEntity)) {
    //             hitEntitiesSet.add(hitEntity);

    //             const hitLocation = hitEntity.getHeadLocation();
    //             const item = hitEntity.getComponent("minecraft:item")?.itemStack;

    //             const familyComponent = hitEntity.getComponent("minecraft:type_family");
    //             if (familyComponent && familyComponent.hasTypeFamily("scrap")) {
    //               return;
    //             }

    //             if (hitEntity !== entity && !item && hitEntity.typeId !== "js:ship") {
    //               console.warn(hitEntity.typeId);
    //               if (!hitEntity.isSneaking) {
    //                 entity.applyDamage(1, {
    //                   cause: EntityDamageCause.void,
    //                   damagingEntity: hitEntity,
    //                 });
    //                 const health = entity.getComponent("minecraft:health");
    //                 health.setCurrentValue(health.effectiveMax);
    //               }
    //             }
    //           }
    //         });
    //         yield;
    //       }
    //     }
    //   }

    //   function spawnConeRayParticles(dimension, startLocation, direction, maxDistance) {
    //     const stepSize = 14; // Distance between each particle
    //     const horizontalSpreadAngle = 65; // Total horizontal spread of the cone
    //     const verticalSpreadAngle = 65; // Total vertical spread of the cone
    //     const particleDensity = 3; // Number of particles per cone slice (higher value = denser particles)

    //     // Convert angles to radians for calculations
    //     const horizontalSpreadRad = horizontalSpreadAngle * (Math.PI / 180);
    //     const verticalSpreadRad = verticalSpreadAngle * (Math.PI / 180);

    //     // Iterate through distances
    //     for (let distance = stepSize; distance <= maxDistance; distance += stepSize) {
    //       // Number of particles per distance slice
    //       for (let i = 0; i < particleDensity; i++) {
    //         // Random angle within the cone
    //         const horizontalAngle = Math.random() * horizontalSpreadRad - horizontalSpreadRad / 2;
    //         const verticalAngle = Math.random() * verticalSpreadRad - verticalSpreadRad / 2;

    //         // Convert spherical coordinates to Cartesian coordinates
    //         const xOffset = Math.cos(verticalAngle) * Math.cos(horizontalAngle);
    //         const yOffset = Math.sin(verticalAngle);
    //         const zOffset = Math.cos(verticalAngle) * Math.sin(horizontalAngle);

    //         // Adjust direction to create a cone
    //         const particleDirection = {
    //           x: direction.x * xOffset - direction.z * zOffset,
    //           y: direction.y * yOffset,
    //           z: direction.z * xOffset + direction.x * zOffset,
    //         };

    //         // Apply the 55-degree downward tilt to the particle direction
    //         const angleInRadians = 55 * (Math.PI / 180);
    //         const tiltedParticleDirection = {
    //           x: particleDirection.x * Math.cos(angleInRadians),
    //           y: particleDirection.y - Math.sin(angleInRadians),
    //           z: particleDirection.z * Math.cos(angleInRadians),
    //         };

    //         // Particle location based on distance and direction
    //         const particleLocation = {
    //           x: startLocation.x + tiltedParticleDirection.x * distance,
    //           y: startLocation.y + tiltedParticleDirection.y * distance,
    //           z: startLocation.z + tiltedParticleDirection.z * distance,
    //         };

    //         // Spawn particle at the calculated location
    //         dimension.spawnParticle("minecraft:endrod", particleLocation);
    //       }
    //     }
    //   }

    //   function calculateSpreadDirection(baseDirection, horizontalAngle, verticalAngle) {
    //     const horizontalRad = horizontalAngle * (Math.PI / 180);
    //     const verticalRad = verticalAngle * (Math.PI / 180);

    //     const direction = {
    //       x: baseDirection.x * Math.cos(horizontalRad) - baseDirection.z * Math.sin(horizontalRad),
    //       y: baseDirection.y + Math.sin(verticalRad),
    //       z: baseDirection.z * Math.cos(horizontalRad) + baseDirection.x * Math.sin(horizontalRad),
    //     };

    //     return direction;
    //   }
    // }

    if (command == "target_walk") {
      system.runJob(oldBirdScanRaycast(entity));

      function* oldBirdScanRaycast(sourceEntity) {
        const dist = 50;
        const horizontalSpreadAngle = 80; // Total horizontal spread from -35 to 35 degrees
        const verticalSpreadAngle = 45; // Vertical spread remains the same
        const rayCount = 24; // Number of rays in each direction

        // Calculate step angles for horizontal and vertical scanning
        const stepAngleHorizontal = horizontalSpreadAngle / (rayCount - 1);
        const stepAngleVertical = verticalSpreadAngle / (rayCount - 1);

        const hitEntitiesSet = new Set();
        const baseDirection = sourceEntity.getViewDirection();
        yield;

        // Scan horizontally from -35 to 35 degrees
        for (let i = 0; i < rayCount; i++) {
          yield;
          for (let j = 0; j < rayCount; j++) {
            yield;
            // Calculate the angles for the current ray
            const horizontalAngle = -horizontalSpreadAngle / 2 + i * stepAngleHorizontal;
            const verticalAngle = -verticalSpreadAngle / 2 + j * stepAngleVertical;

            // Calculate the direction based on the angles
            const spreadDirection = calculateSpreadDirection(baseDirection, horizontalAngle, verticalAngle);
            const sourceDir = {
              x: spreadDirection.x,
              y: spreadDirection.y,
              z: spreadDirection.z,
            };

            // Spawn particles for visual debugging (optional)
            // spawnRayParticles(sourceEntity.dimension, sourceEntity.getHeadLocation(), sourceDir, dist);
            yield;

            // Perform raycasting
            const hitEntities = sourceEntity.dimension
              .getEntitiesFromRay(sourceEntity.getHeadLocation(), sourceDir, { maxDistance: dist })
              .map((result) => result.entity)
              .filter((hitEntity) => hitEntity !== sourceEntity);

            // Process hit entities
            hitEntities.forEach((hitEntity) => {
              if (!hitEntitiesSet.has(hitEntity)) {
                hitEntitiesSet.add(hitEntity);

                const hitLocation = hitEntity.getHeadLocation();
                const item = hitEntity.getComponent("minecraft:item")?.itemStack;

                const familyComponent = hitEntity.getComponent("minecraft:type_family");
                if (familyComponent && familyComponent.hasTypeFamily("scrap")) {
                  return;
                }

                if (hitEntity !== sourceEntity && !item) {
                  sourceEntity.triggerEvent("spotted");
                }
              }
            });
            yield;
          }
        }
      }
    }

    if (command == "missile_shoot") {
      const entityPosition = entity.getHeadLocation();
      const entityViewDirection = entity.getViewDirection();
      const entityRotation = entity.getRotation();

      const spawnPosition = {
        x: entityPosition.x + entityViewDirection.x * 2,
        y: entityPosition.y + entityViewDirection.y * 2,
        z: entityPosition.z + entityViewDirection.z * 2,
      };

      const missile = OVERWORLD.spawnEntity("js:missile", spawnPosition);
      missile.setRotation({ x: entityRotation.x, y: entityRotation.y });
      const projectile = missile.getComponent("minecraft:projectile");
      const magnitudeMultiplier = 2;
      const scaledDirection = {
        x: entityViewDirection.x * magnitudeMultiplier,
        y: entityViewDirection.y * magnitudeMultiplier,
        z: entityViewDirection.z * magnitudeMultiplier,
      };
      projectile.shoot(scaledDirection, { uncertainty: 18 });
    }

    if (command == "burn") {
      const rideComponent = entity.getComponent("minecraft:rideable");
      const riders = rideComponent.getRiders();
      //     console.warn(riders.length)
      if (riders.length == 0) {
        system.runTimeout(() => {
          const players = OVERWORLD.getPlayers({
            closest: 1,
            maxDistance: 4,
            location: entity.location,
          });
          // console.warn(JSON.stringify([...players]))
          for (let player of players) {
            rideComponent.addRider(player);
            player.inputPermissions.movementEnabled = false

            system.runTimeout(() => {
              player.inputPermissions.movementEnabled = true
              damagePlayer(player, 100, entity, EntityDamageCause.lava);
            }, 2.55 * 20);
          }
        }, 0.63 * 20);
      }
    }

    if (command == "missile_explode") {
      const BLOCKS_MISSILES = [
        "ding:ship_collision", "ding:ship_door_collision"
      ]

      OVERWORLD.playSound("monster.old_bird.missile_explosion2", entity.location, SOUND_OPTIONS);
      OVERWORLD.spawnParticle("ding:landmine_impact", entity.location);
      OVERWORLD.spawnParticle("ding:landmine_flames", entity.location);
      const hitEntities = OVERWORLD.getEntities({
        location: entity.location,
        maxDistance: 8,
        excludeFamilies: ["scrap", "inanimate"],
      });
      for (let hitEntity of hitEntities) {
        const distance = getDistance(hitEntity.location, entity.location);

        const directionVector = getDirectionVector(hitEntity.location, entity.location);

        const blocksInBetween = OVERWORLD.getBlockFromRay(hitEntity.location, directionVector, { maxDistance: 6 });
        if (!blocksInBetween || (blocksInBetween && !BLOCKS_MISSILES.includes(blocksInBetween.typeId))) {
          if (hitEntity.typeId == "minecraft:player") {
            if (distance <= 1) {
              damagePlayer(hitEntity, 100, entity, EntityDamageCause.blockExplosion);
            } else {
              damagePlayer(hitEntity, 30, entity, EntityDamageCause.blockExplosion);
            }
            missileKnockback(hitEntity, entity);
          }
          if (hitEntity.typeId == "js:eyeless_dog") {
            if (distance <= 1) {
              hitEntity.applyDamage(12, {
                cause: EntityDamageCause.entityAttack,
                damagingEntity: entity,
              });
            } else {
              hitEntity.applyDamage(6, {
                cause: EntityDamageCause.entityAttack,
                damagingEntity: entity,
              });
            }
          }
          if (hitEntity.typeId == "js:forest_keeper") {
            hitEntity.applyDamage(2, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: entity,
            });
            hitEntity.setOnFire(35, false)
          }
        }
      }
      entity.runCommand(`stopsound @a[r=12] monster.old_bird.missile_fly`);
      entity.remove();

      function missileKnockback(player, entity) {
        const maxRange = 8;
        const knockbackResistance = 0.9;
        const knockbackMultiplier = 1 / (1 - knockbackResistance); // Multiplier to nullify resistance

        // Get the positions of the player and entity
        const playerPos = player.location;
        const entityPos = entity.location;

        // Calculate the distance between the player and the entity
        const dx = entityPos.x - playerPos.x;
        const dy = entityPos.y - playerPos.y;
        const dz = entityPos.z - playerPos.z;
        const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

        // If the entity is within the max range, calculate knockback
        if (distance <= maxRange) {
          // Determine the knockback strength based on the distance
          const strengthFactor = (maxRange - distance) / maxRange;
          const baseHoriStrength = 10; // Base horizontal knockback strength
          const baseVertStrength = 1;  // Base vertical knockback strength
        
          // Calculate the scaled knockback strengths
          const horiStrength = baseHoriStrength * strengthFactor * knockbackMultiplier;
          const vertStrength = baseVertStrength * strengthFactor * knockbackMultiplier;
        
          // Normalize the direction vector (dx, dz)
          const directionLength = Math.sqrt(dx * dx + dz * dz);
          const horiDirectionX = dx / directionLength;
          const horiDirectionZ = dz / directionLength;
        
          // Create horizontal force vector
          const horizontalForce = {
            x: horiDirectionX * horiStrength,
            z: horiDirectionZ * horiStrength
          };
        
          // Apply the knockback to the entity
          player.applyKnockback(horizontalForce, vertStrength);
        }
        
      }
    }

    if (command == "crush") {
      const players = OVERWORLD.getPlayers({ location: entity.location, maxDistance: 3 });
      for (let player of players) {
        const distance = getDistance(player.location, entity.location);
        if (distance <= 1) {
          damagePlayer(player, 70, entity, EntityDamageCause.anvil);
        } else if (distance > 1 && distance <= 2) {
          damagePlayer(player, 50, entity, EntityDamageCause.anvil);
        } else if (distance > 2) {
          damagePlayer(player, 30, entity, EntityDamageCause.anvil);
        }
      }
    }

    if (command == "walk") {
      const position = entity.location;

      // Cardinal direction checks
      try {
        const directions = {
          north: OVERWORLD.getBlock({ x: position.x, y: position.y, z: position.z - 1 }),
          south: OVERWORLD.getBlock({ x: position.x, y: position.y, z: position.z + 1 }),
          east: OVERWORLD.getBlock({ x: position.x + 1, y: position.y, z: position.z }),
          west: OVERWORLD.getBlock({ x: position.x - 1, y: position.y, z: position.z }),
        };

        // Helper function to check if the block should be considered as blocked
        function isBlocked(block) {
          const isAir = block.typeId === "minecraft:air";
          const hasKaiDoublePermutation = block.permutation.getState("kai:double") !== undefined;
          return !isAir && !hasKaiDoublePermutation;
        }

        // Identify blocked directions
        const blocked = {
          north: isBlocked(directions.north),
          south: isBlocked(directions.south),
          east: isBlocked(directions.east),
          west: isBlocked(directions.west),
        };

        //  console.warn(JSON.stringify(blocked))

        const rand = Math.random();
        let randRot;

        let chance;
        if (blocked.north || blocked.south || blocked.east || blocked.west) {
          chance = 1;
        } else {
          // Set chance based on entity state
          if (entity.getProperty("js:state") == 3) {
            chance = 0.65;
          }
          if (entity.getProperty("js:state") == 1) {
            chance = 0.25;
          }
        }
        // console.warn(chance)
        //  console.warn(rand)

        if (rand < chance) {
          if (blocked.north && !blocked.south) {
            randRot = 0 + Math.random() * 45;
          } else if (blocked.south && !blocked.north) {
            randRot = 180 + Math.random() * 45;
          } else if (blocked.east && !blocked.west) {
            randRot = 90 + Math.random() * 45;
          } else if (blocked.west && !blocked.east) {
            randRot = 270 + Math.random() * 45;
          } else {
            randRot = Math.random() * 360;
          }

          entity.setRotation({ x: 0, y: randRot });
        }

        const direction = entity.getViewDirection();
        const impulse = {
          x: direction.x * 0.8, // Strength applied in the x direction
          y: 0.08, // Vertical influence
          z: direction.z * 0.8, // Strength applied in the z direction
        };
        entity.applyImpulse(impulse);

        system.runTimeout(() => {
          entity.clearVelocity();
          entity.runCommand(`camerashake add @a[r=12] 0.3 0.25 rotational`);
          entity.runCommand(`camerashake add @a[rm=12,r=24] 0.1 0.25 rotational`);
        }, 6);
      } catch { }
    }

    if (command == "run") {
      const position = entity.location;

      try {
        const directions = {
          north: OVERWORLD.getBlock({ x: position.x, y: position.y, z: position.z - 1 }),
          south: OVERWORLD.getBlock({ x: position.x, y: position.y, z: position.z + 1 }),
          east: OVERWORLD.getBlock({ x: position.x + 1, y: position.y, z: position.z }),
          west: OVERWORLD.getBlock({ x: position.x - 1, y: position.y, z: position.z }),
        };

        // Helper function to check if the block should be considered as blocked
        function isBlocked(block) {
          const isAir = block.typeId === "minecraft:air";
          const hasKaiDoublePermutation = block.permutation.getState("kai:double") !== undefined;
          return !isAir && !hasKaiDoublePermutation;
        }

        // Identify blocked directions
        const blocked = {
          north: isBlocked(directions.north),
          south: isBlocked(directions.south),
          east: isBlocked(directions.east),
          west: isBlocked(directions.west),
        };

        const rand = Math.random();
        let randRot;

        let chance = 0;
        if (blocked.north || blocked.south || blocked.east || blocked.west) {
          chance = 1;
        }

        if (rand < chance) {
          if (blocked.north && !blocked.south) {
            randRot = 0 + Math.random() * 45;
          } else if (blocked.south && !blocked.north) {
            randRot = 180 + Math.random() * 45;
          } else if (blocked.east && !blocked.west) {
            randRot = 90 + Math.random() * 45;
          } else if (blocked.west && !blocked.east) {
            randRot = 270 + Math.random() * 45;
          } else {
            randRot = Math.random() * 360;
          }

          entity.setRotation({ x: 0, y: randRot });
        }

        const direction = entity.getViewDirection();
        const impulse = {
          x: direction.x * 0.8, // Strength applied in the x direction
          y: 0.08, // Vertical influence
          z: direction.z * 0.8, // Strength applied in the z direction
        };
        entity.applyImpulse(impulse);

        system.runTimeout(() => {
          entity.clearVelocity();
          entity.runCommand(`camerashake add @a[r=12] 0.35 0.25 rotational`);
          entity.runCommand(`camerashake add @a[rm=12,r=24] 0.15 0.25 rotational`);
        }, 6);
      } catch { }
    }
  }
});

function entitySpawns(roundedTime) {
  const moonId = world.getDynamicProperty("moon");
  let mapName;
  for (const key of Object.keys(MAPS)) {
    if (MAPS[key].id === moonId) {
      mapName = key;
      break;
    }
  }
  const ventMap = new Map(world.getDynamicProperty("vents") ? JSON.parse(world.getDynamicProperty("vents")) : []);

  let inactiveVents = 0;
  for (const [key, { ventLocation, active }] of ventMap.entries()) {
    if (!active) {
      inactiveVents++;
    }
  }
  console.warn(`inactive vents ${inactiveVents}`);

  function roundNegativeToZero(number) {
    return number < 0 ? 0 : number;
  }

  const normalizedTime = roundedTime / 980;
  let indoorMinCount =
    5 * normalizedTime * normalizedTime +
    Math.abs(world.getDynamicProperty("day") - 3) / 1.6 -
    MAPS[mapName].indoor_spawn_deviation;
  let indoorMaxCount = 5 * normalizedTime * normalizedTime + MAPS[mapName].indoor_spawn_deviation;
  let clampIndoorMax = Math.min(indoorMaxCount, Math.max(20, inactiveVents));

  let minEntitiesToSpawn = 0;
  const weather = world.getDynamicProperty("currentWeather");
  if (weather == 5) {
    minEntitiesToSpawn = MAPS[mapName].eclipsedMinEntSpawn;
  }
  const minSpawnOverride = world.getDynamicProperty("minSpawnOverride");
  if (minSpawnOverride > 0) {
    minEntitiesToSpawn = minSpawnOverride;
  }

  let indoorSpawnAmount = getRandomInt(Math.ceil(indoorMinCount), Math.floor(indoorMaxCount));

  function clamp(value, min, max) {
    if (value < min) {
      return min;
    } else if (value > max) {
      return max;
    } else {
      return value;
    }
  }
  indoorSpawnAmount = clamp(indoorSpawnAmount, minEntitiesToSpawn, clampIndoorMax);

  console.warn(
    `Indoormin: ${indoorMinCount} indoormax ${indoorMaxCount} clampindoormax ${clampIndoorMax}MinEntitesToSpawn ${minEntitiesToSpawn}  Indoor Spawn Amount: ${indoorSpawnAmount}`
  );

  if (indoorSpawnAmount > 0) {
    let indoorSpawnTable = JSON.parse(JSON.stringify(MAPS[mapName].spawns));
    const pendingSpawns = {}; // Tracking pending spawns

    /*
        for (const ent in indoorSpawnTable) {
            const entPowerLevel = MAPS[mapName].spawns[ent].power_level;
            const entCount = (OVERWORLD.getEntities({ type: ent }).length) + (pendingSpawns[ent] || 0); // Include pending spawns
            const currentPowerLevel = world.getDynamicProperty('currentPowerLevel') ?? 0;
            const maxCount = MAPS[mapName].spawns[ent].max_count;

            console.warn(`EntPowerLevel ${entPowerLevel} entCount ${entCount} currentPowerLevel ${currentPowerLevel}`);

            if ((currentPowerLevel + entPowerLevel > MAPS[mapName].indoor_power) || (entCount + 1 > maxCount)) {
                indoorSpawnTable[ent] = 0;
                console.warn(`Chance of ${ent} is now ${indoorSpawnTable[ent]}`);
            }
        }*/

    const indoorA = MAPS[mapName].spawnCurves["indoor"].a;
    const indoorB = MAPS[mapName].spawnCurves["indoor"].b;
    const indoorC = MAPS[mapName].spawnCurves["indoor"].c;
    const indoorD = MAPS[mapName].spawnCurves["indoor"].d;
    const indoorType = MAPS[mapName].spawnCurves["indoor"].type;

    function calculateExpression(a, x, d, c) {
      let logX = Math.log(x);
      let logXToThePowerD = Math.pow(logX, d);
      return a * logXToThePowerD + c;
    }

    let indoorSpawnChance = 0;
    if (indoorType == "cubic") {
      indoorSpawnChance =
        indoorA * ((roundedTime + indoorB) * (roundedTime + indoorB) * (roundedTime + indoorB)) +
        indoorD * (roundedTime * roundedTime) +
        indoorC;
    }
    if (indoorType == "log") {
      indoorSpawnChance = calculateExpression(indoorA, roundedTime, indoorD, indoorC);
    }
    if (indoorType == "quad") {
      indoorSpawnChance = indoorA * (roundedTime ** 2) + indoorC;
    } 

    console.warn(`indoor spawn chance ${indoorSpawnChance} ${JSON.stringify(indoorSpawnTable)}`);

    for (const ent in indoorSpawnTable) {
      console.warn(`Old Chance of ${ent} is ${indoorSpawnTable[ent]}`);
      if (indoorSpawnChance <= 0) {
        indoorSpawnChance = 0;
      }
      indoorSpawnTable[ent] *= indoorSpawnChance;
      console.warn(`New Chance of ${ent} is now ${indoorSpawnTable[ent]}`);
    }

    function selectRandomEntity(currentPowerLevel) {
      const totalChance = Object.values(indoorSpawnTable).reduce((sum, ent) => sum + ent, 0);
      let randomChance = Math.random() * totalChance;
    
      for (const ent in indoorSpawnTable) {
        const entCount = parseInt(world.getDynamicProperty(`entCount_${ent.split('js:')[1]}`)) || 0;
        const pendingCount = pendingSpawns[ent] || 0; // Include pending spawns
        const entPowerLevel = ENTITIES[ent]?.power_level || 0; // Default to 0 if not defined
        const maxCount = ENTITIES[ent]?.max_count || Infinity; // Default to no limit if not defined
    
        console.warn(`${ent}: ${entCount} + ${pendingCount} (current count), ${entPowerLevel} (power level), ${maxCount} (max count)`);
    
        // Check spawn conditions
        const isUnderMaxCount = entCount + pendingCount + 1 <= maxCount;
        const isWithinPowerLimit = currentPowerLevel + entPowerLevel <= MAPS[mapName].indoor_power;
    
        if (indoorSpawnTable[ent] > 0 && isUnderMaxCount && isWithinPowerLimit) {
          if (randomChance < indoorSpawnTable[ent]) {
            console.warn(`Selected entity: ${ent}`);
            // Track pending spawns
            if (!pendingSpawns[ent]) pendingSpawns[ent] = 0;
            pendingSpawns[ent]++;
    
            // Increment the dynamic property for the selected entity
            world.setDynamicProperty(`entCount_${ent.split('js:')[1]}`, entCount + 1);
    
            return ent; // Return the selected entity
          }
          randomChance -= indoorSpawnTable[ent];
        } else {
          // Detailed warnings for why the entity cannot be selected
          if (indoorSpawnTable[ent] <= 0) {
            console.warn(`Chance of ${ent} is 0`);
          } else if (!isUnderMaxCount) {
            console.warn(`Max count (${maxCount}) of ${ent} reached`);
          } else if (!isWithinPowerLimit) {
            console.warn(
              `Power level (${currentPowerLevel + entPowerLevel}) of ${ent} exceeds indoor power limit (${MAPS[mapName].indoor_power})`
            );
          }
        }
      }
    
      console.warn("No suitable entity found");
      return null; // No entity selected
    }
    

    function selectRandomInactiveVent() {
      const inactiveVentsArray = [...ventMap.entries()].filter(([key, vent]) => !vent.active);
      if (inactiveVentsArray.length === 0) return null;
      const randomIndex = Math.floor(Math.random() * inactiveVentsArray.length);
      return inactiveVentsArray[randomIndex];
    }

    for (let i = 0; i < indoorSpawnAmount; i++) {
      const selectedEntity = selectRandomEntity(world.getDynamicProperty("currentPowerLevel") ?? 0);
      if (selectedEntity) {
        const selectedVent = selectRandomInactiveVent();
        if (selectedVent) {
          const [ventKey, ventData] = selectedVent;
          ventData.active = true;
          ventMap.set(ventKey, ventData);
          world.setDynamicProperty("vents", JSON.stringify([...ventMap]));
          console.warn(`Starting spawn for ${selectedEntity} at ${JSON.stringify(ventData.ventLocation)}`);

          const entPowerLevel = ENTITIES[selectedEntity].power_level
          let currentPowerLevel = world.getDynamicProperty("currentPowerLevel") ?? 0;
          currentPowerLevel += entPowerLevel;
          world.setDynamicProperty("currentPowerLevel", currentPowerLevel);
          let spawnDelay = 11
          let spawnSound = 'misc.vent.crawl'
          let spawnVolume = 0.5
          if (selectedEntity == "js:nutcracker") {
            spawnDelay = 16
            spawnSound = 'monster.nutcracker.vent_crawl'
            spawnVolume = 1.2
          }

          OVERWORLD.playSound(spawnSound, ventData.ventLocation, { volume: spawnVolume })
          system.runTimeout(() => {
            OVERWORLD.spawnEntity(selectedEntity, ventData.ventLocation);
            ventData.active = false;
            ventMap.set(ventKey, ventData);

            const ventBlock = OVERWORLD.getBlock(ventData.ventLocation);
            ventBlock.setPermutation(ventBlock.permutation.withState("js:open", true));

            world.setDynamicProperty("vents", JSON.stringify([...ventMap]));
            console.warn(`Spawned ${selectedEntity} at ${JSON.stringify(ventData.ventLocation)}`);
            OVERWORLD.playSound('misc.vent.open', ventData.ventLocation, SOUND_OPTIONS)
          }, spawnDelay * 20);
        }
      } else {
        console.warn(`None selected`);
      }
    }
  } else {
    console.warn(`No spawns this cycle`);
  }

  let outdoorMinCount = 5 * normalizedTime * normalizedTime + Math.abs(world.getDynamicProperty("day") - 3) / 1.6 - 3;
  let outdoorMaxCount = 5 * normalizedTime * normalizedTime + 3;
  let clampOutdoorMax = Math.min(outdoorMaxCount, 20);

  let minOutdoorEntitiesToSpawn = 0;
  if (weather == 5) {
    minOutdoorEntitiesToSpawn = MAPS[mapName].eclipsedMinEntSpawn;
  }
  if (minSpawnOverride > 0) {
    minOutdoorEntitiesToSpawn = minSpawnOverride;
  }

  let outdoorSpawnAmount = getRandomInt(Math.ceil(outdoorMinCount), Math.floor(outdoorMaxCount));

  outdoorSpawnAmount = clamp(outdoorSpawnAmount, minOutdoorEntitiesToSpawn, clampOutdoorMax);
  console.warn(outdoorSpawnAmount)

  if (outdoorSpawnAmount > 0) {
    let outdoorSpawnTable = JSON.parse(JSON.stringify(MAPS[mapName].outdoor_spawns));

    const outdoorA = MAPS[mapName].spawnCurves["outdoor"].a;
    const outdoorB = MAPS[mapName].spawnCurves["outdoor"].b;
    const outdoorC = MAPS[mapName].spawnCurves["outdoor"].c;
    const outdoorD = MAPS[mapName].spawnCurves["outdoor"].d;
    const outdoorType = MAPS[mapName].spawnCurves["outdoor"].type;

    let outdoorSpawnChance = 0;
    if (outdoorType == "cubic") {
      outdoorSpawnChance =
      outdoorA * ((roundedTime + outdoorB) * (roundedTime + outdoorB) * (roundedTime + outdoorB)) +
      outdoorD * (roundedTime * roundedTime) +
      outdoorC;
    } else if (outdoorType == "quad") {
      outdoorSpawnChance = outdoorA * roundedTime * roundedTime + outdoorC;
    } else if (outdoorType == "quad2") {
      outdoorSpawnChance = outdoorA * ((roundedTime - outdoorB) ** 2) + outdoorC;
    }
    if (weather == 5) {
      outdoorSpawnChance = 1;
    }

    console.warn(`outdoor spawn chance ${outdoorSpawnChance} ${JSON.stringify(outdoorSpawnTable)}`);

    for (const ent in outdoorSpawnTable) {
      console.warn(`Old Chance of ${ent} is ${outdoorSpawnTable[ent]}`);
      if (outdoorSpawnChance <= 0) {
        outdoorSpawnChance = 0;
      }
      outdoorSpawnTable[ent] *= outdoorSpawnChance;
      console.warn(`New Chance of ${ent} is now ${outdoorSpawnTable[ent]}`);
    }


    console.warn(`new ${JSON.stringify(outdoorSpawnTable)}`);

    function selectRandomOutdoorEntity(currentPowerLevel) {
      const totalChance = Object.values(outdoorSpawnTable).reduce((sum, ent) => sum + ent, 0);
      let randomChance = Math.random() * totalChance;
    
      for (const ent in outdoorSpawnTable) {
        const entCount = parseInt(world.getDynamicProperty(`entCount_${ent.split('js:')[1]}`)) || 0;
        const entPowerLevel = ENTITIES[ent]?.power_level || 0; // Default to 0 if not defined
        const maxCount = ENTITIES[ent]?.max_count || Infinity; // Default to no limit if not defined
    
        console.warn(`${ent}: ${entCount} (current count), ${entPowerLevel} (power level), ${maxCount} (max count)`);
    
        // Check spawn conditions
        const isUnderMaxCount = entCount + 1 <= maxCount;
        const isWithinPowerLimit = currentPowerLevel + entPowerLevel <= MAPS[mapName].outdoor_power;
    
        if (outdoorSpawnTable[ent] > 0 && isUnderMaxCount && isWithinPowerLimit) {
          if (randomChance < outdoorSpawnTable[ent]) {
            console.warn(`Selected entity: ${ent}`);
    
            // Increment the dynamic property for the selected entity
            world.setDynamicProperty(`entCount_${ent.split('js:')[1]}`, entCount + 1);
    
            return ent; // Return the selected entity
          }
          randomChance -= outdoorSpawnTable[ent];
        } else {
          // Detailed warnings for why the entity cannot be selected
          if (outdoorSpawnTable[ent] <= 0) {
            console.warn(`Chance of ${ent} is 0`);
          } else if (!isUnderMaxCount) {
            console.warn(`Max count (${maxCount}) of ${ent} reached`);
          } else if (!isWithinPowerLimit) {
            console.warn(
              `Power level (${currentPowerLevel + entPowerLevel}) of ${ent} exceeds outdoor power limit (${MAPS[mapName].outdoor_power})`
            );
          }
        }
      }
    
      console.warn("No suitable entity found");
      return null; // No entity selected
    }
    


    for (let i = 0; i < outdoorSpawnAmount; i++) {
      const selectedEntity = selectRandomOutdoorEntity(world.getDynamicProperty("currentOutdoorPowerLevel") ?? 0);
      if (selectedEntity) {
        let selectedLocation = selectOutdoorSpawnLocation(40, 20);
        if (weather == 5) {
          selectedLocation = selectOutdoorSpawnLocation(32, 20);
        }
        if (selectedLocation) {
          const entPowerLevel = ENTITIES[selectedEntity].power_level
          let currentPowerLevel = world.getDynamicProperty("currentOutdoorPowerLevel") ?? 0;
          currentPowerLevel += entPowerLevel;
          world.setDynamicProperty("currentOutdoorPowerLevel", currentPowerLevel);

          if (selectedEntity === "js:old_bird") {
            const oldBirds = OVERWORLD.getEntities({ type: "js:old_bird" });
            const availableOldBirds = oldBirds.filter((bird) => bird.getProperty("js:state") === 0);

            if (availableOldBirds.length > 0) {
              const randomIndex = Math.floor(Math.random() * availableOldBirds.length);
              const birdToActivate = availableOldBirds[randomIndex];

              birdToActivate.triggerEvent("activate");
              console.warn(`Activated an existing old bird at ${JSON.stringify(birdToActivate.location)}`);
            } else {
              const newSpawnLocation = selectOutdoorSpawnLocation();
              if (newSpawnLocation) {
                const newOldBird = OVERWORLD.spawnEntity("js:old_bird", newSpawnLocation);
                newOldBird.triggerEvent("activate");
                console.warn(`Spawned a new old bird at ${JSON.stringify(newSpawnLocation)}`);
              } else {
                console.warn("No valid spawn location available for new old bird.");
              }
            }
          } else {
            const dog = OVERWORLD.spawnEntity(selectedEntity, selectedLocation);
            dog.triggerEvent("minecraft:entity_spawned");
            console.warn(`Spawned ${selectedEntity} at ${JSON.stringify(selectedLocation)}`);
          }
        }
      } else {
        console.warn(`None selected`);
      }
    }
  } else {
    console.warn(`No spawns this cycle`);
  }
}

export function spawnTrees(moonId = 1, spawnCount = Math.floor(Math.random() * (38 - 29 + 1)) + 29, treeType = "js:tree") {
  for (let i = 0; i < spawnCount; i++) {
    let selectedLocation
    if (moonId == 5) {
      selectedLocation = selectOutdoorSpawnLocation(108, 10);
    } else {
      selectedLocation = selectOutdoorSpawnLocation();
    }
    if (selectedLocation) {
      const spawnedEntity = OVERWORLD.spawnEntity(treeType, selectedLocation);
      spawnedEntity.triggerEvent("minecraft:entity_spawned");
      // console.warn(`Spawned tree at ${JSON.stringify(selectedLocation)}`);
    }
  }
}

export function spawnDaytimeEntities() {
  const moonId = world.getDynamicProperty("moon");
  if (moonId == 0) return;
  let mapName;
  for (const key of Object.keys(MAPS)) {
    if (MAPS[key].id === moonId) {
      mapName = key;
      break;
    }
  }

  function getRandomEntity(moonId) {
    const randomValue = Math.random() * 100;
    let locustChance = 75
    if (moonId == 1) {
      locustChance = 80
    }
    if (moonId == 5) {
      locustChance = 60
    }

    if (randomValue <= locustChance) {
      return "js:wandering_locusts"; // Roaming Locust
    } else {
      return "js:circuit_bee"; // Circuit Bee
    }
  }

  const weather = world.getDynamicProperty("currentWeather");

  if (moonId !== 7 && moonId !== 9 && moonId !== 10) {
    let spawnCount = Math.floor(Math.random() * (6 - 2 + 1)) + 2;

    if (weather == 5) {
      spawnCount += 1;
    }

    if (moonId == 5) {
      spawnCount += 1;
    }

    for (let i = 0; i < spawnCount; i++) {
      const selectedLocation = selectOutdoorSpawnLocation();
      if (selectedLocation) {
        const entityToSpawn = getRandomEntity(moonId);

        if (entityToSpawn == "js:wandering_locusts") {
          const spawnedEntity = OVERWORLD.spawnEntity(entityToSpawn, selectedLocation);
          spawnedEntity.triggerEvent("minecraft:entity_spawned");
          console.warn(`Spawned ${entityToSpawn} at ${JSON.stringify(selectedLocation)}`);
        } else {
          const distance = getDistance(shipEntity.location, selectedLocation);
          let hiveValue;
          if (distance <= 40) {
            hiveValue = Math.floor(Math.random() * (100 - 40 + 1)) + 40;
          } else {
            hiveValue = Math.floor(Math.random() * (150 - 50 + 1)) + 50;
          }

          const beeHive = OVERWORLD.spawnEntity("js:hive_ent", selectedLocation);
          const bee = OVERWORLD.spawnEntity("js:circuit_bees", selectedLocation);
          beeHive.setDynamicProperty("scrapValue", hiveValue);
          console.warn(`Spawned ${entityToSpawn} at ${JSON.stringify(selectedLocation)}`);

          let currentTotalScrapPossible = world.getDynamicProperty("totalScrapPossible");
          world.setDynamicProperty("totalScrapPossible", currentTotalScrapPossible + hiveValue);
        }
      }
    }
  }

  if (moonId == 1 || moonId >= 9) {
    let oldBirdSpawnCount = 1;
    let range = 32;
    let oldBirdBaseChance = 0.0222;
  
    if (moonId == 9) {
      oldBirdBaseChance = 0.0569;
    }
    if (moonId == 11) {
      oldBirdBaseChance = 0.45;
      oldBirdSpawnCount = Math.floor(Math.random() * 5); // 0 - 4
    }
  
    if (weather == 5) {
      oldBirdBaseChance += 0.25;
    }
  
    if (moonId == 10) {
      oldBirdBaseChance = 1;
      range = 64;
      oldBirdSpawnCount = Math.floor(Math.random() * (14 - 20 + 1)) + 20; // 14 - 20 inclusive
    }
  
    // world.sendMessage(`${oldBirdSpawnCount}`)
    for (let i = 0; i < oldBirdSpawnCount; i++) {
      const spawnOldBirdChance = Math.random(); // Generate random chance per iteration
      if (spawnOldBirdChance <= oldBirdBaseChance) {
        let selectedLocation = selectOutdoorSpawnLocation(range, 10);
        if (selectedLocation) {
          const randRot = Math.random() * 360;
          const oldBird = OVERWORLD.spawnEntity("js:old_bird", selectedLocation, { initialRotation: randRot });
          oldBird.triggerEvent("minecraft:entity_spawned");
          console.warn(`Spawned js:old_bird at ${JSON.stringify(selectedLocation)}`);
  
          // const entCount = world.getDynamicProperty('entCount_old_bird') ?? 0;
          // world.setDynamicProperty('entCount_old_bird', entCount + 1);
          // let currentPowerLevel = world.getDynamicProperty("currentOutdoorPowerLevel") ?? 0;
          // world.setDynamicProperty('currentOutdoorPowerLevel', currentPowerLevel + 3)
        }
      } else {
        console.warn(`Chance failed for js:old_bird spawn attempt ${i + 1}`);
      }
    }
  }
  
}

function getRandomCoordinateInRange(origin, range) {
  const randomOffset = () => Math.floor(Math.random() * (2 * range + 1)) - range;
  return {
    x: origin.x + randomOffset(),
    z: origin.z + randomOffset(),
  };
}

function selectOutdoorSpawnLocation(range = 64, minDistance = 12) {
  let origin = shipEntity.location;

  const moonId = world.getDynamicProperty('moon')
  if (moonId == 11) {
    origin.x += 65
  }
  if (moonId == 2) {
    origin.z -= 32
    minDistance = 26
    range = 44

  }
  if (moonId == 9) {
    range = 42
  }
  if (moonId == 10) {
    origin.z += 55
  }
  const maxAttempts = 500; // Maximum number of attempts to prevent infinite looping
  let attempts = 0;

  while (attempts < maxAttempts) {
    attempts++;

    // Get a random coordinate
    const randomCoord = getRandomCoordinateInRange(origin, range);

    // Attempt to get the topmost block at the random coordinate
    const topmostBlock = OVERWORLD.getTopmostBlock({ x: randomCoord.x, z: randomCoord.z }, 80);
    if (!topmostBlock) {
      // world.sendMessage(`No topmost block found at ${randomCoord.x}, ${randomCoord.z}`);
      continue; // Skip to the next attempt
    }

    // Get the block on top of the topmost block
    const blockOnTop = OVERWORLD.getBlock({
      x: topmostBlock.location.x,
      y: topmostBlock.location.y + 1,
      z: topmostBlock.location.z,
    });

    // Ensure blocks meet the criteria for outdoor spawning
    if (
      topmostBlock &&
      OUTDOOR_SPAWNABLE_BLOCKS.includes(topmostBlock.typeId) &&
      blockOnTop &&
      blockOnTop.typeId !== "minecraft:water"
    ) {
      // Calculate the distance from the ship
      let distanceFromShip = Math.sqrt(
        Math.pow(randomCoord.x - shipEntity.location.x, 2) + Math.pow(randomCoord.z - shipEntity.location.z, 2)
      );

      // Check if the distance meets the minimum requirement
      if (distanceFromShip >= minDistance) {
        return {
          x: randomCoord.x,
          y: topmostBlock.location.y + 1,
          z: randomCoord.z,
        };
      }
    }
  }

  // If no valid location is found after maxAttempts, return null
  // world.sendMessage("No suitable outdoor spawn location found after max attempts.");
  return null;
}



function findTopmostBlock(x, z, minY = 59, maxY = 89) {
  for (let y = minY; y <= maxY; y++) {
    const currentBlock = OVERWORLD.getBlock({ x, y, z });
    const blockAbove = OVERWORLD.getBlock({ x, y: y + 1, z });

    if (currentBlock.typeId !== "minecraft:air" && blockAbove.typeId === "minecraft:air") {
      return currentBlock;
    }
  }
  return null;
}

export function convertToTicks(minutes) {
  const ticksPerMinute = (18000 - 2000) / 980;
  return 2000 + Math.round(ticksPerMinute * minutes);
}
export function convertToTicks2(minutes) {
  const ticksPerMinute = (18000 - 14000) / 980;
  return 14000 + Math.round(ticksPerMinute * minutes);
}

function getTimePeriod(currentTime) {
  if (currentTime >= 0 && currentTime < 260) {
    return 1; // 7:40 AM - 12:00 PM
  } else if (currentTime >= 260 && currentTime < 560) {
    return 2; // 12:00 PM - 5:30 PM
  } else if (currentTime >= 560 && currentTime < 860) {
    return 3; // 5:30 PM - 10:00 PM
  } else {
    return 5; // 10:00 PM - 12:00 AM
  }
}

function display12HourTime(currentTime) {
  const adjustedTime = currentTime + 7 * 60 + 40;

  // Calculate hours and minutes
  const hours = Math.floor(adjustedTime / 60);
  const minutes = adjustedTime % 60;

  // Convert hours to 12-hour format and determine AM/PM
  const displayHours = hours % 12 || 12;
  const ampm = hours >= 12 ? "PM" : "AM";

  return `${displayHours}:${minutes.toString().padStart(2, "0")} ${ampm}${displayHours < 10 ? " " : ""}`;
}

/**
 * 
 * @param {Player} player 
 */
export function updateWeight(player) {
  const inventory = player.getComponent("inventory").container;

  // Reset hands_filled property
  player.setProperty("js:hands_filled", false);
  player.totalWeight = 0;
  player.uiManager.updateHandsFull(0)

  // Initialize inventory full flag
  let allSlotsFilled = true;

  let hasFillHandsItem = false
  for (let slot = 2; slot <= 6; slot++) {
    const item = inventory.getItem(slot);
    if (!item || !item.typeId) {
      allSlotsFilled = false; // If any slot is empty, set flag to false
      continue;
    }
    if (!ITEM_LIST[item.typeId]) continue;

    // Add the weight of the item to the player's total weight
    player.totalWeight += ITEM_LIST[item.typeId].weight;

    // Unlock all slots initially

    if (ITEM_LIST[item.typeId].fills_hands === true) {
      player.setProperty("js:item", ITEM_LIST[item.typeId].id);
      player.setProperty("js:item_type", ITEM_LIST[item.typeId].item_type);
      player.setProperty("js:hands_filled", true);
      player.uiManager.updateHandsFull(1)
      hasFillHandsItem = true
      for (let slot2 = 2; slot2 <= 6; slot2++) {
        const item = inventory.getItem(slot2);
        if (item && ITEM_LIST[item.typeId]) {
          if (slot2 !== slot) {
            item.lockMode = "slot";
          } else {
            item.lockMode = "none";
          }
          inventory.setItem(slot2, item);
        }
      }
    }
  }
  if (hasFillHandsItem == false) {
    unlockItems(player)
  }

  // Set inventory full property based on whether all slots are filled
  player.setProperty("js:inv_full", allSlotsFilled);

  player.uiManager.updateWeight(player.totalWeight)
}

export function lockItems(player) {
  const inventory = player.getComponent("inventory").container;

  for (let slot = 2; slot <= 6; slot++) {
    const item = inventory.getItem(slot);
    if (item && ITEM_LIST[item.typeId]) {
      item.lockMode = "slot";
      inventory.setItem(slot, item);
      console.warn(`eeye`);
    }
  }
}

export function unlockItems(player) {
  const inventory = player.getComponent("inventory").container;

  for (let slot = 2; slot <= 6; slot++) {
    const item = inventory.getItem(slot);
    if (item && ITEM_LIST[item.typeId]) {
      item.lockMode = "none";
      inventory.setItem(slot, item);
    }
  }
}

world.afterEvents.entityDie.subscribe((ev) => {
  const player = ev.deadEntity;

  if (!(player instanceof Player)) return;

  const nearbyPlayers = OVERWORLD.getPlayers({ location: player.location, maxDistance: 12 });
  for (let nearbyPlayer of nearbyPlayers) {
    if (nearbyPlayer == player) continue;

    changeFear(player, 0.9, true);
  }

  system.runTimeout(() => {
    updateSpectators();
  }, 10);

  system.run(() => {
    const location = player.location;
    const name = player.nameTag;
    try {
      system.clearRun(player.teleportLocation) 
      player.teleportLocation = undefined
    } catch {}

    player.uiManager.updateHud(0)
    player.removeTag("inFacility");
    player.removeTag("selected");
    player.setProperty("js:flashlight_state", 0);
    player.setProperty("js:has_jetpack", false);
    
    player.runCommand(`inputpermission set @s jump enabled`)

    console.warn(`Die ${player.causeOfDeath}`);
    
    world.setDynamicProperty('totalRunDeaths', world.getDynamicProperty('totalRunDeaths') + 1)

    player.runCommand(`scriptevent js:fog outside`);
    
    const lightningTarget = player.getDynamicProperty("lightningTarget");
    const strikeId = player.getDynamicProperty("strikeId");

    if (lightningTarget === true && strikeId && lightningStrikes[strikeId]) {
      system.clearRun(updateInterval);
    }

    if (player.causeOfDeath !== "worm" && player.causeOfDeath !== "giant" && player.causeOfDeath !== "company") {
      const body = OVERWORLD.spawnEntity("js:body_ent", location, {initialRotation: player.getRotation().y });
      // body.setRotation({ x: player.getRotation().x, y: player.getRotation().y });
      body.nameTag = name;
      body.setDynamicProperty("causeOfDeath", player.causeOfDeath);
      body.setDynamicProperty("scrapValue", 5);
      body.triggerEvent("ragdoll");
      body.dimension.spawnParticle('ding:blood_drop', { x: body.location.x, y: body.location.y + 0.01, z: body.location.z})
      
      const direction = player.getViewDirection();
      let horiStrength = -1.5;
      let vertStrength = 0.25;
      
      if (player.causeOfDeath == EntityDamageCause.lightning) {
        horiStrength = -9.5 + Math.random() * 3;
        vertStrength = 2.25 + Math.random() * 3;
      }
      
      // Create the horizontal force vector
      const horizontalForce = {
        x: direction.x * horiStrength,
        z: direction.z * horiStrength
      };
      
      body.applyKnockback(horizontalForce, vertStrength);
      

    }
    if (player.causeOfDeath == "company") {
      player.dimension.spawnParticle('ding:blood_drop', { x: player.location.x, y: player.location.y + 0.01, z: player.location.z})
    }

    player.inventory = player.getComponent("inventory").container;

    if (player.causeOfDeath !== "worm" && player.causeOfDeath !== "giant") {
      const slots = [
        player.inventory.getItem(2),
        player.inventory.getItem(3),
        player.inventory.getItem(4),
        player.inventory.getItem(5),
        player.inventory.getItem(6),
      ];

      const playerRot = player.getRotation();
      for (let index = 0; index < slots.length; index++) {
        const item = slots[index];
        if (item && !item.typeId) {
          continue;
        }
        if (item) {
          const itemId = item.typeId;
          if (ITEM_LIST[itemId]) {
            let value = item?.getDynamicProperty("scrapValue") ?? 0;
            let battery = item?.getDynamicProperty("battery") ?? 0;
            let collected = item?.getDynamicProperty("collected") ?? false;
            
            let bullets = item.getDynamicProperty('bullets') ?? 0
            let safetyOn = item.getDynamicProperty('safetyOn') ?? false
            let causeOfDeath = item.getDynamicProperty("causeOfDeath");
            let jetpackBroken = item.getDynamicProperty('jetpackBroken') ?? false

            console.warn(`JETPCK ${jetpackBroken}`)

            const spawnItemId = itemId + "_ent";
            const scrap = OVERWORLD.spawnEntity(spawnItemId, location, {initialRotation: playerRot.y + 180 });
            scrap.setDynamicProperty("scrapValue", value);
            scrap.setDynamicProperty("battery", battery);
            scrap.setDynamicProperty("collected", collected);
            // scrap.setRotation({ x: playerRot.x, y: playerRot.y + 180 });

            if (itemId == "js:shotgun") {
              scrap.setDynamicProperty("safetyOn", safetyOn);
              scrap.setDynamicProperty("bullets", bullets);
            }
            if (itemId == "js:body") {
              scrap.setDynamicProperty("causeOfDeath", causeOfDeath);
            }
            if (itemId == "js:jetpack") {
              scrap.setDynamicProperty("jetpackBroken", jetpackBroken);
            }
            console.warn(`JETPCK ${scrap.getDynamicProperty('jetpackBroken')}`)

            OVERWORLD.playSound(ITEM_LIST[itemId].sound, location, SOUND_OPTIONS);

            player.inventory.setItem(index + 2, undefined)
          }
        }
      }
    } else {
      player.runCommand(`clear @s`)
      player.runCommand(`function initializeItems`)
    }
    player.setProperty("js:hands_filled", false);
    player.setProperty("js:item", 0);
    player.setProperty("js:item_type", 0);

    updateWeight(player);
    player.runCommand(`gamemode spectator @s`);

    system.runTimeout(() => {
      player.state = "dead";
      

      const inOrbit = world.getDynamicProperty("inOrbit");

      player.teleport(location);
      player.addEffect("invisibility", 99999, { showParticles: false });
      player.uiManager.updateHud(0)

      const moonId = world.getDynamicProperty("moon");
      let shipLocation;
      for (const key of Object.keys(MAPS)) {
        if (MAPS[key].id === moonId) {
          shipLocation = MAPS[key].shipLocation;
          break;
        }
      }
      if (inOrbit == false) {
        const newLocation = {
          x: shipLocation.x - 14 + 0.5,
          y: shipLocation.y + 1,
          z: shipLocation.z + 12 + 0.5,
        };

        player.addEffect("invisibility", 99999, { showParticles: false });
        player.camera.setCamera("minecraft:free", {
          location: newLocation,
          rotation: { x: -15, y: -120 },
        });
      }
    }, 3);
  });
});



function updateSpectators() {
  const inOrbit = world.getDynamicProperty("inOrbit");

  spectatablePlayers = OVERWORLD.getPlayers({ excludeTags: ["spectator"] });
  spectators = OVERWORLD.getPlayers({ tags: ["spectator"] });

  const spectatablePlayerNames = spectatablePlayers.map((player) => player.name);
  const spectatorNames = spectators.map((player) => player.name);

  console.warn(`Spectatable players: ${JSON.stringify(spectatablePlayerNames)}`);
  console.warn(`Spectators: ${JSON.stringify(spectatorNames)}`);

  if (allPlayersAreSpectators == false && spectatablePlayers.length == 0 && inOrbit == false) {
    for (let player of worldPlayers) {
      player.spectatorIndex = undefined;
      player.addEffect("invisibility", 99999, { showParticles: false });
    }
    shipEntity.runCommand(`scriptevent js:shipLeave 1 allPlayersDead`);
    allPlayersAreSpectators = true;
    return;
  } else {
    allPlayersAreSpectators = false;
    for (let player of worldPlayers) {
      spectatorSwitching(player);
    }
  }
}

function spectatorSystem(player) {
  const playerSpectating = worldPlayers[player.spectatorIndex];
  if (playerSpectating) {
    const directionX = playerSpectating.location.x - player.location.x;
    const directionY = playerSpectating.location.y - player.location.y + 10;
    const directionZ = playerSpectating.location.z - player.location.z;

    const magnitude = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ);
    //console.warn(magnitude)
    if (magnitude > 1) {
      const normalizedDirectionX = directionX / magnitude;
      const normalizedDirectionZ = directionZ / magnitude;
    
      let horizontalStrength = 1;
      let verticalStrength = 0.5;
    
      if (directionY < 0) {
        verticalStrength = -0.5;
      }
    
      const horizontalForce = {
        x: normalizedDirectionX * horizontalStrength,
        z: normalizedDirectionZ * horizontalStrength
      };
    
      player.applyKnockback(horizontalForce, verticalStrength);
    }
    
    if (magnitude > 100) {
      player.teleport(playerSpectating.location);
    }
  }

  if (player.spectateCooldown > 0) {
    return;
  }

  if (player.spectatorIndex !== undefined) {
    if (player.isSprinting) {
      spectatorSwitching(player);
    }
  }
}

function spectatorSwitching(player) {
  spectatablePlayers = OVERWORLD.getPlayers({ excludeTags: ["spectator"] });
  spectators = OVERWORLD.getPlayers({ tags: ["spectator"] });

  const spectatablePlayerNames = spectatablePlayers.map((player) => player.name);
  const spectatorNames = spectators.map((player) => player.name);

  // console.warn(`Spectatable players: ${JSON.stringify(spectatablePlayerNames)}`);
  // console.warn(`Spectators: ${JSON.stringify(spectatorNames)}`);

  // console.warn(`curent spectate ${player.spectatorIndex}`);

  let currentlySpectatingIndex = player.spectatorIndex ?? 0;

  if (spectatablePlayers.length > 0) {
    // Increment the index
    currentlySpectatingIndex++;

    // Validate the current index and loop until a valid player is found or it wraps around
    while (
      currentlySpectatingIndex >= worldPlayers.length ||
      !spectatablePlayers.includes(worldPlayers[currentlySpectatingIndex])
    ) {
      currentlySpectatingIndex++;
      if (currentlySpectatingIndex >= worldPlayers.length) {
        currentlySpectatingIndex = 0;
      }

      // Break if we wrap around and still don't find a valid player
      if (currentlySpectatingIndex === player.spectatorIndex) {
        break;
      }
    }

    // Avoid spectating yourself
    if (worldPlayers[currentlySpectatingIndex] === player) {
      currentlySpectatingIndex++;
      if (currentlySpectatingIndex >= worldPlayers.length) {
        currentlySpectatingIndex = 0;
      }
    }
  } else {
    // If no spectatable players, reset the index
    currentlySpectatingIndex = -1;
  }

  player.spectatorIndex = currentlySpectatingIndex;
  // console.warn(`Currently Spectating ${worldPlayers[currentlySpectatingIndex].nameTag} ${currentlySpectatingIndex}`);
  player.spectateCooldown = COOLDOWN_DURATION;
}

function updateCooldown(players) {
  players.forEach((player) => {
    if (player.spectateCooldown > 0) {
      player.spectateCooldown--;
    }
  });
}

function getLocationInFrontOfPlayer(player, distance) {
  const playerLocation = player.location;
  const playerViewDirection = player.getViewDirection();

  return {
    x: playerLocation.x + playerViewDirection.x * distance,
    y: playerLocation.y + playerViewDirection.y * distance,
    z: playerLocation.z + playerViewDirection.z * distance,
  };
}

system.afterEvents.scriptEventReceive.subscribe((ev) => {
  const entity = ev.sourceEntity;
  const block = ev.sourceBlock;
  const message = ev.id;
  if (message.split(" ")[0] == "js:broadcastNoise") {
    const noiseNameInput = ev.message.split(" ")[0].toString();
    if (!SOUNDS[noiseNameInput]) return;
    let sourceLocation;
    if (entity) {
      sourceLocation = entity.location;
    }
    if (block) {
      sourceLocation = blockLocation;
    }
    const range = SOUNDS[noiseNameInput].range;
    const volume = SOUNDS[noiseNameInput].volume;
    //console.warn(`${noiseNameInput}, ${sourceLocation}, ${range}, ${volume}`)
    broadcastNoise(noiseNameInput, sourceLocation, range, volume);
  }
  

  if (entity) {
    const command = message.split(" ")[0];
    switch (command) {
      case "js:thumper_stunned":
        entity.clearVelocity();
        entity.addEffect("slowness", 20, { showParticles: false, amplifier: 5 });
        OVERWORLD.playSound("monster.thumper.stun", entity.location, SOUND_OPTIONS);
        break;
      case "js:snare_flea":
        const playerRideComponent = entity.getComponent("minecraft:rideable");
        const playerRiders = playerRideComponent.getRiders();

        const snareFlea = playerRiders[0];
        damagePlayer(entity, 10, snareFlea, EntityDamageCause.temperature);

        break;
      case "js:snare_flea_initial":
        entity.inventory = entity.getComponent("inventory").container;
        const entityLocation = entity.location;

        changeFear(entity, 1, true);
        entity.setProperty("js:flashlight_state", 0);

        const slots = [
          entity.inventory.getItem(2),
          entity.inventory.getItem(3),
          entity.inventory.getItem(4),
          entity.inventory.getItem(5),
          entity.inventory.getItem(6),
        ];

        const playerRot = entity.getRotation();
        for (let index = 0; index < slots.length; index++) {
          const item = slots[index];
          if (item && !item.typeId) {
            continue;
          }
          if (item) {
            const itemId = item.typeId;
            if (ITEM_LIST[itemId]) {
              let value = item?.getDynamicProperty("scrapValue") ?? 0;
              let battery = item?.getDynamicProperty("battery") ?? 0;
              let collected = item?.getDynamicProperty("collected") ?? false;

              const spawnId = itemId + "_ent";
              const scrap = OVERWORLD.spawnEntity(spawnId, entityLocation, { initialRotation: playerRot.y + 180});
              scrap.setDynamicProperty("scrapValue", value);
              scrap.setDynamicProperty("battery", battery);
              scrap.setDynamicProperty("collected", collected);
              // scrap.setRotation({ x: playerRot.x, y: playerRot.y + 180 });

              OVERWORLD.playSound(ITEM_LIST[itemId].sound, entityLocation, SOUND_OPTIONS);

              entity.inventory.setItem(index + 2, undefined)
            }
          }
        }

        updateWeight(entity);

        break;
      case "js:spawn_mine":
        const block = OVERWORLD.getBlock(entity.location);
        if (block.typeId !== "minecraft:air") {
          entity.remove();
        } else {
          OVERWORLD.setBlockType(entity.location, "js:mine");
        }
        break;
      case "js:coil_head_stopped":
        const nearestPlayers = OVERWORLD.getEntities({
          type: "minecraft:player",
          location: entity.location,
          maxDistance: 4,
        });
        const nearPlayers = OVERWORLD.getEntities({
          type: "minecraft:player",
          location: entity.location,
          minDistance: 4,
          maxDistance: 9,
        });

        for (let player of nearestPlayers) {
          changeFear(player, 0.9, true);
        }
        for (let player of nearPlayers) {
          changeFear(player, 0.4, true);
        }

        entity.clearVelocity()

        break;
      case "js:coil_head_unstopped":
        const direction = entity.getViewDirection();

        const horizontalForce = {
          x: direction.x * 1,
          z: direction.z * 1
        };
        
        entity.applyKnockback(horizontalForce, 0.02);
        

        break;
      case "js:bracken_found":
        const nearestPlayers2 = OVERWORLD.getPlayers({
          location: entity.location,
          maxDistance: 5,
        });
        const nearPlayers2 = OVERWORLD.getPlayers({
          location: entity.location,
          minDistance: 5,
          maxDistance: 16,
        });

        for (let player of nearestPlayers2) {
          changeFear(player, 0.6, true);
        }
        for (let player of nearPlayers2) {
          changeFear(player, 0.3, true);
        }

        break;
      case "js:thumper_aggro":
        const nearPlayers3 = OVERWORLD.getPlayers({
          location: entity.location,
          maxDistance: 15,
        });
        for (let player of nearPlayers3) {
          changeFear(player, 0.75, true);
        }
        break;
      case "js:hoarding_bug_aggro":
        if (entity.target && entity.target.typeId == "minecraft:player") {
          const nearPlayers4 = OVERWORLD.getPlayers({
            location: entity.location,
            maxDistance: 10,
          });
          for (let player of nearPlayers4) {
            changeFear(player, 0.5, true);
          }
        }
        break;
      case "js:snare_flea_drop":
        const nearPlayers5 = OVERWORLD.getPlayers({
          location: entity.location,
          maxDistance: 7,
        });
        for (let player of nearPlayers5) {
          changeFear(player, 0.5, true);
        }
        break;
      case "js:brackenAttack":
        const bracken = entity;
        let brackenTarget = bracken.target;

        if (!brackenTarget) {
          console.warn(`bracken no target`)
          return
        }

        if (brackenTarget.typeId !== "minecraft:player") {
          console.warn(`bracken target not player`)
          return
        }

        brackenTarget.inputPermissions.movementEnabled = false

        console.warn(brackenTarget.nameTag);

        const brackenRideComponent = bracken.getComponent("minecraft:rideable");
        const brackenRiders = brackenRideComponent.getRiders();

        const work = brackenRideComponent.addRider(brackenTarget);
        console.warn(`thing ${work}`)
        //brackenTarget.runCommand(`ride @s start_riding @e[type=js:bracken,c=1,r=3.5]`);
        brackenTarget.attackedTimeout = system.runTimeout(() => {
          damagePlayer(brackenTarget, 100, bracken, EntityDamageCause.charging);
          brackenTarget.triggerEvent('not_in_facility')
          system.runTimeout(() => {
            brackenTarget.inputPermissions.movementEnabled = true
            const body = OVERWORLD.getEntities({
              type: "js:body_ent",
              location: bracken.location,
              maxDistance: 6,
              closest: 1,
            });
            brackenRideComponent.addRider(body[0]);
          }, 5);
        }, 20 * 0.96);

        break;
      case "js:brackenDropBody":
        const bracken2 = entity;
        const brackenRideComponent2 = bracken2.getComponent("minecraft:rideable");
        const brackenRiders2 = brackenRideComponent2.getRiders();

        if (brackenRiders2[0] instanceof Entity) {
          brackenRideComponent2.ejectRider(brackenRiders2[0]);
        }
        break;
      case "js:brackenHit":
        entity.setProperty("js:anger", 500);
        break;
      case "js:brackenLookedAt":
        entity.retreatTimer = 460;
        break;
      case "js:eyelessDogInvestigateFinish":
        const dogLoc1 = {
          x: entity.location.x - 3,
          y: entity.location.y - 3,
          z: entity.location.z - 3,
        };
        const dogLoc2 = {
          x: entity.location.x + 3,
          y: entity.location.y + 3,
          z: entity.location.z + 3,
        };
        const fillVol = new BlockVolume(dogLoc1, dogLoc2);
        OVERWORLD.fillBlocks(fillVol, "air", { blockFilter: { includeTypes: [`js:sound_marker`] } });

        break;

      case "js:eyelessDogLunge":
        console.warn(`lunging`);
        entity.playAnimation('animation.eyeless_dog.leap')
        entity.addEffect("slowness", 2.2083 * 20, { amplifier: 255, showParticles: false });
        const viewDirection = entity.getViewDirection();
        entity.applyImpulse({
          x: viewDirection.x * 3,
          y: viewDirection.y * 1.35,
          z: viewDirection.z * 3,
        });

        break;
      case "js:eyelessDogAngry":
        entity.playAnimation('animation.eyeless_dog.roar')
        entity.addEffect("slowness", 2.25 * 20, { amplifier: 255, showParticles: false });
        system.runTimeout(() => {
          entity.triggerEvent("enrage");
        }, 2.25 * 20);
        break;

      case "js:eyelessDogAttack":
        entity.addEffect("slowness", 5.5833 * 20, { amplifier: 255, showParticles: false });
        const nearbyEntities = OVERWORLD.getEntities({ location: entity.location, maxDistance: 3 });

        const dogAttackDamage = ENTITIES["js:eyeless_dog"].damage;
        const dogAttackType = ENTITIES["js:eyeless_dog"].damage_type;

        for (let ent of nearbyEntities) {
          if (ent == entity) continue;
          const item = ent.getComponent("minecraft:item")?.itemStack;
          if (item || ITEM_LIST[ent.typeId]) continue;

          if (ent instanceof Player) {
            damagePlayer(ent, dogAttackDamage, entity, dogAttackType);
          } else {
            ent.applyDamage(1, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: entity,
            });
          }
        }

        break;

      case "js:spawnScrap":
        const scrapId = ev.message.split(" ")[0].toString();
        const value = ev.message.split(" ")[1];
        const scrapSpawned = OVERWORLD.spawnEntity(scrapId, entity.location);
        scrapSpawned.setDynamicProperty("scrapValue", parseInt(value));
        scrapSpawned.setDynamicProperty("collected", false);
        break;
      case "js:spawnBee":
        const hiveValue = Math.floor(Math.random() * (150 - 40 + 1)) + 40;
        const beeHive = OVERWORLD.spawnEntity("js:hive_ent", entity.location);
        const bee = OVERWORLD.spawnEntity("js:circuit_bees", entity.location);
        beeHive.setDynamicProperty("scrapValue", parseInt(hiveValue));
        break;
      case "js:openDoor":
        openDoors(entity);
        break;
      case "js:dropped_by_hoarding_bug":
        if (ITEM_LIST[entity.typeId.split("_ent")[0]]) {
          OVERWORLD.playSound(ITEM_LIST[entity.typeId.split("_ent")[0]].sound, entity.location, SOUND_OPTIONS);
        }
        entity.runCommand(`ride @s stop_riding`);
        break;
      case "js:hoarding_bug_drop_items":
        const rideComponent = entity.getComponent("minecraft:rideable");
        const riders = rideComponent.getRiders();
        for (let rider of riders) {
          const riderTypeId = rider.typeId.split("_ent")[0];
          if (ITEM_LIST[riderTypeId]) {
            OVERWORLD.playSound(ITEM_LIST[riderTypeId].sound, rider.location, SOUND_OPTIONS);
            rideComponent.ejectRider(rider);
          }
        }
        break;
      case "js:hoarding_bug_drop_items_aggro":
        const rideComponent2 = entity.getComponent("minecraft:rideable");
        const riders2 = rideComponent2.getRiders();
        for (let rider of riders2) {
          const riderTypeId = rider.typeId.split("_ent")[0];
          if (ITEM_LIST[riderTypeId]) {
            OVERWORLD.playSound(ITEM_LIST[riderTypeId].sound, rider.location, SOUND_OPTIONS);
            rider.removeTag("stashed");
            rideComponent2.ejectRider(rider);
          }
        }
        break;

      /*
            case 'js:scan':

                const test = entity.getEntitiesFromViewDirection()
                for (let ent of test) {
                    console.warn(ent.typeId)
                }


                const playerLocation = {
                    x: entity.location.x,
                    y: entity.location.y + 0.1,
                    z: entity.location.z
                }
                const entities = OVERWORLD.getEntities({ location: entity.location, maxDistance: 8 })
                let totalValue = 0
                for (let entity of entities) {
                    if (!ITEM_LIST[entity.typeId] && !ENTITIES[entity.typeId]) continue

                    const scannedEntityLocation = entity.location
                    const directionVector = getDirectionVector(playerLocation, scannedEntityLocation);
                   // console.warn(JSON.stringify(directionVector))

                    const entitiesInLineOfSight = OVERWORLD.getEntitiesFromRay(playerLocation, directionVector, { maxDistance: 8 }) 
                    console.warn(JSON.stringify(entitiesInLineOfSight))
                    let inSight = false
                    for (let ent of entitiesInLineOfSight) {
                        if (ent.entity == entity) {
                            inSight = true
                            break;
                        }
                    }
                    if (inSight == false) continue

                    if (ITEM_LIST[entity.typeId]) {
                        const value = entity.getDynamicProperty('scrapValue')
                        totalValue += value
                    
                        const display = OVERWORLD.spawnEntity("js:scan_ent", { x: entity.location.x, y: entity.location.y - 0.25, z: entity.location.z})
                        
                        let name = '§a' + ITEM_LIST[entity.typeId].name + ': ' + value

                        if (entity.typeId == "js:apparatus" && entity.getDynamicProperty('collected') == false) {
                            name = '§a' + ITEM_LIST[entity.typeId].name + ': ' + '???'
                        }

                        if (entity.typeId == "js:body") {
                            const deathCause = DEATH_CAUSES[entity.getDynamicProperty('causeOfDeath')]
                            name = '§a' + entity.nameTag + "'s Body: " + value + `\n§aCause of Death: ${deathCause}`
                        }
                        display.nameTag = name.toString()
                        
                        OVERWORLD.spawnParticle("ding:scan", { x: entity.location.x, y: entity.location.y + 0.25, z: entity.location.z})
                        display.addEffect("levitation", 5, { amplifier: 3, showParticles: false });
                        system.runTimeout(() => {
                            display.clearVelocity()
                        },5)
                    } else if (ENTITIES[entity.typeId] && !(entity instanceof Player)) {

                        const display = OVERWORLD.spawnEntity("js:scan_ent", { x: entity.location.x, y: entity.location.y + 0.75, z: entity.location.z})

                        let name = '§c' + ENTITIES[entity.typeId].display_name
                        display.nameTag = name.toString()
                        
                        OVERWORLD.spawnParticle("ding:scan2", { x: entity.location.x, y: entity.location.y + 1.25, z: entity.location.z})
                        display.addEffect("levitation", 5, { amplifier: 3, showParticles: false });
                        system.runTimeout(() => {
                            display.clearVelocity()
                        },5)
                        
                    }

                }
                entity.runCommand(`say Total Scanned: ${totalValue}`)
                
                break;
                */
      case "js:playerJump":
        if (entity.stamina >= 20) {
          entity.stamina -= 8;
        }
        break;
      case "js:updateSpectators":
        if (!entity.hasTag("cutscene")) {
          system.runTimeout(() => {
            updateSpectators();
          }, 20);
        }
        break;
      case "js:exitSpectator":
        entity.uiManager.updateHud(1)
        system.run(() => {
          entity.camera.clear();
        });
        break;
      /*
            case 'js:swingShovel': 
                const player = entity;
                let damage
            
                const inOrbit = world.getDynamicProperty('inOrbit')
                const locationInFront = getLocationInFrontOfPlayer(player, 1.5);
                const hits = OVERWORLD.getEntities({ location: locationInFront, maxDistance: 1.7 });

                console.warn(`Entities found: ${hits.length}`);

                hits.forEach(hit => {
                    const target = hit
                    const hitLocation = target.location
                    const item = target.getComponent("minecraft:item")?.itemStack
                    
                    const familyComponent = hit.getComponent("minecraft:type_family")
                    if (familyComponent && familyComponent.hasTypeFamily("scrap")) {
                        return;
                    }

                    if (target !== player && !item) {
                        if (!(target instanceof Player)) {
                            damage = 1;

                                target.applyDamage(damage, {
                                    cause: EntityDamageCause.entityAttack,
                                    damagingEntity: player
                                });
                            console.warn(`Dealt ${damage} damage to ${target.typeId} `);
                        } else {
                            damage = 10
                            if (inOrbit == false) {
                                damagePlayer(target,damage,player, EntityDamageCause.entityAttack)
                            }
                        }
                        
                        if (ENTITIES[target.typeId]) {
                            OVERWORLD.playSound(ENTITIES[target.typeId].shovel_sound, hitLocation, SOUND_OPTIONS)
                        } else {
                            OVERWORLD.playSound("items.shovel.hit", hitLocation, SOUND_OPTIONS)
                        }
                    }
                });
                
                const block = player.getBlockFromViewDirection({ includeLiquidBlocks: true, includePassableBlocks: true, maxDistance: 2 });
    
                console.warn(block && block.block.typeId)
                if (block && BLOCKS[block.block.typeId]) {
                    OVERWORLD.playSound(BLOCKS[block.block.typeId].hit_sound, block.block.location, SOUND_OPTIONS)
                }
                if (block && block.block.typeId == "minecraft:web") {
                    OVERWORLD.setBlockType(block.block.location, "minecraft:air")
                    player.runCommand(`damage @e[type=js:bunker_spider] 0 entity_attack entity @s`)
                }

                break;
                */
      case "js:entityAttack":
        const attacker = entity;

        if (attacker.typeId == "js:nutcracker") {
          attacker.playAnimation('animation.nutcracker.kick')
          const targetHit = attacker.dimension.getPlayers({maxDistance: 3, closest: 1, location: attacker.location})[0];

          const attackDamage = ENTITIES[attacker.typeId].damage;
          const attackType = ENTITIES[attacker.typeId].damage_type;
          damagePlayer(targetHit, attackDamage, attacker, attackType);

        } else {
          const targetHit = attacker.target;

          const attackDamage = ENTITIES[attacker.typeId].damage;
          const attackType = ENTITIES[attacker.typeId].damage_type;
          damagePlayer(targetHit, attackDamage, attacker, attackType);
        }
        break;

      case "js:setTime":
        const input = ev.message;
        console.warn(input);
        world.setDynamicProperty("time", parseInt(input));
        break;
      case "js:spawnEntities":
        let currentTime = world.getDynamicProperty("time");
        entitySpawns(currentTime);
        break;

      case "js:setDay":
        const input2 = ev.message;
        console.warn(input2);
        world.setDynamicProperty("day", parseInt(input2));
        break;

      case "js:spawnWeb":
        entity.runCommand(`fill ~~~ ~~~ web replace air`);
        break;
      case "js:spawnDoor":
        const doorParams = ev.message;
        let location = entity.location;
        let doorType = doorParams.split(" ")[0].toString() ?? "js:facility_door";
        let direction2 = doorParams.split(" ")[1].toString() ?? "north";
        let locked2 = doorParams.split(" ")[2] ?? false;
        let inverse = doorParams.split(" ")[3] ?? false;
        let fireExitNumber = doorParams.split(" ")[4] ?? 0;

        spawnDoor(location, doorType, direction2, locked2, inverse, fireExitNumber);
        break;
      case "js:fired":
        OVERWORLD.runCommand(`playsound game.profit.fired @a`);
        OVERWORLD.runCommand(`playsound game.profit.alarm @a`);
        world.setTimeOfDay(18000);
        worldPlayers.forEach((player) => {
          player.triggerEvent("fired");
          player.triggerEvent("spectator");
          player.runCommand(`fill ~-12 ~-12 ~-12 ~12 ~12 ~12 air replace ding:terminal_block`);
          player.runCommand(`fill ~-12 ~-12 ~-12 ~12 ~12 ~12 air replace ding:ship_lever`);
          uiManager.closeAllForms(player);
          player.setProperty("js:player_animation", 0);
        });
        system.runTimeout(() => {
          OVERWORLD.runCommand(`playsound misc.ship.eject @a`);
          OVERWORLD.runCommand(`playsound music.sucked_into_space @a`);
          shipEntity.setProperty("js:door", 1);
          shipEntity.runCommand(`fill ~2 ~44 ~-39 ~-2 ~47 ~-39 air replace ding:ship_door_collision`);

          fired = true;
          system.runTimeout(() => {
            fired = false;
            worldPlayers.forEach((player) => {
              player.triggerEvent("reset");
              player.triggerEvent("not_spectator");
              player.runCommand(`clear @s`)
              player.runCommand(`function playerInitialize`);
            });
            system.run(() => {
              const daysSurvived = world.getDynamicProperty('daysSurvived')
              const quotasFulfilled = world.getDynamicProperty('quotasFulfilled')
              const totalRunScrapCollected = world.getDynamicProperty('totalRunScrapCollected')
              const totalRunDeaths = world.getDynamicProperty('totalRunDeaths')
              world.sendMessage(`§bStats:`)
              world.sendMessage(`§b>> Days on the job: ${daysSurvived}`)
              world.sendMessage(`§b>> Scrap value collected: ${totalRunScrapCollected}`)
              world.sendMessage(`§b>> Deaths: ${totalRunDeaths}`)
              world.sendMessage(`§b>> Quotas Fulfilled: ${quotasFulfilled}`)

              OVERWORLD.runCommand(`execute as @p at @s run function reset`);
              console.warn(`setup`);
            });
          }, 9 * 20);
        }, 9.5 * 20);
        break;
      case "js:dropshipLand":
        const dropshipNearEntities = OVERWORLD.getEntities({
          type: "minecraft:player",
          location: entity.location,
          maxDistance: 2,
        });
        for (let ent of dropshipNearEntities) {
          if (ent.typeId == "minecraft:player") {
            damagePlayer(ent, 100, entity, EntityDamageCause.anvil);
          }
        }
        break;
      case "js:dropshipSound":
        const range = SOUNDS["dropship"].range;
        const volume = SOUNDS["dropship"].volume;
        broadcastNoise("dropship", entity.location, range, volume);

        break;

      case "js:activate_spider":
        for (let spider of OVERWORLD.getEntities({ type: "js:bunker_spider" })) {
          if (!spider.target) {
            spider.applyDamage(1, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: entity,
            });
            const health = spider.getComponent("minecraft:health");
            health.setCurrentValue(health.currentValue + 1);
          }
        }

        break;

      case "js:using_reset":
          const SKIPPED_BLOCKS = [
            "js:facility_door",
            "js:fire_exit_door",
            "js:main_door",
            "ding:ship_lever",
            "ding:terminal_block"
          ]
          const currentLoading = loading.getScore(entity);
          const blockRaycast = entity.getBlockFromViewDirection({ maxDistance: 8 });
          const resetUsingRaycastBlock = blockRaycast?.block;
          if (!resetUsingRaycastBlock || (resetUsingRaycastBlock && !SKIPPED_BLOCKS.includes(resetUsingRaycastBlock.typeId))) {
            if (currentLoading <= 960) {
              loading.addScore(entity, 40);
              const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
              entity.uiManager.updateLoading(scaledScore)
            } else {
              loading.setScore(entity, 0);
              const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
              entity.uiManager.updateLoading(scaledScore)
              entity.runCommand('function reset')
              world.sendMessage(`§9${entity.nameTag} reset the map.`)
            }
          }
      
        break;

      case "js:not_using_reset":
        loading.setScore(entity, 0);
        const scaledScore = Math.min(Math.floor(0 / 10), 100);
        entity.uiManager.updateLoading(scaledScore)
        let middleText = '@'
        entity.uiManager.updateMiddleText(middleText)
        break;



      case "js:using_walkie":
        const walkieState = entity.getProperty("js:walkie_state");

        if (walkieState == 1) {
          entity.setProperty("js:walkie_state", 2);

          OVERWORLD.playSound("items.walkie.transmission_on", entity.location, SOUND_OPTIONS);
        }
        break;

      case "js:not_using_walkie":
        const walkieState2 = entity.getProperty("js:walkie_state");

        if (walkieState2 == 2) {
          entity.setProperty("js:walkie_state", 1);

          OVERWORLD.playSound("items.walkie.transmission_off", entity.location, SOUND_OPTIONS);
        }
        break;

      case "js:toggle_walkie":
        const player = entity;
        const inventory = player.getComponent("minecraft:inventory").container;
        const item = inventory.getItem(player.selectedSlotIndex);

        if (player.walkieDeplete) {
          system.clearRun(player.walkieDeplete);
        }

        //  console.warn(`Battery ${item.getDynamicProperty('battery')}`)

        if (item.getDynamicProperty("battery") == undefined) {
          let batteryLife = ITEM_LIST["js:walkie"].battery_life;

          item.setDynamicProperty("battery", batteryLife);
          inventory.setItem(player.walkieSlot, item);

          //   console.warn(`New Battery ${item.getDynamicProperty('battery')} initialised`)
        }

        if (item.getDynamicProperty("battery") > 0) {
          if (player.getProperty("js:walkie_state") == 0) {
            player.setProperty("js:walkie_state", 1);
          } else {
            player.setProperty("js:walkie_state", 0);
          }

          //for some reason the value here is inversed.
          if (player.getProperty("js:walkie_state") == 0) {
            player.playSound("items.walkie.on", SOUND_OPTIONS);
            //   console.warn(`ON`)

            const unclampedEnergyLevel = Math.ceil(
              (item.getDynamicProperty("battery") / ITEM_LIST[item.typeId].battery_life) * 100
            );
            player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
            player.setProperty("js:energy", player.energyLevel);

            player.walkieDeplete = system.runInterval(() => {
              let walkieItem = inventory.getItem(player.walkieSlot);
              if (walkieItem == undefined) {
                player.playSound("items.walkie.off", SOUND_OPTIONS);
                player.setProperty("js:walkie_state", 0);
                system.clearRun(player.walkieDeplete);
              }

              let currentEnergy = walkieItem.getDynamicProperty("battery") ?? 0;

              let mainHandItem;
              try {
                mainHandItem = inventory.getItem(player.selectedSlotIndex);
              } catch { }

              // Check if the walkie is in the main hand
              if (mainHandItem && mainHandItem.typeId === "js:walkie") {
                // Deplete the energy in memory
                currentEnergy = currentEnergy > 0 ? currentEnergy - 1 : 0;
              } else {
                // Deplete the energy in dynamic property and update the inventory
                currentEnergy = currentEnergy > 0 ? currentEnergy - 1 : 0;
                walkieItem.setDynamicProperty("battery", currentEnergy);
                inventory.setItem(player.walkieSlot, walkieItem);
              }

              if (currentEnergy == 0) {
                player.playSound("items.walkie.off", SOUND_OPTIONS);
                player.setProperty("js:walkie_state", 0);
                system.clearRun(player.walkieDeplete);
              }

              const unclampedEnergyLevel = Math.ceil((currentEnergy / ITEM_LIST[item.typeId].battery_life) * 100);
              player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);

              player.setProperty("js:energy", player.energyLevel);
              //   console.warn(`${flashlightItem.typeId} ${currentEnergy}`)
            }, 20);
          }
          if (player.getProperty("js:walkie_state") == 1) {
            //     console.warn(`OFF`)

            system.clearRun(player.walkieDeplete);
            player.setProperty("js:walkie_state", 0);
            player.playSound("items.walkie.off", SOUND_OPTIONS);
            player.energyLevel = 0;
            player.setProperty("js:energy", player.energyLevel);
          }
        } else {
          //  console.warn(`Out of batt`)
          player.setProperty("js:walkie_state", 0);
          player.playSound("items.walkie.off", SOUND_OPTIONS);
          player.energyLevel = 0;
          player.setProperty("js:energy", player.energyLevel);
        }

        break;

      default:
        break;
    }
  }
  if (block) {
    const blockLocation = block.location;
    const command = message.split(" ")[0];
    switch (command) {
      case "js:toggle_door":
        OVERWORLD.playSound("buttons.door.press", blockLocation, SOUND_OPTIONS);

        const inOrbit = world.getDynamicProperty("inOrbit");
        if (inOrbit == true) return;
        const door = shipEntity.getProperty("js:door");

        if (door == 0) {
          block.setPermutation(block.permutation.withState("ding:cooldown", 1));
          shipEntity.setProperty("js:door", 1);
        } else {
          block.setPermutation(block.permutation.withState("ding:cooldown", 1));
          shipEntity.setProperty("js:door", 0);
        }
        break;
      default:
        break;
    }
  }
});

export function spawnDoor(location, doorType, direction, locked, inverse, fireExitNumber) {
  console.warn(
    `${JSON.stringify(location)} ${doorType} Dir ${direction} locked ${locked} Inv ${inverse} fire ${fireExitNumber}`
  );
  const facilityType = world.getDynamicProperty('facilityType')
  let doorModelType = 0

  if (facilityType == "facility") {
    doorModelType = 0
  } else if (facilityType == "mansion") {
    doorModelType = 1
  }

  if (doorType == "js:facility_door") {
    OVERWORLD.runCommand(
      `setblock ${location.x} ${location.y} ${location.z} js:facility_door ["js:locked"=${locked},"js:cooldown"=false,"js:open_bit"=false,"js:upper_block_bit"=false,"minecraft:cardinal_direction"="${direction}","js:door_type"=${doorModelType}]`
    );

    OVERWORLD.runCommand(
      `setblock ${location.x} ${location.y + 1} ${location.z
      } ${doorType} ["js:locked"=${locked},"js:cooldown"=false,"js:open_bit"=false,"js:upper_block_bit"=true,"minecraft:cardinal_direction"="${direction}","js:door_type"=${doorModelType}]`
    );
  }
  
  if (doorType == "js:main_door") {
    OVERWORLD.runCommand(
      `setblock ${location.x} ${location.y} ${location.z} js:main_door ["js:inversed"=${inverse},"js:open_bit"=false,"js:upper_block_bit"=false,"minecraft:cardinal_direction"="${direction}","js:door_type"=${doorModelType}]`
    );

    OVERWORLD.runCommand(
      `setblock ${location.x} ${location.y + 1} ${location.z
      } js:main_door ["js:inversed"=${inverse},"js:open_bit"=false,"js:upper_block_bit"=true,"minecraft:cardinal_direction"="${direction}","js:door_type"=${doorModelType}]`
    );
  }

  if (doorType == "js:fire_exit_door") {
    OVERWORLD.runCommand(
      `setblock ${location.x} ${location.y} ${location.z} js:fire_exit_door ["js:open_bit"=false,"js:upper_block_bit"=false,"minecraft:cardinal_direction"="${direction}","js:fire_exit_number"=${fireExitNumber}]`
    );

    OVERWORLD.runCommand(
      `setblock ${location.x} ${location.y + 1} ${location.z
      } js:fire_exit_door ["js:open_bit"=false,"js:upper_block_bit"=true,"minecraft:cardinal_direction"="${direction}","js:fire_exit_number"=${fireExitNumber}]`
    );
  }
}



world.beforeEvents.playerInteractWithEntity.subscribe((ev) => {
  system.run(() => {
    const entity = ev.target;

    const entityId = entity.typeId;
    if (entityId == "js:nutcracker_dead") {
      const shotgunTaken = entity.getProperty('js:shotgun_taken')
      if (shotgunTaken == false) {
        const player = ev.player;
        const playerLocation = player.location;
        const entityLocation = entity.location;
        const distance = getDistance(playerLocation, entityLocation);
        if (distance >= 3) return;

        const handsFilled = player.getProperty("js:hands_filled");
        const itemType = player.getProperty("js:item_type");
        const invFull = player.getProperty("js:inv_full");
        const inventory = player.getComponent("minecraft:inventory").container;
        const itemId = "js:shotgun"
    
        if (handsFilled == 0 && itemType < 2 && invFull == false) {
          const item = new ItemStack(itemId);
          const value = 60
          const collected = entity.getDynamicProperty("collected") ?? false;
          item.setDynamicProperty("scrapValue", value);
          
          const bullets = entity.getProperty('js:bullets') ?? 0
          console.warn(bullets)
          item.setDynamicProperty('bullets', bullets)
          item.setDynamicProperty('safetyOn', false)


          inventory.addItem(item);
          entity.setProperty('js:shotgun_taken', true)
        }
        if (handsFilled == 1) {
          player.sendMessage(`§b[!] Hands Full!`);
          player.playSound("misc.tips_hud", SOUND_OPTIONS);
        }
        if (invFull == true) {
          player.sendMessage(`§b[!] Inventory Full!`);
          player.playSound("misc.tips_hud", SOUND_OPTIONS);
        }
        updateWeight(player);
        system.runTimeout(() => {
          updateWeight(player);
        }, 5);
      }
    }
  })
})

const skipPickUpInteractionItems = [
  "js:scan", "js:reset", "js:flashlight", "js:pro_flashlight"
]
world.beforeEvents.playerInteractWithEntity.subscribe((ev) => {
  system.run(() => {
    const entity = ev.target;

    const entityId = entity.typeId;
    const itemId = entity.typeId.split("_ent")[0];
    if (!ITEM_LIST[itemId]) return;
    const player = ev.player;
    const playerLocation = player.location;
    const entityLocation = entity.location;
    const distance = getDistance(playerLocation, entityLocation);
    if (distance >= 3) return;

    const handsFilled = player.getProperty("js:hands_filled");
    const itemType = player.getProperty("js:item_type");
    const invFull = player.getProperty("js:inv_full");
    const inventory = player.getComponent("minecraft:inventory").container;
    
    console.warn(inventory.getItem(player.selectedSlotIndex)?.typeId)
    if (skipPickUpInteractionItems.includes(inventory.getItem(player.selectedSlotIndex)?.typeId)) return

    if (handsFilled == 0 && itemType < 2 && invFull == false) {
      const item = new ItemStack(itemId);
      const value = entity.getDynamicProperty("scrapValue");
      const collected = entity.getDynamicProperty("collected") ?? false;
      item.setDynamicProperty("scrapValue", value);

      if (ITEM_LIST[itemId].battery_life) {
        const battery = entity.getDynamicProperty("battery");
        if (!battery && battery !== 0) {
          item.setDynamicProperty("battery", ITEM_LIST[itemId].battery_life);
        } else {
          item.setDynamicProperty("battery", battery);
        }
      }
      //console.warn(`${item.getDynamicProperty('scrapValue')}`)

      if (itemId == "js:body") {
        const name = entity.nameTag;
        console.warn(name);
        item.setDynamicProperty("name", name);
        const causeOfDeath = entity.getDynamicProperty("causeOfDeath");
        item.setDynamicProperty("causeOfDeath", causeOfDeath);
      }
      if (itemId == "js:boombox") {
        stopLoopedSound("music.boomblock");
        stopLoopedSound("music.icabobs_funk");
        entity.runCommand(`stopsound @a[r=40] music.icabobs_funk`);
        entity.runCommand(`stopsound @a[r=40] music.boomblock`);
        if (entity.boomboxDeplete) {
          system.clearRun(entity.batteryDeplete);
        }
      }
      if (itemId == "js:shotgun") {
        const bullets = entity.getDynamicProperty('bullets') ?? 0
        const safety = entity.getDynamicProperty('safetyOn') ?? false
        item.setDynamicProperty('bullets', bullets)
        item.setDynamicProperty('safetyOn', safety)
        console.warn(bullets)
        console.warn(safety)
      }
      if (itemId == "js:jetpack") {
        const jetpackBroken = entity.getDynamicProperty('jetpackBroken') ?? false
        item.setDynamicProperty('jetpackBroken', jetpackBroken)
        console.warn(jetpackBroken)
      }

      if (itemId == "js:hive") {
        /*
              const vol = new BlockVolume({
                  x: entity.location.x,
                  y: -63,
                  z: entity.location.z
              }, {
                  x: entity.location.x,
                  y: 319,
                  z: entity.location.z
              })
              OVERWORLD.fillBlocks(vol, "air", { blockFilter: { includeTypes: [`js:bee_marker`] }})*/

        const bees = OVERWORLD.getEntities({ type: "js:circuit_bees", location: entity.location, closest: 1 });
        for (let bee of bees) {
          bee.applyDamage(1, {
            cause: EntityDamageCause.void,
            damagingEntity: player,
          });
          const health = bee.getComponent("minecraft:health");
          health.setCurrentValue(health.effectiveMax);
          bee.runCommand(`ride @s stop_riding`);
          bee.triggerEvent("angry");
        }
      }

      if (itemId == "js:apparatus" && entity.hasTag("appActivate")) {
        item.setDynamicProperty("scrapValue", 80);
        OVERWORLD.playSound("items.apparatus.disconnect", player.location, SOUND_OPTIONS);

        const chance = Math.random() * 100;
        if (chance <= 70) {
          world.setDynamicProperty("minSpawnOverride", 2);
        }

        worldPlayers.forEach((player) => {
          OVERWORLD.playSound("items.apparatus.blackout", player.location, SOUND_OPTIONS);
          player.runCommand(`stopsound @s items.apparatus.ambience`);
        });
        system.runTimeout(() => {
          const lightMap = new Map(
            world.getDynamicProperty("lights") ? JSON.parse(world.getDynamicProperty("lights")) : []
          );

          for (const [key, { lightLocation }] of lightMap.entries()) {
            const light = OVERWORLD.getBlock(lightLocation);

            light.setPermutation(light.permutation.withState("js:on", false));
            // OVERWORLD.runCommand(
            //   `setblock ${lightLocation.x} ${lightLocation.y} ${lightLocation.z} ding:ceiling_lamp["js:on"=false]`
            // );
          }
          worldPlayers.forEach((player) => {
            OVERWORLD.playSound("items.apparatus.alert", player.location, SOUND_OPTIONS);
          });

          const oldBirds = OVERWORLD.getEntities({ type: "js:old_bird" });
          for (let oldBird of oldBirds) {
            if (oldBird.getProperty("js:state") == 0) {
              oldBird.triggerEvent("activate");
            }
          }
        }, 2 * 20);
      }

      if (entity.hasTag("stashed")) {
        const owner = entity.getDynamicProperty("owner") ?? undefined;
        if (owner) {
          let hoardingBugToTarget;
          const entities = OVERWORLD.getEntities({ type: "js:hoarding_bug" });
          for (let ent of entities) {
            if (ent.id == owner) {
              hoardingBugToTarget = ent;
              break;
            }
          }
          if (hoardingBugToTarget) {
            hoardingBugToTarget.applyDamage(1, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: player,
            });
            const health = hoardingBugToTarget.getComponent("minecraft:health");
            health.setCurrentValue(health.currentValue + 1);
            console.warn(`Damaged ${hoardingBugToTarget.typeId}`);
          }
        }
      }

      item.setDynamicProperty("collected", collected);

      inventory.addItem(item);

      let jetpackCount = 0
      for (let slot = 2; slot <= 6; slot++) {
        const item = inventory.getItem(slot);
        if (item && item.typeId == "js:jetpack") {
          jetpackCount++;
        }
      }
      if (jetpackCount > 0) {
        player.setProperty('js:has_jetpack', true)
      } else {
        player.setProperty('js:has_jetpack', false)
      }

      const lightningTarget = entity.getDynamicProperty("lightningTarget");
      const strikeId = entity.getDynamicProperty("strikeId");

      if (lightningTarget === true && strikeId && lightningStrikes[strikeId]) {
        lightningStrikes[strikeId].location = player.location;
        lightningStrikes[strikeId].targetEntity = player;
        lightningStrikes[strikeId].inventorySlot = findItemSlotInPlayerInventory(player, item);
        console.warn(lightningStrikes[strikeId].inventorySlot);
        const targetEntity = lightningStrikes[strikeId].targetEntity;
        const inventorySlot = lightningStrikes[strikeId].inventorySlot;
        if (targetEntity instanceof Player) {
          let player = targetEntity;

          // Set a custom tag on the item if it's associated with a player
          if (inventorySlot !== null) {
            const item = player.getComponent("inventory").container.getItem(inventorySlot);
            if (item) {
              item.setDynamicProperty("lightningTarget", true);
              item.setDynamicProperty("strikeId", strikeId); // Set strikeId on the item
              player.getComponent("inventory").container.setItem(inventorySlot, item);
            }
          }


          // Periodically update the lightning strike location based on the player's movement
          updateInterval = system.runInterval(() => {
            lightningStrikes[strikeId].location = player.location;
          });

          // Stop tracking when the player no longer holds the item or after a timeout
          system.runTimeout(() => {
            system.clearRun(updateInterval);
          }, 10 * 20);
        }

        console.warn(`Lightning strike updated to follow player at ${JSON.stringify(player.location)}`);
      }

      function findItemSlotInPlayerInventory(player, item) {
        const inventory = player.getComponent("inventory").container;
        for (let slot = 2; slot < 6; slot++) {
          const currentItem = inventory.getItem(slot);
          console.warn(currentItem.typeId);
          if (currentItem && currentItem.typeId === item.typeId) {
            return slot; // Return the slot where the item was found
          }
        }
        return null; // Return null if the item is not found in the inventory
      }

      OVERWORLD.playSound(ITEM_LIST[itemId].pick_up_sound, entity.location, SOUND_OPTIONS);

      entity.triggerEvent("js:despawn");
    }
    if (handsFilled == 1) {
      player.sendMessage(`§b[!] Hands Full!`);
      player.playSound("misc.tips_hud", SOUND_OPTIONS);
    }
    if (invFull == true) {
      player.sendMessage(`§b[!] Inventory Full!`);
      player.playSound("misc.tips_hud", SOUND_OPTIONS);
    }
    updateWeight(player);
    system.runTimeout(() => {
      updateWeight(player);
    }, 5);
  })
});

world.afterEvents.entitySpawn.subscribe((event) => {
  const familyComponent = event.entity.getComponent("minecraft:type_family");
  if (familyComponent && familyComponent.hasTypeFamily("monster")) {
    event.entity.openingDoor = false;
  }
  const eventEntity = event.entity

  const item = eventEntity.getComponent("minecraft:item")?.itemStack;
  if (item) {
    dropItem(eventEntity, item, true)
  }
});

export function dropItem(eventEntity, item, isDroppedItem) {
  const itemId = item.typeId;
  const entityItemId = itemId + "_ent";

  console.warn(itemId);

  const itemLocation = eventEntity.location;
  let owner
  if (isDroppedItem == true) {
    const nearbyItemEntities = OVERWORLD.getEntities({
      location: itemLocation,
      maxDistance: 2,
      closest: 1,
      type: "minecraft:player",
    });
    owner = nearbyItemEntities[0]
  } else {
    owner = eventEntity
  }

  if (ITEM_LIST[itemId]) {
    let player
    let playerRot
    let entity
    let initialRotation
    if (isDroppedItem == true) {
      const players = OVERWORLD.getPlayers({ closest: 1, location: itemLocation });

      if (players.length > 0) {
        player = players[0];
        playerRot = player.getRotation();

        if (itemId == "js:apparatus") {
          initialRotation = playerRot.y + 270
        } else {
          initialRotation = playerRot.y + 180
        }

        console.warn(entityItemId);
        entity = OVERWORLD.spawnEntity(entityItemId, {
          x: itemLocation.x,
          y: itemLocation.y - 0.5,
          z: itemLocation.z,
        }, { initialRotation: initialRotation });

      }
    } else {
      player = eventEntity
      playerRot = eventEntity.getRotation()

      if (itemId == "js:apparatus") {
        initialRotation = playerRot.y + 270
      } else {
        initialRotation = playerRot.y + 180
      }
  
      entity = OVERWORLD.spawnEntity(entityItemId, {
        x: itemLocation.x,
        y: itemLocation.y + 0.2,
        z: itemLocation.z,
      }, { initialRotation: initialRotation });
    }

    const lightningTarget = item.getDynamicProperty("lightningTarget");
    const strikeId = item.getDynamicProperty("strikeId");
    console.warn(lightningTarget)
    console.warn(strikeId)
  
    if (lightningTarget === true && strikeId && lightningStrikes[strikeId]) {
      system.clearRun(updateInterval);
      lightningStrikes[strikeId].location = entity.location;
      lightningStrikes[strikeId].targetEntity = entity;
      console.warn(`New target: Dropped item at ${JSON.stringify(entity.location)}`);
      OVERWORLD.spawnParticle("ding:sparking", entity.location);
      entity.setDynamicProperty("lightningTarget", true);
      entity.setDynamicProperty("strikeId", strikeId); // Update strikeId on the dropped item
    }

    const value = item.getDynamicProperty("scrapValue") ?? 0;

    const collected = item.getDynamicProperty("collected") ?? false;

    if (ITEM_LIST[itemId].battery_life) {
      const battery = item.getDynamicProperty("battery");
      if (!battery && battery !== 0) {
        entity.setDynamicProperty("battery", ITEM_LIST[itemId].battery_life);
      } else {
        entity.setDynamicProperty("battery", battery);
      }
    }

    if (itemId == "js:jetpack") {
      const jetpackBroken = item.getDynamicProperty('jetpackBroken') ?? false
      console.warn(jetpackBroken)
      entity.setDynamicProperty("jetpackBroken", jetpackBroken);

    }

    if (itemId == "js:body") {
      let name = item.getDynamicProperty("name") ?? "Steve";
      const causeOfDeath = item.getDynamicProperty("causeOfDeath");
      entity.nameTag = name;
      entity.setDynamicProperty("causeOfDeath", causeOfDeath);
      entity.triggerEvent("dropped");
    }

    if (itemId == "js:flashlight" || itemId == "js:pro_flashlight") {
      player.playSound("items.flashlight.click", SOUND_OPTIONS);
      player.setProperty("js:flashlight_state", 0);
      player.setProperty("js:energy", 0);
      if (player.flashLightDeplete) {
        system.clearRun(player.flashLightDeplete);
      }
    }

    if (itemId == "js:walkie") {
      player.playSound("items.walkie.off", SOUND_OPTIONS);
      player.setProperty("js:walkie_state", 0);
      player.setProperty("js:energy", 0);
      if (player.walkieDeplete) {
        system.clearRun(player.walkieDeplete);
      }
    }

    if (itemId == "js:shotgun") {
      const bullets = item.getDynamicProperty('bullets') ?? 0
      const safety = item.getDynamicProperty('safetyOn') ?? false
      entity.setDynamicProperty('bullets', bullets)
      entity.setDynamicProperty('safetyOn', safety)

      console.warn(bullets)
      console.warn(safety)
      if (safety == false && bullets > 0) {
        const random = Math.random()
        if (random < 0.05) {
          shotgunShoot(entity)
          entity.setDynamicProperty('bullets', entity.getDynamicProperty('bullets') - 1)
        }
      }
    }

    if (itemId == "js:icabob_plush") {
      player.runCommand(`camera @a[r=8] fade time 0.1 0.1 3 color 255 255 255`)
      
      const range = SOUNDS["icabob"].range;
      const volume = SOUNDS["icabob"].volume;
      broadcastNoise("icabob", player.location, range, volume);

      system.runTimeout(() => {
        player.playSound('items.shotgun.ears_ringing')
      }, 2 * 20)
    }
    
    if (itemId == "js:mecha_plush") {
      const random = Math.random()
      if (random < 0.05) {
        shotgunShoot(entity)
      }
    }


    if (itemId == "js:robot_toy") {
      player.runCommand(`stopsound @a[r=12] items.robot_toy.cheer`);
    }

    if (itemId == "js:boombox") {
      stopLoopedSound("music.boomblock");
      stopLoopedSound("music.icabobs_funk");
      entity.runCommand(`stopsound @a[r=40] music.boomblock`);
      entity.runCommand(`stopsound @a[r=40] music.icabobs_funk`);

      if (player.getProperty("js:boombox_state") == 1) {
        if (player.boomboxDeplete) {
          system.clearRun(player.boomboxDeplete);
        }
        if (player.boomboxSong) {
          player.runCommand(`stopsound @a[r=40] music.boomblock`);
          player.runCommand(`stopsound @a[r=40] music.icabobs_funk`);
          system.clearRun(player.boomboxSong);
        }
        let currentEnergy = 0;
        const unclampedEnergyLevel = Math.ceil((currentEnergy / ITEM_LIST[item.typeId].battery_life) * 100);
        player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
        player.setProperty("js:energy", player.energyLevel);

        player.setProperty("js:boombox_state", 0);

        system.runTimeout(() => {
          const songSelection = Math.random() 
          console.warn(songSelection)
          console.warn(`onE`)
          if (songSelection < 0.5) {
            console.warn(`bom`)
            OVERWORLD.playSound("music.boomblock", entity.location, SOUND_OPTIONS);
            playLoopedSound("music.boomblock", parseFloat(38.4), entity.location);
          } else {
            console.warn(`icabo`)
            OVERWORLD.playSound("music.icabobs_funk", entity.location, SOUND_OPTIONS);
            playLoopedSound("music.icabobs_funk", parseFloat(9.5), entity.location);
          }

          entity.batteryDeplete = system.runInterval(() => {
            if (!entity) {
              stopLoopedSound("music.boomblock");
              stopLoopedSound("music.icabobs_funk");
              system.clearRun(entity.batteryDeplete);
            }

            const range = SOUNDS["boombox"].range;
            const volume = SOUNDS["boombox"].volume;
            broadcastNoise("boombox", entity.location, range, volume);

            let currentEnergy = entity.getDynamicProperty("battery") ?? 0;

            currentEnergy = currentEnergy > 0 ? currentEnergy - 1 : 0;
            entity.setDynamicProperty("battery", currentEnergy);

            if (currentEnergy == 0) {
              player.playSound("items.pro_flashlight.out_of_batteries", SOUND_OPTIONS);
              system.clearRun(entity.batteryDeplete);
              stopLoopedSound("music.boomblock");
            }
          }, 20);
        }, 5);
      }
    }

    //  console.warn(value)

    if (value) {
      entity.setDynamicProperty("scrapValue", value);
    } else {
      if (itemId == "js:key") {
        entity.setDynamicProperty("scrapValue", 3);
      } else {
        entity.setDynamicProperty("scrapValue", 0);
      }
    }



    if (itemId == "js:brass_bell") {
      const range = SOUNDS["drop_bell"].range;
      const volume = SOUNDS["drop_bell"].volume;
      broadcastNoise("drop_bell", itemLocation, range, volume);
    } else {
      const range = SOUNDS["drop_scrap"].range;
      const volume = SOUNDS["drop_scrap"].volume;
      broadcastNoise("drop_scrap", itemLocation, range, volume);
    }

    if (itemId == "js:mrdinger_plush") {
      const random = Math.random()
      if (world.getDynamicProperty('inOrbit') == false && random < 0.025) {
        OVERWORLD.playSound(`misc.moons_before`, entity.location, SOUND_OPTIONS)
        system.runTimeout(() => {
          entity.runCommand(`stopsound @a[r=12] misc.moons_before`);
          entity.runCommand(`summon lightning_bolt`);
        }, 1.7 * 20)
      } else {
        OVERWORLD.playSound(ITEM_LIST[itemId].sound, itemLocation, SOUND_OPTIONS);
      }
    } else {
      OVERWORLD.playSound(ITEM_LIST[itemId].sound, itemLocation, SOUND_OPTIONS);
    }


    if (itemId == 'js:duck') {
      OVERWORLD.playSound('items.drop.duck', itemLocation, SOUND_OPTIONS)
    }

    entity.setDynamicProperty("collected", collected);

    if (value <= 3) {
      entity.setDynamicProperty("collected", true);
    }
    if (collected == false && entity.getDynamicProperty("collected") == false) {
      const shipVol = updateShipVol();

      if (shipVol.isInside(entity.location)) {
        entity.setDynamicProperty("collected", true);

        // const name = item.getDynamicProperty('name') ?? "Steve"

        worldPlayers.forEach((player) => {
          /*
                        if (itemId == "js:body") {
                            player.runCommand(`tellraw @s {"rawtext":[{"text":"dingUI:scrap_____§a${name}\nValue : ${value}"}]}`)
                        } else {
                            player.runCommand(`tellraw @s {"rawtext":[{"text":"dingUI:scrap_____§a${ITEM_LIST[itemId].name}\nValue : ${value}"}]}`)
                        }*/

        
          player.sendMessage(`dingUI:scrap_____§a${ITEM_LIST[itemId].name}\nValue : ${value}`)

          OVERWORLD.playSound("misc.collected", player.location, SOUND_OPTIONS);
        });

        player.profitability += parseInt(value);
        const currentScrapCollected = world.getDynamicProperty("scrapCollected");
        world.setDynamicProperty("scrapCollected", currentScrapCollected + value);
        console.warn(world.getDynamicProperty("scrapCollected"))
        

      }
    }
    
    if (isDroppedItem == true) {
      eventEntity.remove();
    } else {
      const inventory = eventEntity.getComponent("inventory").container;
      inventory.setItem(eventEntity.selectedSlotIndex)
      let jetpackCount = 0
      for (let slot = 2; slot <= 6; slot++) {
        const item = inventory.getItem(slot);
        if (item && item.typeId == "js:jetpack") {
          jetpackCount++;
        }
      }
      if (jetpackCount > 0) {
        eventEntity.setProperty('js:has_jetpack', true)
      } else {
        eventEntity.setProperty('js:has_jetpack', false)
      }
    }
    
    const inventory = owner.getComponent("inventory").container;
    let jetpackCount = 0
    for (let slot = 2; slot <= 6; slot++) {
      const item = inventory.getItem(slot);
      if (item && item.typeId == "js:jetpack") {
        jetpackCount++;
      }
    }
    if (jetpackCount > 0) {
      owner.setProperty('js:has_jetpack', true)
    } else {
      owner.setProperty('js:has_jetpack', false)
    }
    updateWeight(owner);
  }
}

world.afterEvents.entityHealthChanged.subscribe((ev) => {
  const entity = ev.entity;
  if (entity instanceof Player) {
    const health = ev.newValue;
    const damage = ev.oldValue - ev.newValue;

    entity.injuries += damage;

    if (health <= 10 && !entity.criticallyInjured && health > 0) {
      entity.criticallyInjured = true;
      entity.sendMessage(`dingUI:crit_injry`)
      entity.playSound("player.critical_injury", SOUND_OPTIONS);

      entity.runCommand(`camerashake add @s 0.7 0.3 rotational`);
    }

    entity.uiManager.updateHealth(health)

    if (health >= 20) {
      entity.criticallyInjured = false;
    }

    if (damage >= 10) {
      let staminaRegainPercentage = damage / 1.25;

      let staminaRegained = 100 * (staminaRegainPercentage / 100);

      console.warn(`Regained ${staminaRegained} stamina`);
      entity.stamina += staminaRegained;
      if (entity.stamina > 100) {
        entity.stamina = 100;
      }
    }
  }
});

const CANCEL_INTERACTION_BLOCKS = [
  "ding:ship_charger",
  "ding:ship_lever",
  "ding:teleporter",
  "ding:inverse_teleporter",
  "ding:door_buttons_block",
  "js:facility_door",
  "js:fire_exit_door",
  "js:main_door",
  "js:main_door2",
];



world.afterEvents.itemUse.subscribe((data) => {
  const player = data.source;
  const item = data.itemStack;

  const blockRaycast = player.getBlockFromViewDirection({ maxDistance: 8 });
  const block = blockRaycast?.block;
  const inventory = player.getComponent("inventory").container;

  usingItem(player, item, block, inventory)
 
});

/**
 * 
 * @param {Player} player 
 * @param {ItemStack} item 
 * @param {*} block 
 * @returns 
 */
function usingItem(player, item, block) {
  const inventory = player.getComponent("inventory").container;
  switch (item.typeId) {
    case "js:flashlight":
      if (block && CANCEL_INTERACTION_BLOCKS.includes(block.typeId)) return;

      if (player.flashLightDeplete) {
        system.clearRun(player.flashLightDeplete);
      }

      //  console.warn(`Battery ${item.getDynamicProperty('battery')}`)

      if (item.getDynamicProperty("battery") == undefined) {
        let batteryLife = ITEM_LIST["js:flashlight"].battery_life;

        item.setDynamicProperty("battery", batteryLife);
        inventory.setItem(player.flashlightSlot, item);

        //   console.warn(`New Battery ${item.getDynamicProperty('battery')} initialised`)
      }

      if (item.getDynamicProperty("battery") > 0) {
        if (player.getProperty("js:flashlight_state") == 0) {
          player.setProperty("js:flashlight_state", 1);
        } else {
          player.setProperty("js:flashlight_state", 0);
        }

        //for some reason the value here is inversed.
        if (player.getProperty("js:flashlight_state") == 0) {
          player.playSound("items.flashlight.click", SOUND_OPTIONS);
          //   console.warn(`ON`)

          const unclampedEnergyLevel = Math.ceil(
            (item.getDynamicProperty("battery") / ITEM_LIST[item.typeId].battery_life) * 100
          );
          player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
          player.setProperty("js:energy", player.energyLevel);

          player.flashLightDeplete = system.runInterval(() => {
            const inventory = player.getComponent("inventory").container;
            let flashlightItem = inventory.getItem(player.flashlightSlot);
            if (flashlightItem == undefined) {
              player.playSound("items.flashlight.click", SOUND_OPTIONS);
              player.setProperty("js:flashlight_state", 0);
              system.clearRun(player.flashLightDeplete);
            }

            let currentEnergy = flashlightItem.getDynamicProperty("battery") ?? 0;

            currentEnergy = currentEnergy > 0 ? currentEnergy - 1 : 0;
            flashlightItem.setDynamicProperty("battery", currentEnergy);
            inventory.setItem(player.flashlightSlot, flashlightItem);

            if (currentEnergy == 0) {
              player.playSound("items.flashlight.out_of_batteries", SOUND_OPTIONS);
              player.setProperty("js:flashlight_state", 0);
              system.clearRun(player.flashLightDeplete);
            }
            const unclampedEnergyLevel = Math.ceil((currentEnergy / ITEM_LIST[item.typeId].battery_life) * 100);
            player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
            player.setProperty("js:energy", player.energyLevel);

            //player.runCommand(`playanimation @s animation.employee.hold_item.small.flashlight.particle`)

            //   console.warn(`${flashlightItem.typeId} ${currentEnergy}`)
          }, 20);
        }
        if (player.getProperty("js:flashlight_state") == 1) {
          //     console.warn(`OFF`)

          system.clearRun(player.flashLightDeplete);
          player.setProperty("js:flashlight_state", 0);
          player.playSound("items.flashlight.click", SOUND_OPTIONS);
          player.energyLevel = 0;
          player.setProperty("js:energy", player.energyLevel);
        }
      } else {
        //  console.warn(`Out of batt`)
        player.setProperty("js:flashlight_state", 0);
        player.playSound("items.flashlight.out_of_batteries", SOUND_OPTIONS);
        player.energyLevel = 0;
        player.setProperty("js:energy", player.energyLevel);
      }
      break;

    case "js:pro_flashlight":
      if (block && CANCEL_INTERACTION_BLOCKS.includes(block.typeId)) return;

      if (player.flashLightDeplete) {
        system.clearRun(player.flashLightDeplete);
      }

      // console.warn(`Battery ${item.getDynamicProperty('battery')}`)

      if (item.getDynamicProperty("battery") == undefined) {
        let batteryLife = ITEM_LIST["js:pro_flashlight"].battery_life;

        item.setDynamicProperty("battery", batteryLife);
        inventory.setItem(player.flashlightSlot, item);

        // console.warn(`New Battery ${item.getDynamicProperty('battery')} initialised`)
      }

      if (item.getDynamicProperty("battery") > 0) {
        if (player.getProperty("js:flashlight_state") == 0) {
          player.setProperty("js:flashlight_state", 2);
        } else {
          player.setProperty("js:flashlight_state", 0);
        }

        //for some reason the value here is inversed.
        if (player.getProperty("js:flashlight_state") == 0) {
          player.playSound("items.pro_flashlight.click", SOUND_OPTIONS);
          //   console.warn(`ON`)

          const unclampedEnergyLevel = Math.ceil(
            (item.getDynamicProperty("battery") / ITEM_LIST[item.typeId].battery_life) * 100
          );
          player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
          player.setProperty("js:energy", player.energyLevel);

          player.flashLightDeplete = system.runInterval(() => {
            const inventory = player.getComponent("inventory").container;
            let flashlightItem = inventory.getItem(player.flashlightSlot);
            if (flashlightItem == undefined) {
              player.playSound("items.pro_flashlight.click", SOUND_OPTIONS);
              player.setProperty("js:flashlight_state", 0);
              system.clearRun(player.flashLightDeplete);
            }

            let currentEnergy = flashlightItem.getDynamicProperty("battery") ?? 0;

            currentEnergy = currentEnergy > 0 ? currentEnergy - 1 : 0;
            flashlightItem.setDynamicProperty("battery", currentEnergy);
            inventory.setItem(player.flashlightSlot, flashlightItem);

            if (currentEnergy == 0) {
              player.playSound("items.pro_flashlight.out_of_batteries", SOUND_OPTIONS);
              player.setProperty("js:flashlight_state", 0);
              system.clearRun(player.flashLightDeplete);
            }

            const unclampedEnergyLevel = Math.ceil((currentEnergy / ITEM_LIST[item.typeId].battery_life) * 100);
            player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
            player.setProperty("js:energy", player.energyLevel);

            //player.runCommand(`playanimation @s animation.employee.hold_item.small.pro_flashlight.particle`)

            //  console.warn(`${flashlightItem.typeId} ${currentEnergy}`)
          }, 20);
        }
        if (player.getProperty("js:flashlight_state") == 2) {
          //   console.warn(`OFF`)

          system.clearRun(player.flashLightDeplete);
          player.setProperty("js:flashlight_state", 0);
          player.playSound("items.pro_flashlight.click", SOUND_OPTIONS);
          player.energyLevel = 0;
          player.setProperty("js:energy", player.energyLevel);
        }
      } else {
        //  console.warn(`Out of batt`)
        player.setProperty("js:flashlight_state", 0);
        player.playSound("items.pro_flashlight.out_of_batteries", SOUND_OPTIONS);
        player.energyLevel = 0;
        player.setProperty("js:energy", player.energyLevel);
      }
      break;

    case "js:boombox":
      if (block && CANCEL_INTERACTION_BLOCKS.includes(block.typeId)) return;

      if (player.boomboxDeplete) {
        system.clearRun(player.boomboxDeplete);
      }
      if (player.boomboxSong) {
        player.runCommand(`stopsound @a[r=40] music.boomblock`);
        player.runCommand(`stopsound @a[r=40] music.icabobs_funk`);
        system.clearRun(player.boomboxSong);
      }

      // console.warn(`Battery ${item.getDynamicProperty('battery')}`)

      if (item.getDynamicProperty("battery") == undefined) {
        let batteryLife = ITEM_LIST["js:boombox"].battery_life;

        item.setDynamicProperty("battery", batteryLife);
        inventory.setItem(player.boomboxSlot, item);

        // console.warn(`New Battery ${item.getDynamicProperty('battery')} initialised`)
      }

      if (item.getDynamicProperty("battery") > 0) {
        if (player.getProperty("js:boombox_state") == 0) {
          player.setProperty("js:boombox_state", 1);
        } else {
          player.setProperty("js:boombox_state", 0);
        }

        //for some reason the value here is inversed.
        if (player.getProperty("js:boombox_state") == 0) {
          player.playSound("items.boombox.stop", SOUND_OPTIONS);
          //   console.warn(`ON`)

          const unclampedEnergyLevel = Math.ceil(
            (item.getDynamicProperty("battery") / ITEM_LIST[item.typeId].battery_life) * 100
          );
          player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
          player.setProperty("js:energy", player.energyLevel);

          player.boomboxDeplete = system.runInterval(() => {
            const inventory = player.getComponent("inventory").container;
            let boomboxItem = inventory.getItem(player.boomboxSlot);
            if (boomboxItem == undefined) {
              player.playSound("items.boombox.stop", SOUND_OPTIONS);
              player.setProperty("js:boombox_state", 0);
              system.clearRun(player.boomboxDeplete);
              player.runCommand(`stopsound @a[r=40] music.boomblock`);
              player.runCommand(`stopsound @a[r=40] music.icabobs_funk`);
              system.clearRun(player.boomboxSong);
            }

            const range = SOUNDS["boombox"].range;
            const volume = SOUNDS["boombox"].volume;
            broadcastNoise("boombox", player.location, range, volume);

            let currentEnergy = boomboxItem.getDynamicProperty("battery") ?? 0;

            currentEnergy = currentEnergy > 0 ? currentEnergy - 1 : 0;
            boomboxItem.setDynamicProperty("battery", currentEnergy);
            inventory.setItem(player.boomboxSlot, boomboxItem);

            if (currentEnergy == 0) {
              player.playSound("items.pro_flashlight.out_of_batteries", SOUND_OPTIONS);
              player.setProperty("js:boombox_state", 0);
              system.clearRun(player.boomboxDeplete);
              player.runCommand(`stopsound @a[r=40] music.boomblock`);
              player.runCommand(`stopsound @a[r=40] music.icabobs_funk`);
              system.clearRun(player.boomboxSong);
            }

            const unclampedEnergyLevel = Math.ceil((currentEnergy / ITEM_LIST[item.typeId].battery_life) * 100);
            player.energyLevel = Math.min(Math.max(unclampedEnergyLevel, 0), 100);
            player.setProperty("js:energy", player.energyLevel);

            //  console.warn(`${flashlightItem.typeId} ${currentEnergy}`)
          }, 20);

          const songSelection = Math.random() 
          console.warn(songSelection)
          if (songSelection < 0.5) {
            console.warn(`boom`)
            player.runCommand(`playsound music.boomblock @a[r=20]`);
            player.boomboxSong = system.runInterval(() => {
              player.runCommand(`playsound music.boomblock @a[r=20]`);
            }, 38.4 * 20);
          } else {
            console.warn(`icabo`)
            player.runCommand(`playsound music.icabobs_funk @a[r=20]`);
            player.boomboxSong = system.runInterval(() => {
              player.runCommand(`playsound music.icabobs_funk @a[r=20]`);
            }, 9.5 * 20);
          }
        }
        if (player.getProperty("js:boombox_state") == 1) {
          //   console.warn(`OFF`)

          system.clearRun(player.boomboxDeplete);
          player.runCommand(`stopsound @a[r=40] music.boomblock`);
          player.runCommand(`stopsound @a[r=40] music.icabobs_funk`);
          system.clearRun(player.boomboxSong);

          player.setProperty("js:boombox_state", 0);
          player.playSound("items.boombox.stop", SOUND_OPTIONS);
          player.energyLevel = 0;
          player.setProperty("js:energy", player.energyLevel);
        }
      } else {
        //  console.warn(`Out of batt`)
        player.setProperty("js:boombox_state", 0);
        player.playSound("items.pro_flashlight.out_of_batteries", SOUND_OPTIONS);
        player.energyLevel = 0;
        player.setProperty("js:energy", player.energyLevel);
      }
      break;

    case "js:scan":
      let cooldownData = player.getItemCooldown("scan_cooldown");
      if (cooldownData == 0) {
        player.startItemCooldown("scan_cooldown", 30);
        player.runCommand(`fill ~ ~ ~ ~ ~1 ~ js:light_block3 replace air`)
        const scanEnt = player.dimension.spawnEntity('js:scan_ent',player.location)
        system.runTimeout(() => {
          scanEnt.playAnimation('animation.scan.scale',{controller: 'k',blendOutTime:999999, players: [ player ]})
        },2)
        player.runCommand(`scriptevent js:scan 8 120 45 20`);
        player.playSound('misc.scan')
        player.playAnimation('animation.employee.scan.scanning', {
          stopExpression: "q.all_animations_finished",
          blendOutTime: 0
        })
      }
      break;
    case "js:clown_horn":
      let cooldownData2 = player.getItemCooldown("clown_horn_cooldown");
      if (cooldownData2 == 0) {
        player.startItemCooldown("clown_horn_cooldown", 5);
        player.playAnimation('animation.employee.clown_horn')
        for (let otherPlayer of worldPlayers) {
          if (otherPlayer == player) continue
          const distance = getDistance(player.location, otherPlayer.location)
          if (distance > 20) {
            // otherPlayer.playSound("items.clown_horn.horn_far", player.location, { volume: 50, pitch: 1});
            otherPlayer.runCommand(`playsound items.clown_horn.horn_far @s ${player.getHeadLocation().x} ${player.getHeadLocation().y} ${player.getHeadLocation().z} 65 1 10`)
          } else {
            // otherPlayer.playSound("items.clown_horn.horn", player.location, SOUND_OPTIONS);
            otherPlayer.runCommand(`playsound items.clown_horn.horn @s ${player.getHeadLocation().x} ${player.getHeadLocation().y} ${player.getHeadLocation().z} 1 1 0.25`)
          }
        }
        player.playSound("items.clown_horn.horn", { volume: 1, pitch: 1});

        const range = SOUNDS["clown_horn"].range;
        const volume = SOUNDS["clown_horn"].volume;
        broadcastNoise("clown_horn", player.location, range, volume);
      }
      break; 
    case "js:air_horn":
      let cooldownData5 = player.getItemCooldown("air_horn_cooldown");
      if (cooldownData5 == 0) {
        player.startItemCooldown("air_horn_cooldown", 25);
        player.playAnimation('animation.employee.air_horn')
        for (let otherPlayer of worldPlayers) {
          if (otherPlayer == player) continue
          const distance = getDistance(player.location, otherPlayer.location)
          if (distance > 26) {
            otherPlayer.runCommand(`playsound items.air_horn.horn_far @s ${player.getHeadLocation().x} ${player.getHeadLocation().y} ${player.getHeadLocation().z} 65 1 10`)
          } else {
            otherPlayer.runCommand(`playsound items.air_horn.horn @s ${player.getHeadLocation().x} ${player.getHeadLocation().y} ${player.getHeadLocation().z} 1 1 0.25`)
          }
        }
        player.playSound("items.air_horn.horn", { volume: 1, pitch: 1});

        const range = SOUNDS["air_horn"].range;
        const volume = SOUNDS["air_horn"].volume;
        broadcastNoise("air_horn", player.location, range, volume);
      }
      break; 
    case "js:remote":
      let cooldownData6 = player.getItemCooldown("remote_cooldown");
      if (cooldownData6 == 0) {
        player.startItemCooldown("remote_cooldown", 5);
        player.dimension.playSound("items.remote.click", player.location, { volume: 1, pitch: 1});
      }
      break; 
    case "js:shotgun":
      let cooldownData3 = player.getItemCooldown("shotgun_cooldown");
      if (cooldownData3 == 0) {
        player.startItemCooldown("shotgun_cooldown", 10);
        const bullets = item.getDynamicProperty('bullets') ?? 0
        const safety = item.getDynamicProperty('safetyOn') ?? false

        const inventory = player.getComponent("inventory").container;
        let ammoCount = 0;
        let ammoSlot = 2
  
        for (let slot = 2; slot <= 6; slot++) {
          const item = inventory.getItem(slot);
          if (item && item.typeId == "js:shotgun_shell") {
            ammoCount++;
            ammoSlot = slot
          }
        }

        if (bullets > 0 && safety == false && !player.isSneaking) {
          shotgunShoot(player)
          player.playAnimation('animation.employee.shotgun.shoot')
          item.setDynamicProperty('bullets', bullets - 1)
          player.runCommand(`camerashake add @s 1 0.2 rotational`)
        } else if (bullets == 0) {
          if (ammoCount > 0) {
            player.setProperty('js:reloading', 1)
          } else {
            player.playSound('items.shotgun.shoot_fail')
          }
        } else if (bullets == 1 && player.isSneaking) {
          if (ammoCount > 0) {
            player.setProperty('js:reloading', 2)
          } else {
            player.playSound('items.shotgun.shoot_fail')
          }
        } else if (safety == true) {
          player.playSound('items.shotgun.shoot_fail')
        }
        
        inventory.setItem(player.selectedSlotIndex, item)
      }
      break;
    case "js:gift":
      let cooldownData4 = player.getItemCooldown("gift_cooldown");
      const inventory = player.getComponent('inventory').container
      if (cooldownData4 == 0) {
        player.startItemCooldown("gift_cooldown", 5);
        const moonId = world.getDynamicProperty("moon");
        let mapName;
        for (const key of Object.keys(MAPS)) {
          if (MAPS[key].id === moonId) {
            mapName = key;
            break;
          }
        }

        if (moonId == 0) {
          inventory.setItem(player.selectedSlotIndex, undefined)
        } else {
          const lootTable = MOON_LOOT_TABLE[mapName];
          const totalWeight = Object.values(lootTable).reduce((sum, weight) => sum + weight, 0);
          const random = Math.random() * totalWeight;
          let accumulatedPercentage = 0;
          let selectedScrap;

          for (const scrap in lootTable) {
            accumulatedPercentage += lootTable[scrap];
            if (random <= accumulatedPercentage) {
              selectedScrap = scrap;
              break;
            }
          }

          if (selectedScrap) {
            const minValue = ITEM_LIST[selectedScrap].value[0] + 10;
            const maxValue = ITEM_LIST[selectedScrap].value[1] + 14;
            const randomScrapValue = Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;
            const selectedScrapId = selectedScrap + "_ent";
            let initialRotation = player.getRotation().y + 180
            const scrap = OVERWORLD.spawnEntity(selectedScrapId, { x: player.location.x, y: player.location.y + 0.5, z: player.location.z }, {initialRotation: initialRotation});

            scrap.setDynamicProperty('scrapValue', randomScrapValue)
            
            const shipVol = updateShipVol();
      
            if (shipVol.isInside(scrap.location)) {
              scrap.setDynamicProperty("collected", true);
      
              worldPlayers.forEach((player) => {
                player.sendMessage(`dingUI:scrap_____§a${ITEM_LIST[selectedScrap].name}\nValue : ${randomScrapValue}`)
      
                OVERWORLD.playSound("misc.collected", player.location, SOUND_OPTIONS);
              });
      
              player.profitability += parseInt(randomScrapValue);
              const currentScrapCollected = world.getDynamicProperty("scrapCollected");
              world.setDynamicProperty("scrapCollected", currentScrapCollected + randomScrapValue);
              console.warn(world.getDynamicProperty("scrapCollected"))
      
            }
            OVERWORLD.playSound('items.gift.open', player.location, SOUND_OPTIONS)
            inventory.setItem(player.selectedSlotIndex, undefined)
            updateWeight(player);
          }
        }

      }
      break; 
    default:
      break;
  }
}



const loadingData = new Map();

function updateLoadingScores(player) {
  const playerId = player.id;
  const currentLoading = loading.getScore(player); // Get the current loading score

  // Initialize player data if not already present
  if (!loadingData.has(playerId)) {
    loadingData.set(playerId, {
      previousLoading: currentLoading,
    });
  }

  const playerInfo = loadingData.get(playerId);
  const previousLoading = playerInfo.previousLoading;

  // If loading score is the same as before, reset it
  if (currentLoading === previousLoading) {
    loading.setScore(player, 0);
    player.uiManager.updateLoading(0)
  }

  // Update the player's previous loading score
  playerInfo.previousLoading = currentLoading;
}

system.runInterval(() => {
  const generationPercentage = world.getDynamicProperty('generationPercentage');

  // Skip update if the generation percentage is active
  if (generationPercentage > 0) return;

  for (let player of worldPlayers) {
    updateLoadingScores(player); // Process loading scores per player
  }
}, 2);



const DOORS = ["js:main_door", "js:facility_door", "js:fire_exit_door"];

world.beforeEvents.playerBreakBlock.subscribe((data) => {
  const { block, dimension } = data;

  if (!DOORS.includes(block.typeId)) return;

  const topHalf = block.permutation.getState("js:upper_block_bit");
  let doorPart = undefined;
  if (topHalf == false) {
    //gets the block above
    try {
      doorPart = block.above(1);
    } catch { }
  } else
    try {
      doorPart = block.below(1);
    } catch { }

  const otherLocation = doorPart.location;
  system.run(() => {
    dimension.setBlockType(otherLocation, "minecraft:air");
  });
});

function dropAllItems(player) {
  player.inventory = player.getComponent("inventory").container;
  const slots = [
    player.inventory.getItem(2),
    player.inventory.getItem(3),
    player.inventory.getItem(4),
    player.inventory.getItem(5),
    player.inventory.getItem(6),
  ];

  const playerRot = player.getRotation();
  for (let index = 0; index < slots.length; index++) {
    const item = slots[index];
    if (item && !item.typeId) {
      continue;
    }
    if (item) {
      const itemId = item.typeId;
      if (ITEM_LIST[itemId]) {
        let value = item?.getDynamicProperty("scrapValue") ?? 0;
        let battery = item?.getDynamicProperty("battery") ?? 0;
        let collected = item?.getDynamicProperty("collected") ?? false;
        
        let bullets = item.getDynamicProperty('bullets') ?? 0
        let safetyOn = item.getDynamicProperty('safetyOn') ?? false
        let causeOfDeath = item.getDynamicProperty("causeOfDeath");
        let jetpackBroken = item.getDynamicProperty('jetpackBroken') ?? false

        console.warn(`JETPCK ${jetpackBroken}`)

        const spawnItemId = itemId + "_ent";
        const scrap = OVERWORLD.spawnEntity(spawnItemId, player.location, {initialRotation: playerRot.y + 180 });
        scrap.setDynamicProperty("scrapValue", value);
        scrap.setDynamicProperty("battery", battery);
        scrap.setDynamicProperty("collected", collected);
        // scrap.setRotation({ x: playerRot.x, y: playerRot.y + 180 });

        if (itemId == "js:shotgun") {
          scrap.setDynamicProperty("safetyOn", safetyOn);
          scrap.setDynamicProperty("bullets", bullets);
        }
        if (itemId == "js:body") {
          scrap.setDynamicProperty("causeOfDeath", causeOfDeath);
        }
        if (itemId == "js:jetpack") {
          scrap.setDynamicProperty("jetpackBroken", jetpackBroken);
        }
        console.warn(`JETPCK ${scrap.getDynamicProperty('jetpackBroken')}`)

        OVERWORLD.playSound(ITEM_LIST[itemId].sound, player.location, SOUND_OPTIONS);

        player.inventory.setItem(index + 2, undefined)
      }
    }
  }
}

world.beforeEvents.playerInteractWithBlock.subscribe((ev) => {
  const player = ev.player;
  const block = ev.block;
  const item = ev.itemStack

  system.run(() => {
    if (item && ev.isFirstEvent) {
      const inventory = player.getComponent("inventory").container;
      
      usingItem(player, item, block, inventory)
    }

    if (ev.isFirstEvent && block.typeId == "ding:teleporter" && world.getDynamicProperty('js:teleporter') == true) {
      shipEntity.playAnimation('animation.teleporter.button_press')
      block.dimension.playSound('buttons.ship.press', block.location)

      const inOrbit = world.getDynamicProperty("inOrbit");
      const teleporterCooldown = world.getDynamicProperty('js:teleporter_cooldown')
      console.warn(teleporterCooldown)
      if (inOrbit == false) {
        if (teleporterCooldown == 0) {
          world.setDynamicProperty('js:teleporter_cooldown', 10)
          shipEntity.playAnimation('animation.teleporter.boot')

          const selectedPlayer = OVERWORLD.getEntities({tags:["selected"], closest: 1})
          if (selectedPlayer[0]) {
            const teleportee = selectedPlayer[0]
            teleportee.playAnimation('animation.employee.being_teleported')
            teleportee.teleportTimeout = system.runTimeout(() => {
              
              if (teleportee.attackedTimeout) {
                system.clearRun(teleportee.attackedTimeout) 
                teleportee.attackedTimeout = undefined
              }

              if (teleportee.typeId == "minecraft:player" && teleportee.getGameMode() !== "Spectator") {
                teleportee.camera.setCamera('minecraft:first_person')
                teleportee.inputPermissions.movementEnabled = true
                teleportee.inputPermissions.cameraEnabled = true
                dropAllItems(teleportee)
              }
              if (teleportee.hasTag("inFacility")) {
                player.removeTag("inFacility");
                player.runCommand(`scriptevent js:stop_ambience`);
                player.runCommand(`scriptevent js:ambience`);
              }
              let teleportLocation = {
                x: shipEntity.location.x - 2,
                y: shipEntity.location.y + 0.5,
                z: shipEntity.location.z - 7
              };
              teleportee.teleport(teleportLocation)
              system.run(() => {
                teleportee.dimension.playSound('misc.ship.teleporter_beam', teleportee.location)
              })
            }, 3.5 * 20)
          }


        }
      }
    }

    if (ev.isFirstEvent && block.typeId == "ding:inverse_teleporter" && world.getDynamicProperty('js:inverse_teleporter') == true) {
      shipEntity.playAnimation('animation.inverse_teleporter.button_press')
      block.dimension.playSound('buttons.ship.press', block.location)
      const inOrbit = world.getDynamicProperty("inOrbit");
      const inverseTeleporterCooldown = world.getDynamicProperty('js:inverse_teleporter_cooldown')
      console.warn(inverseTeleporterCooldown)
      if (inOrbit == false) {
        if (inverseTeleporterCooldown == 0) {
          world.setDynamicProperty('js:inverse_teleporter_cooldown', 210)
          shipEntity.playAnimation('animation.inverse_teleporter.boot')

          let inverseTeleporterLocation = {
            x: shipEntity.location.x - 2,
            y: shipEntity.location.y + 0.5,
            z: shipEntity.location.z - 5
          };
          const roomsMap = new Map(
            world.getDynamicProperty("rooms") ? JSON.parse(world.getDynamicProperty("rooms")) : []
          );
          const roomEntries = Array.from(roomsMap.entries()); // [ [key, {min, max}], ... ]

          // Generator to find a teleport spot for a player
          function* findTeleportLocation(player, roomEntries, SPAWNABLE_BLOCKS, OVERWORLD) {
            let iterationCount = 0;

            if (roomEntries.length === 0) {
              console.warn("no rooms available in roomsMap, skipping teleport");
              return;
            }

            while (iterationCount < 450) {
              iterationCount++;

              // pick a random room
              const [roomKey, room] = roomEntries[Math.floor(Math.random() * roomEntries.length)];
              const { min, max } = room;

              // compute bounds inside the room (same inner-volume offsets you used earlier)
              const minX = Math.min(min.x + 1, max.x - 1);
              const maxX = Math.max(min.x + 1, max.x - 1);
              const minZ = Math.min(min.z + 1, max.z - 1);
              const maxZ = Math.max(min.z + 1, max.z - 1);
              const minY = Math.min(min.y + 1, max.y - 1);
              const maxY = Math.max(min.y + 1, max.y - 1);

              if (minX >= maxX || minZ >= maxZ || minY >= maxY) {
                yield; // let system.runJob breathe
                continue;
              }

              const randomX = Math.floor(Math.random() * (maxX - minX + 1)) + minX;
              const randomZ = Math.floor(Math.random() * (maxZ - minZ + 1)) + minZ;

              const highestSearchY = Math.max(minY, Math.min(maxY - 2, maxY));
              if (highestSearchY < minY) {
                yield;
                continue;
              }

              const randomY = Math.floor(Math.random() * (highestSearchY - minY + 1)) + minY;

              const block = OVERWORLD.getBlock({ x: randomX, y: randomY, z: randomZ });
              const blockAbove = OVERWORLD.getBlock({ x: randomX, y: randomY + 1, z: randomZ });
              const blockAbove2 = OVERWORLD.getBlock({ x: randomX, y: randomY + 2, z: randomZ });
              console.warn(block.typeId)

              let failReason = null;

              if (!block) failReason = "no ground block";
              else if (block.typeId == "minecraft:air") failReason = `block is air (${block.typeId})`;
              else if (DEATH_BLOCKS.includes(block.typeId)) failReason = `ground block death (${block.typeId})`;
              else if (!blockAbove || blockAbove.typeId !== "minecraft:air") failReason = `block above not air (${blockAbove?.typeId})`;
              else if (!blockAbove2 || blockAbove2.typeId !== "minecraft:air") failReason = `second block above not air (${blockAbove2?.typeId})`;
              else if (block.typeId.includes("death")) failReason = `ground block contains 'death' (${block.typeId})`;

              if (failReason) {
                const debugLoc = { x: randomX, y: randomY, z: randomZ };
                console.warn(`Teleport failed for player ${player.name ?? player.id ?? "<player>"} at`, debugLoc, "Reason:", failReason);
                
                // // Optional: still teleport to a fallback location slightly above the block
                // const fallbackLoc = { x: randomX + 0.5, y: randomY + 1.5, z: randomZ + 0.5 };
                // player.teleport(fallbackLoc);

                yield; // allow system.runJob to continue
                continue;
              }

              // Found a good spot!
              const newLoc = { x: randomX + 0.5, y: randomY + 1.5, z: randomZ + 0.5 };

              player.teleport(newLoc);
              player.addTag("inFacility");
              player.runCommand(`scriptevent js:stop_ambience`);
              player.runCommand(`scriptevent js:ambience`);
              system.runTimeout(() => {
                player.dimension.playSound("misc.ship.teleporter_beam", player.location);
              },2);

              return; // success
            }

            console.warn("failed to find teleport location for player after", iterationCount, "attempts");
          }

          system.runTimeout(() => {
            // Usage with system.runJob
            const inversedPlayers = OVERWORLD.getEntities({
              type: "minecraft:player",
              location: inverseTeleporterLocation,
              maxDistance: 1.25,
            });

            for (let player of inversedPlayers) {
              dropAllItems(player)
              system.runJob(findTeleportLocation(player, roomEntries, SPAWNABLE_BLOCKS, OVERWORLD));
            }
          }, 4.7 * 20)

        }
      }
    }

    if (item && item.typeId == "minecraft:barrier") {

      const loc1 = {
        x: block.location.x,
        y: 59,
        z: block.location.z
      }
      const loc2 = {
        x: block.location.x,
        y: 128,
        z: block.location.z
      }
      
      if (!player.isSneaking) {
        OVERWORLD.runCommand(`fill ${loc1.x} ${loc1.y} ${loc1.z} ${loc2.x} ${loc2.y} ${loc2.z} barrier replace air`)
      } else {
        OVERWORLD.runCommand(`fill ${loc1.x} ${loc1.y} ${loc1.z} ${loc2.x} ${loc2.y} ${loc2.z} air replace barrier`)
      }
    }
  })

})


world.beforeEvents.playerInteractWithBlock.subscribe((ev) => {
  system.run(() => {
    const player = ev.player;
    const block = ev.block;
    const cooldown = block.permutation.getState("js:cooldown");

    let enterFacility = 13
    let exitFacility = 17
    let shipLever = 13
    let facilityDoor = 25

    if (player.clientSystemInfo.platformType !== "Desktop") {
      enterFacility = 20
      exitFacility = 25
      shipLever = 20 
    }

    const currentLoading = loading.getScore(player);

    if (block?.typeId == "js:main_door") {

      if (currentLoading <= 990 && !player.hasTag("inFacility")) {
        loading.addScore(player, enterFacility);
        const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
        player.uiManager.updateLoading(scaledScore)

      } else if (currentLoading <= 990 && player.hasTag("inFacility")) {
        loading.addScore(player, exitFacility);
        const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
        player.uiManager.updateLoading(scaledScore)

      } else if (currentLoading >= 1000) {
        const rideComponent = player.getComponent("minecraft:rideable");
        const riders = rideComponent.getRiders();
        for (let rider of riders) {
          if (rider.typeId == "js:snare_flea") {
            rideComponent.ejectRider(rider);

            rider.applyDamage(100, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: player,
            });
          }
        }

        const moonId = world.getDynamicProperty("moon");
        let shipLocation;
        let mainLocation;
        for (const key of Object.keys(MAPS)) {
          if (MAPS[key].id === moonId) {
            shipLocation = MAPS[key].shipLocation;
            mainLocation = MAPS[key].mainLocation;
            break;
          }
        }
        if (!player.hasTag("inFacility")) {
          let teleportLocation = {
            x: shipLocation.x + 3.99,
            y: shipLocation.y - 60 - 30,
            z: shipLocation.z + 1.8,
          };

          if (world.getDynamicProperty('facilityType') == "mansion") {
            teleportLocation = {
              x: shipLocation.x + 12.0,
              y: shipLocation.y - 74 - 30,
              z: shipLocation.z + 1.5,
            };
          }
          //console.warn(JSON.stringify(teleportLocation))
          player.teleport(teleportLocation, { rotation: { x: 0, y: 0 } });
          player.addTag("inFacility");
          system.runTimeout(() => {
            player.playSound("door.main.open", { volume: 1, pitch: 1 });
            player.runCommand(`scriptevent js:stop_ambience`);
            player.runCommand(`scriptevent js:ambience`);
          }, 5);
          loading.setScore(player, 0);
          player.uiManager.updateLoading(0)

        } else if (player.hasTag("inFacility")) {

          let teleportRotation = { x: 0, y: 180 }
          if (moonId == 2) {
            teleportRotation = { x: 0, y: 0 }
          }
          if (moonId == 7) {
            teleportRotation = { x: 0, y: 90 }
          }
          if (moonId == 10) {
            teleportRotation = { x: 0, y: 180 }
          }
          if (moonId == 11) {
            teleportRotation = { x: 0, y: 90 }
          }

          player.teleport(mainLocation, { rotation: teleportRotation });
          player.removeTag("inFacility");
          system.runTimeout(() => {
            player.playSound("door.main.open", { volume: 1, pitch: 1 });
            player.runCommand(`scriptevent js:stop_ambience`);
            player.runCommand(`scriptevent js:ambience`);
          }, 5);
          loading.setScore(player, 0);
          player.uiManager.updateLoading(0)
        }
      }
    }

    if (block?.typeId == "js:fire_exit_door") {
      const blockLocation = block.location;

      const thisFireExitNumber = block.permutation.getState("js:fire_exit_number");

      const moonId = world.getDynamicProperty("moon");
      let shipLocation;
      let mainLocation;
      let mapName;
      for (const key of Object.keys(MAPS)) {
        if (MAPS[key].id === moonId) {
          shipLocation = MAPS[key].shipLocation;
          mainLocation = MAPS[key].mainLocation;
          mapName = key;
          break;
        }
      }

      if (currentLoading <= 990 && !player.hasTag("inFacility")) {
        loading.addScore(player, enterFacility);
        const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
        player.uiManager.updateLoading(scaledScore)

      } else if (currentLoading <= 990 && player.hasTag("inFacility")) {
        loading.addScore(player, exitFacility);
        const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
        player.uiManager.updateLoading(scaledScore)

      } else if (currentLoading >= 1000) {
        const rideComponent = player.getComponent("minecraft:rideable");
        const riders = rideComponent.getRiders();
        for (let rider of riders) {
          if (rider.typeId == "js:snare_flea") {
            rideComponent.ejectRider(rider);

            rider.applyDamage(100, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: player,
            });
          }
        }

        if (!player.hasTag("inFacility")) {
          const fireExitsMap = new Map(
            world.getDynamicProperty("fireExits") ? JSON.parse(world.getDynamicProperty("fireExits")) : []
          );

          let teleportInLocation = blockLocation;
          let teleportRotation;

          let obstructed = false;
          let found = false;

          for (const [
            key,
            { location, inFrontLocation, originalDir, inverseDir, fireExitNum },
          ] of fireExitsMap.entries()) {
            if (thisFireExitNumber === fireExitNum) {
              teleportInLocation = inFrontLocation;
              found = true;

              if (originalDir === "north") {
                teleportRotation = 0;
              } else if (originalDir === "west") {
                teleportRotation = -90;
              } else if (originalDir === "east") {
                teleportRotation = 90;
              } else if (originalDir === "south") {
                teleportRotation = -180;
              }
              break;
            }
          }

          if (!found) {
            obstructed = true;
          }

          if (obstructed == false) {
            console.warn(JSON.stringify(`Teleport In ${JSON.stringify(teleportInLocation)}`));

            player.teleport(teleportInLocation, { rotation: { x: 0, y: teleportRotation } });

            player.addTag("inFacility");

            system.runTimeout(() => {
              player.playSound("door.main.open", { volume: 1, pitch: 1 });
              player.runCommand(`scriptevent js:stop_ambience`);
              player.runCommand(`scriptevent js:ambience`);
            }, 5);

            loading.setScore(player, 0);
            const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
            player.uiManager.updateLoading(scaledScore)

          } else {
            player.sendMessage(`dingUI:pilot_w___??? The entrance appears to be blocked.`)
            player.playSound("misc.tips_hud", SOUND_OPTIONS);
            loading.setScore(player, 0);
            const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
            player.uiManager.updateLoading(scaledScore)

          }
        } else if (player.hasTag("inFacility")) {
          const fireExitNumber = thisFireExitNumber.toString();

          let teleportOutLocation = MAPS[mapName].fireExitLocations[fireExitNumber];

          // let teleportRotation = 0;
          // const rotation = block.permutation.getState("minecraft:cardinal_direction");

          // if (rotation === "north") {
          //   teleportRotation = -180;
          // } else if (rotation === "west") {
          //   teleportRotation = 90;
          // } else if (rotation === "east") {
          //   teleportRotation = -90;
          // } else if (rotation === "south") {
          //   teleportRotation = 0;
          // }


          let teleportRotation = 0
          if (moonId == 1) {
            teleportRotation = -90
          }
          if (moonId == 2) {
            teleportRotation = 90
          }
          if (moonId == 5) {
            if (fireExitNumber == 0) {
              teleportRotation = 90
            }
            if (fireExitNumber == 1) {
              teleportRotation = -90
            }
            if (fireExitNumber == 2) {
              teleportRotation = 180
            }
          }
          if (moonId == 7) {
            teleportRotation = 180
          }
          if (moonId == 9) {
            teleportRotation = 180
          }
          if (moonId == 10) {
            teleportRotation = 180
          }

          console.warn(`tp out ${JSON.stringify(teleportOutLocation)} firenum ${fireExitNumber}`);
          player.teleport(teleportOutLocation, { rotation: { x: 0, y: teleportRotation } });

          player.removeTag("inFacility");
          system.runTimeout(() => {
            player.playSound("door.main.open", { volume: 1, pitch: 1 });
            player.runCommand(`scriptevent js:stop_ambience`);
            player.runCommand(`scriptevent js:ambience`);
          }, 5);


          loading.setScore(player, 0);
          const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
          player.uiManager.updateLoading(scaledScore)

        }
      }
    }

    if (block?.typeId == "js:facility_door") {
      const blockLocation = block.location;

      const topHalf = block.permutation.getState("js:upper_block_bit");
      const open = block.permutation.getState("js:open_bit");
      const doorType = block.permutation.getState("js:door_type");

      let openSound = "door.facility.open"
      let closeSound = "door.facility.close"
      if (doorType == 1) {
        openSound = "door.mansion.open"
        closeSound = "door.mansion.close"
      }

      let doorPart = undefined;
      if (topHalf == false) {
        //gets the block above
        try {
          doorPart = block.above(1);
        } catch { }
      } else
        try {
          doorPart = block.below(1);
        } catch { }

      let locked = block.permutation.getState("js:locked");
      let doorCooldown = block.permutation.getState("js:cooldown");

      const blocks = [block, doorPart];

      if (doorCooldown == false && open == false && locked == false && currentLoading <= 975) {
        loading.addScore(player, facilityDoor);
        const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
        player.uiManager.updateLoading(scaledScore)

      } else if (doorCooldown == false && locked == true) {
        const inv = player.getComponent("inventory").container
        const handItem = inv.getItem(player.selectedSlotIndex);
        if (handItem?.typeId == "js:key") {
          OVERWORLD.playSound("door.facility.unlock", blockLocation, { volume: 1, pitch: 1 });

          for (const door of blocks) {
            try {
              door.setPermutation(door.permutation.withState("js:locked", false));
            } catch { }
          }
          inv.setItem(player.selectedSlotIndex, undefined)
          updateWeight(player)
        }
      } else if (doorCooldown == false && open == true && locked == false) {
        OVERWORLD.playSound(closeSound, blockLocation, { volume: 1, pitch: 1 });

        for (const door of blocks) {
          try {
            door.setPermutation(door.permutation.withState("js:open_bit", false));
            door.setPermutation(door.permutation.withState("js:cooldown", true));
            system.runTimeout(() => {
              try {
                door.setPermutation(door.permutation.withState("js:cooldown", false));
              } catch { }
            }, 10);
          } catch { }
        }
      } else if (doorCooldown == false && open == false && locked == false && currentLoading >= 1000) {
        OVERWORLD.playSound(openSound, blockLocation, { volume: 1, pitch: 1 });

        for (const door of blocks) {
          try {
            door.setPermutation(door.permutation.withState("js:open_bit", true));
            door.setPermutation(door.permutation.withState("js:cooldown", true));
            system.runTimeout(() => {
              try {
                door.setPermutation(door.permutation.withState("js:cooldown", false));
              } catch { }
            }, 10);
          } catch { }
        }
      }
    }
    if (block?.typeId == "ding:door_buttons_block") {
      OVERWORLD.playSound("buttons.door.press", block.location, SOUND_OPTIONS);

      const inOrbit = world.getDynamicProperty("inOrbit");
      if (inOrbit == true) return;
      const door = shipEntity.getProperty("js:door");

      if (door == 0) {
        block.setPermutation(block.permutation.withState("ding:cooldown", 1));
        shipEntity.setProperty("js:door", 1);
      } else {
        block.setPermutation(block.permutation.withState("ding:cooldown", 1));
        shipEntity.setProperty("js:door", 0);
      }
    }

    if (block?.typeId == "ding:ship_lever") {
      const playerLocation = player.location;
      const blockLocation = block.location;
      const distance = getDistance(playerLocation, blockLocation);
      if (distance >= 3) return;

      const leverCooldownState = block.permutation.getState("js:cooldown");

      if (leverCooldownState == false && currentLoading <= 990) {
        loading.addScore(player, shipLever);
        const scaledScore = Math.min(Math.floor(currentLoading / 10), 100);
        player.uiManager.updateLoading(scaledScore)

      }

      if (leverCooldownState == false && currentLoading >= 1000) {
        const leverLocation = block.location;

        block.setPermutation(block.permutation.withState("js:cooldown", true));
        system.runTimeout(() => {
          try {
            block.setPermutation(block.permutation.withState("js:cooldown", false));
          } catch { }
        }, 10);

        player.teleport(
          { x: leverLocation.x + 0.5, y: leverLocation.y - 1, z: leverLocation.z + 1.25 },
          { rotation: { x: 20, y: 180 } }
        );
        player.runCommand(`inputpermission set @a movement disabled`);
        player.runCommand(`inputpermission set @a camera disabled`);
        player.setProperty("js:player_animation", 2);

        shipEntity.runCommand(`playanimation @s animation.ship.lever`);

        OVERWORLD.setBlockType(leverLocation, "minecraft:air");

        system.runTimeout(() => {
          player.playSound("buttons.lever.flick", { volume: 1, pitch: 1 });
          world.sendMessage(`§9${player.nameTag} started the ship.`)
        }, 0.36 * 20);
        
        system.runTimeout(() => {
          worldPlayers.forEach((player) => {
            player.camera.fade({
              fadeTime: { fadeInTime: 0.25, holdTime: 0.5, fadeOutTime: 0.25 },
            });
            player.uiManager.updateGenerationPercent(0)
          });
        }, 1.2 * 20);

        system.runTimeout(() => {
          player.setProperty("js:player_animation", 0);

          const inOrbit = world.getDynamicProperty("inOrbit");
          const moonId = world.getDynamicProperty("moon");
          if (inOrbit == true) {
            for (let player of worldPlayers) {
              player.runCommand(`fill ~-12 ~-12 ~-12 ~12 ~12 ~12 air replace ding:terminal_block`);
              uiManager.closeAllForms(player);
              player.setProperty("js:player_animation", 0);
            }

            if (moonId !== 0) {
              player.runCommand(`scriptevent js:generate`);
            } else {
              shipEntity.runCommand(`scriptevent js:shipEnter 1`);
            }
          } else {
            shipEntity.runCommand(`scriptevent js:shipLeave 1 none`);
          }
        }, 1.6 * 20);
      }
    }

    const inOrbit = world.getDynamicProperty("inOrbit");
    if (block?.typeId == "ding:ship_charger") {
      const chargerCooldownState = block.permutation.getState("js:cooldown");
      const inventory = player.getComponent("inventory").container;
      const mainhand = inventory.getItem(player.selectedSlotIndex);
      if (mainhand && ITEM_LIST[mainhand.typeId] && ITEM_LIST[mainhand.typeId].battery_life) {
        if (chargerCooldownState == false) {
          const chargerLocation = block.location;
          block.setPermutation(block.permutation.withState("js:cooldown", true));
          player.teleport(
            { x: chargerLocation.x + 0.4, y: chargerLocation.y - 1, z: chargerLocation.z + 0.5 },
            { rotation: { x: 0, y: -90 } }
          );

          player.inputPermissions.movementEnabled = false
          player.inputPermissions.cameraEnabled = false

          player.setProperty("js:player_animation", 3);

          let currentEnergy = mainhand.getDynamicProperty("battery") ?? 0;
          currentEnergy = ITEM_LIST[mainhand.typeId].battery_life;
          mainhand.setDynamicProperty("battery", currentEnergy);
          inventory.setItem(player.selectedSlotIndex, mainhand);

          player.playSound("items.charge", SOUND_OPTIONS);
          system.runTimeout(() => {
            OVERWORLD.spawnParticle("ding:charge", {
              x: chargerLocation.x + 0.75,
              y: chargerLocation.y + 0.6,
              z: chargerLocation.z + 0.5,
            });
          }, 0.75 * 20);
          system.runTimeout(() => {
            player.setProperty("js:player_animation", 0);

            player.inputPermissions.movementEnabled = true
            player.inputPermissions.cameraEnabled = true
          }, 2 * 20);
        }
      }
      if (mainhand && mainhand.typeId == "js:shovel" && inOrbit == false) {
        if (chargerCooldownState == false) {
          const chargerLocation = block.location;
          block.setPermutation(block.permutation.withState("js:cooldown", true));
          player.teleport(
            { x: chargerLocation.x + 0.4, y: chargerLocation.y - 1, z: chargerLocation.z + 0.5 },
            { rotation: { x: 0, y: -90 } }
          );
          player.inputPermissions.movementEnabled = false
          player.inputPermissions.cameraEnabled = false
          player.setProperty("js:player_animation", 3);

          player.playSound("items.charge", SOUND_OPTIONS);
          system.runTimeout(() => {
            OVERWORLD.spawnParticle("ding:charge", {
              x: chargerLocation.x + 0.75,
              y: chargerLocation.y + 0.6,
              z: chargerLocation.z + 0.5,
            });

            if (player.nameTag !== "Thebonniestar") {
              const random = Math.random() * 100;
              if (random < 25) {
                player.runCommand(`summon lightning_bolt`);
              }
            } else {
              player.runCommand(`summon lightning_bolt`);
            }
          }, 0.6 * 20);
          system.runTimeout(() => {
            player.setProperty("js:player_animation", 0);

            player.inputPermissions.movementEnabled = true
            player.inputPermissions.cameraEnabled = true
          }, 2 * 20);
        }
      }
    }
  });
});


let runTimeTimer = 0

system.runInterval(() => {
  for (let player of worldPlayers) {
    const health = player.getComponent("health");
    if (player.criticallyInjured == true && health.currentValue < 20) {
      let newHealth = health.currentValue + 1;
      console.warn(newHealth);
      health.setCurrentValue(newHealth);
      OVERWORLD.spawnParticle("ding:blood_drop_2", player.location);
    }
  }

  for (const id in lightningStrikes) {
    const strike = lightningStrikes[id];

    if (!strike.location) continue;

    if (strike.targetEntity && strike.targetEntity.typeId == "minecraft:player") {
      strike.targetEntity.playAnimation('animation.employee.charge')
    } else {
      OVERWORLD.spawnParticle("ding:sparking", strike.location);
    }
  }

  runTimeTimer++

  if (runTimeTimer > 5) {
    // updateSpectators()
    runTimeTimer = 0
  }

  if (world.getDynamicProperty('js:teleporter_cooldown') > 0) {
    world.setDynamicProperty('js:teleporter_cooldown', world.getDynamicProperty('js:teleporter_cooldown') - 1)
  }
  if (world.getDynamicProperty('js:inverse_teleporter_cooldown') > 0) {
    world.setDynamicProperty('js:inverse_teleporter_cooldown', world.getDynamicProperty('js:inverse_teleporter_cooldown') - 1)
  }
}, 20);

world.afterEvents.entityHurt.subscribe((ev) => {
  const damage = ev.damage;
  const source = ev.damageSource;
  const hurtEntity = ev.hurtEntity;

  if (hurtEntity.typeId !== "minecraft:player") return;
  const cause = source.cause;
  const hitter = source.damagingEntity || source.damagingProjectile;

  // if (source.damagingProjectile) {
  //   console.warn(`ehdbgvfh`)
  // }

  const health = hurtEntity.getComponent("minecraft:health");

  if (hitter && (hitter.typeId == "js:bunker_spider") && health.currentValue > 0) {
    OVERWORLD.playSound("monster.bunker_spider.attack", hurtEntity.location, SOUND_OPTIONS);
  }

  hurtEntity.runCommand(`camerashake add @s 0.3 0.2 rotational`);

  if (health.currentValue <= 0) {
    if (hitter.typeId == "js:bunker_spider") {
      OVERWORLD.playSound("monster.bunker_spider.kill", hurtEntity.location, SOUND_OPTIONS);
      hurtEntity.causeOfDeath = EntityDamageCause.contact;
    }
    if (hitter.typeId == "js:hoarding_bug" || hitter.typeId == "js:thumper") {
      hurtEntity.causeOfDeath = EntityDamageCause.contact;
    }
    if (hitter.typeId == "js:bullet" || hitter.typeId == "js:turret") {
      hurtEntity.causeOfDeath = EntityDamageCause.projectile;
    }
    if (
      hitter.typeId !== "js:bunker_spider" &&
      hitter.typeId !== "js:hoarding_bug" &&
      hitter.typeId !== "js:thumper" &&
      hitter.typeId !== "js:company" && !hurtEntity.causeOfDeath
    ) {
      hurtEntity.causeOfDeath = cause;
    }

    console.warn(`Hurt ${hurtEntity.causeOfDeath}`);
  }
});


world.afterEvents.entityHitEntity.subscribe((event) => {
  const hit = event.hitEntity;
  const hitter = event.damagingEntity;
  const hitTypeId = hit.typeId.split("_ent")[0];

  if (ITEM_LIST[hitTypeId] && hitter.typeId == "js:hoarding_bug") {
    OVERWORLD.playSound(ITEM_LIST[hitTypeId].pick_up_sound, hit.location, SOUND_OPTIONS);

    const rideComponent = hitter.getComponent("minecraft:rideable");

    rideComponent.addRider(hit);

    system.runTimeout(() => {
      hit.addTag("stashed");

      hit.setDynamicProperty("owner", hitter.id);
    }, 10);
    hitter.triggerEvent("return");
  }

  if (hitter.typeId == "js:bracken" && hit.typeId == "minecraft:player") {
    const bracken = hitter;
    let brackenTarget = hit;
    brackenTarget.inputPermissions.movementEnabled = false
    bracken.triggerEvent('attack')

    console.warn(brackenTarget.nameTag);

    const brackenRideComponent = bracken.getComponent("minecraft:rideable");
    const brackenRiders = brackenRideComponent.getRiders();

    const work = brackenRideComponent.addRider(brackenTarget);
    console.warn(`thing ${work}`)
    //brackenTarget.runCommand(`ride @s start_riding @e[type=js:bracken,c=1,r=3.5]`);
    system.runTimeout(() => {
      damagePlayer(brackenTarget, 100, bracken, EntityDamageCause.charging);
      brackenTarget.triggerEvent('not_in_facility')
      system.runTimeout(() => {
        brackenTarget.inputPermissions.movementEnabled = true
        const body = OVERWORLD.getEntities({
          type: "js:body_ent",
          location: bracken.location,
          maxDistance: 6,
          closest: 1,
        });
        brackenRideComponent.addRider(body[0]);
      }, 5);
    }, 20 * 0.96);

  }

  if (hitter.typeId == "js:forest_keeper" && hit.typeId == "minecraft:player") {
    const victim = hit
    const entity = hitter

      if (victim) {
        victim.inputPermissions.movementEnabled = false
        victim.inputPermissions.cameraEnabled = false
        const entityYRot = entity.getRotation().y
        entity.triggerEvent('attack')
        let yRot  
        if (entityYRot < 0) {
          yRot = entityYRot - 180
        } else {
          yRot = entityYRot + 180
        }

        victim.setRotation({ x: entity.getRotation().x, y: yRot })

        victim.playAnimation('animation.forest_keeper.attack_employee')

        console.warn(victim.nameTag);
        victim.runCommand(`execute positioned as @e[type=js:forest_keeper,r=12,c=1] as @a[name="${victim.nameTag}"] anchored eyes run camera @s set minecraft:free pos ~ ~6.5 ~7 facing @s`)
        // victim.runCommand(`execute positioned as @e[type=js:forest_keeper,c=1] rotated as @e[type=js:forest_keeper,c=1] positioned ^ ^ ^5 run camera @a[name="${victim.nameTag}"] set minecraft:free pos ~ ~10.5 ~ facing @s`)
        
  
        const rideComponent = entity.getComponent("minecraft:rideable");
        const riders = rideComponent.getRiders();
  
        victim.setRotation({ x: entity.getRotation().x, y: yRot })
        
        const work = rideComponent.addRider(victim);
        // console.warn(`thing ${work}`)
        // system.runTimeout(() => {
        //   damagePlayer(victim, 100, entity, EntityDamageCause.override);
        //   victim.setProperty('js:sus_meter', 0)
        //   victim.triggerEvent('not_in_facility')
        //   system.runTimeout(() => {
        //     victim.inputPermissions.movementEnabled = true
        //     victim.inputPermissions.cameraEnabled = true
        //   }, 5);
        // }, 5.0 * 20);

        console.warn(`before `, victim.attackedTimeout)
        victim.attackedTimeout = system.runTimeout(() => {
          damagePlayer(victim, 100, entity, EntityDamageCause.override);
          victim.setProperty('js:sus_meter', 0)
          victim.triggerEvent('not_in_facility')
          system.runTimeout(() => {
            victim.inputPermissions.movementEnabled = true
            victim.inputPermissions.cameraEnabled = true
          }, 5);
        }, 5.0 * 20);
        
        console.warn(`attacked`, victim.attackedTimeout)
        
      } else {
        console.warn(`No player found`)
      }
  }
});

function openDoors(entity) {
  if (entity.openingDoor == true) return;
  try {
    const blockLocations = [
      OVERWORLD.getBlock({ x: entity.location.x, y: entity.location.y, z: entity.location.z }),
      OVERWORLD.getBlock({ x: entity.location.x, y: entity.location.y, z: entity.location.z - 1 }),
      OVERWORLD.getBlock({ x: entity.location.x, y: entity.location.y, z: entity.location.z + 1 }),
      OVERWORLD.getBlock({ x: entity.location.x + 1, y: entity.location.y, z: entity.location.z }),
      OVERWORLD.getBlock({ x: entity.location.x - 1, y: entity.location.y, z: entity.location.z }),
      OVERWORLD.getBlock({ x: entity.location.x - 1, y: entity.location.y, z: entity.location.z + 1 }),
      OVERWORLD.getBlock({ x: entity.location.x - 1, y: entity.location.y, z: entity.location.z - 1 }),
      OVERWORLD.getBlock({ x: entity.location.x + 1, y: entity.location.y, z: entity.location.z - 1 }),
      OVERWORLD.getBlock({ x: entity.location.x + 1, y: entity.location.y, z: entity.location.z + 1 }),
    ];
    for (let block of blockLocations) {
      if (block.typeId == "js:facility_door") {
        const blockLocation = block.location;
        const topHalf = block.permutation.getState("js:upper_block_bit");
        const open = block.permutation.getState("js:open_bit");
        const locked = block.permutation.getState("js:locked");

        if (open == false && locked == false) {
          const doorOpenSpeed = ENTITIES[entity.typeId].door_open_speed;
          entity.openingDoor = true;
          system.runTimeout(() => {
            let doorPart = undefined;
            if (topHalf == false) {
              //gets the block above
              try {
                doorPart = block.above(1);
              } catch { }
            } else
              try {
                doorPart = block.below(1);
              } catch { }

            let doorCooldown = block.permutation.getState("js:cooldown");

            const blocks = [block, doorPart];

            OVERWORLD.playSound("door.facility.open", blockLocation, { volume: 1, pitch: 1 });

            for (const door of blocks) {
              try {
                door.setPermutation(door.permutation.withState("js:open_bit", true));
                door.setPermutation(door.permutation.withState("js:cooldown", true));
                system.runTimeout(() => {
                  try {
                    door.setPermutation(door.permutation.withState("js:cooldown", false));
                  } catch { }
                }, 10);
              } catch { }
            }

            entity.openingDoor = false;
          }, 20 * doorOpenSpeed);
        }
      }
    }
  } catch { }
}

const IGNORE_BLOCKS = [
  "ding:stair_rail_collision",
  "ding:stairs_bottom",
  "ding:stairs_top",
  "ding:stairs_top_rail",
  "ding:hand_rail",
  "minecraft:purpur_stairs",
];

// function lookMechanics(player) {
//   const entityRaycast = player.getEntitiesFromViewDirection({ maxDistance: 24 })[0];
//   const entity = entityRaycast?.entity;
//   /*
//     if (entity && entity.typeId === "js:coil_head") {


//         entity.addEffect("slowness", 2, {
//             amplifier: 255,
//             showParticles: false
//         });
//         entity.clearVelocity();
//     }
//     */

//   if (entity && entity.typeId === "js:hoarding_bug") {
//     entity.addEffect("bad_omen", 2, {
//       amplifier: 255,
//       showParticles: false,
//     });
//   }
//   // if (entity && entity.typeId === "js:bracken") {
//   //   const isSaddled = entity.hasComponent("minecraft:is_saddled");

//   //   if (isSaddled) return;
//   //   if (entity.)
//   //   entity.addEffect("bad_omen", 2, {
//   //     amplifier: 255,
//   //     showParticles: false,
//   //   });
//   // }
// }

function coilHeadLook(player) {
  const dist = 12;
  const spreadAngle = 80;
  const verticalSpreadAngle = 1;
  const rayCount = 7;

  const baseDirection = player.getViewDirection();
  const stepAngleHorizontal = spreadAngle / (rayCount - 1);
  const stepAngleVertical = verticalSpreadAngle / (rayCount - 1);

  const hitEntitiesSet = new Set();

  for (let i = 0; i < rayCount; i++) {
    for (let j = 0; j < rayCount; j++) {
      const horizontalAngle = -spreadAngle / 2 + i * stepAngleHorizontal;
      const verticalAngle = -verticalSpreadAngle / 2 + j * stepAngleVertical;
      const spreadDirection = calculateSpreadDirection(baseDirection, horizontalAngle, verticalAngle);
      const sourceDir = {
        x: spreadDirection.x,
        y: spreadDirection.y,
        z: spreadDirection.z,
      };

      // spawnRayParticles(player.dimension, player.getHeadLocation(), sourceDir, dist);

      const hitEntities = player.dimension
        .getEntitiesFromRay(player.getHeadLocation(), sourceDir, { maxDistance: dist })
        .map((result) => result.entity)
        .filter((hitEntity) => hitEntity !== player);

      hitEntities.forEach((hitEntity) => {
        if (!hitEntitiesSet.has(hitEntity) && hitEntity.typeId == "js:coil_head") {
          hitEntitiesSet.add(hitEntity);
          hitEntity.addEffect("slowness", 2, {
            amplifier: 255,
            showParticles: false,
          });
          hitEntity.clearVelocity();
        }
      });
      hitEntitiesSet.clear();
    }
  }
}

/*
WEATHER
clear 0
foggy 1
rainy 2
flooded 3
stormy 4
eclipsed 5
*/

export function startWeather() {
  const inOrbit = world.getDynamicProperty("inOrbit");
  if (inOrbit == false) {
    //console.warn(`uee`)
    const weather = world.getDynamicProperty("currentWeather");
    //  console.warn(weather)

    if (weather == 2) {
      OVERWORLD.setWeather(WeatherType.Rain, 100000);
    } else if (weather == 3) {
      OVERWORLD.setWeather(WeatherType.Rain, 100000);

      for (let player of worldPlayers) {
        const water = player.dimension.getEntities({name:player.nameTag,type:'ding:water'})
        if (water) {
          for (let waterEnt of water) {
            waterEnt.remove()
          }
        }
        player.runCommand(`scriptevent js:water`)
      }


    } else if (weather == 4) {
      OVERWORLD.setWeather(WeatherType.Thunder, 100000);

      const stormyEvent = OVERWORLD.getEntities({ type: "js:stormy_event" });
      if (stormyEvent.length <= 0 || !stormyEvent) {
        const storm = OVERWORLD.spawnEntity("js:stormy_event", {
          x: shipEntity.location.x,
          y: shipEntity.location.y - 5,
          z: shipEntity.location.z,
        });
      } else {
        for (let storm of stormyEvent) {
          storm.remove();
        }
        const storm = OVERWORLD.spawnEntity("js:stormy_event", {
          x: shipEntity.location.x,
          y: shipEntity.location.y - 5,
          z: shipEntity.location.z,
        });
      }
    } else if (weather !== 2 && weather !== 3 && weather !== 4) {
      OVERWORLD.setWeather(WeatherType.Clear);
    }
  } else {
    OVERWORLD.setWeather(WeatherType.Clear);
    const stormyEvent = OVERWORLD.getEntities({ type: "js:stormy_event" });
    for (let storm of stormyEvent) {
      storm.remove();
    }

    const floodWater = OVERWORLD.getEntities({ type: "ding:water" });
    for (let water of floodWater) {
      water.remove();
    }
  }
}


const soundLoops = new Map();
let loopCounter = 0;

function playLoopedSound(soundName, soundDuration, soundLocation) {
  loopCounter++;

  const loopId = `${soundName}_${loopCounter}`;

  const intervalId = system.runInterval(() => {
    OVERWORLD.playSound(soundName, soundLocation);
  }, soundDuration * 20);

  soundLoops.set(loopId, { soundName, soundDuration, soundLocation, intervalId });
}

function stopLoopedSound(soundName) {
  for (const [loopId, loopData] of soundLoops) {
    if (loopData.soundName === soundName) {
      system.clearRun(loopData.intervalId);

      soundLoops.delete(loopId);
      break;
    }
  }
}

const lightningStrikes = {}; // Global variable to track lightning locations
let updateInterval 

function startLightningStrike(id, initialLocation, targetEntity, inventorySlot = null) {
  // Add a new lightning strike to the tracking object
  lightningStrikes[id] = {
    location: initialLocation,
    targetEntity: targetEntity,
    inventorySlot: inventorySlot,
  };

  OVERWORLD.playSound("ambient.lightning.static", initialLocation, SOUND_OPTIONS);
  targetEntity.setDynamicProperty("lightningTarget", true);
  targetEntity.setDynamicProperty("strikeId", id); // Set strikeId on the target entity

  if (targetEntity instanceof Player) {
    let player = targetEntity;

    // Set a custom tag on the item if it's associated with a player
    if (inventorySlot !== null) {
      const item = player.getComponent("inventory").container.getItem(inventorySlot);
      if (item) {
        item.setDynamicProperty("lightningTarget", true);
        item.setDynamicProperty("strikeId", id); // Set strikeId on the item
        player.getComponent("inventory").container.setItem(inventorySlot, item);


        console.warn(item.getDynamicProperty('lightningTarget'))
        console.warn(item.getDynamicProperty('strikeId'))
      }
    }

    // Periodically update the lightning strike location based on the player's movement
    updateInterval = system.runInterval(() => {
      lightningStrikes[id].location = player.location;
    });

    // Stop tracking when the player no longer holds the item or after a timeout
    system.runTimeout(() => {
      system.clearRun(updateInterval);
    }, 10 * 20);
  } else {
    targetEntity.setDynamicProperty("lightningTarget", true);
    OVERWORLD.spawnParticle("ding:sparking", initialLocation);
  }

  system.runTimeout(() => {
    const strikeLocation = lightningStrikes[id].location;
    const topBlock = OVERWORLD.getTopmostBlock({ x: strikeLocation.x, z: strikeLocation.z });

    if (!topBlock) {
      OVERWORLD.spawnEntity('minecraft:lightning_bolt', strikeLocation)
    } else {
      const topLocation = topBlock.location;
      OVERWORLD.spawnEntity('minecraft:lightning_bolt', topLocation)
    }

    // Remove the strike from tracking after it has struck
    delete lightningStrikes[id];
  }, 10 * 20);
}



system.afterEvents.scriptEventReceive.subscribe((event) => {
  const entity = event.sourceEntity;
  if (event.id == "js:k") {
    console.warn(OVERWORLD.getBlock(entity.location).typeId)
  }

  if (event.id == "js:shipStructure") {
    OVERWORLD.runCommand(`execute as @e[type=js:ship] at @s run structure load mystructure:ship ~-6 ~42 ~-54`);

    system.runTimeout(() => {
      OVERWORLD.runCommand(
        `execute as @e[type=js:ship] at @s run fill ~2 ~44 ~-39 ~-2 ~47 ~-39 ding:ship_door_collision`
      );
    })
  }
  
  if (event.id == "js:shotgun_reloaded") {
    entity.setProperty('js:reloading', 0)
    const inventory = entity.getComponent('inventory').container
    const item = inventory.getItem(entity.selectedSlotIndex)
    
    let ammoSlot = 2

    for (let slot = 2; slot <= 6; slot++) {
      const item = inventory.getItem(slot);
      if (item && item.typeId == "js:shotgun_shell") {
        ammoSlot = slot
      }
    }

    if (item && item.typeId == "js:shotgun") {
      const bullets = item.getDynamicProperty('bullets') ?? 0
      // console.warn(bullets)
      item.setDynamicProperty('bullets', bullets + 1)
      inventory.setItem(entity.selectedSlotIndex, item)

      inventory.setItem(ammoSlot, undefined)
      updateWeight(entity)
    }
  }
  
  if (event.id == "js:toggle_shotgun") {
    const inventory = entity.getComponent('inventory').container
    const item = inventory.getItem(entity.selectedSlotIndex)
    if (item && item.typeId == "js:shotgun") {
      const safety = item.getDynamicProperty('safetyOn') ?? false
      // console.warn(bullets)
      if (safety == false) {
        item.setDynamicProperty('safetyOn', true)
        entity.playSound('items.shotgun.safety_on')
      } else {
        item.setDynamicProperty('safetyOn', false)
        entity.playSound('items.shotgun.safety_off')
      }
      inventory.setItem(entity.selectedSlotIndex, item)
    }
  }

  if (event.id == "js:shotgun_misfire") {
    const bullets = entity.getDynamicProperty('bullets') ?? 0
    const safety = entity.getDynamicProperty('safetyOn') ?? false

    if (safety == false && bullets > 0) {
      const random = Math.random()
      if (random < 0.05) {
        shotgunShoot(entity)
        entity.setDynamicProperty('bullets', entity.getDynamicProperty('bullets') - 1)
        entity.playAnimation('animation.shotgun.shoot')
      }
    }
  }

  if (event.id == "js:mecha_fire") {
    const random = Math.random()
    if (random < 0.05) {
      shotgunShoot(entity)
      entity.playAnimation('animation.shotgun.shoot')
    }
  }

  if (event.id == "js:shotgun_wielded_misfire") {
    shotgunWieldedMisfire(entity)
  }

  if (event.id == "js:spawnTrees") {
    spawnTrees(5, 256, "js:alive_tree");
  }
  if (event.id == "js:clearTrees") {
    const trees = OVERWORLD.getEntities({ type: "js:tree" });
    for (let tree of trees) {
      const pos1 = {
        x: tree.location.x,
        y: tree.location.y + 1,
        z: tree.location.z
      }
      const pos2 = {
        x: tree.location.x,
        y: tree.location.y + 10,
        z: tree.location.z
      }
      const fillArea = new BlockVolume(pos1, pos2)
      tree.dimension.fillBlocks(fillArea, 'minecraft:air', {blockFilter: {includeTypes: ["ding:tree_collision"]}})
      tree.remove();
    }
    const trees2 = OVERWORLD.getEntities({ type: "js:alive_tree" });
    for (let tree2 of trees2) {  
      const pos1 = {
        x: tree2.location.x,
        y: tree2.location.y + 1,
        z: tree2.location.z
      }
      const pos2 = {
        x: tree2.location.x,
        y: tree2.location.y + 10,
        z: tree2.location.z
      }
      const fillArea = new BlockVolume(pos1, pos2)
      tree2.dimension.fillBlocks(fillArea, 'minecraft:air', {blockFilter: {includeTypes: ["ding:tree_collision"]}})
      tree2.remove();
    }
  }

  if (event.id == "js:drop_item") {
    const eventEntity = event.sourceEntity

    const inventory = eventEntity.getComponent("inventory").container;
    const item = inventory.getItem(eventEntity.selectedSlotIndex);

    if (item && ITEM_LIST[item.typeId]) {
      if (item.lockMode == 'none') {
        dropItem(eventEntity, item, false)
      }
    }
  }

  if (event.id === "js:lightning_strike") {
    const items = OVERWORLD.getEntities({ location: shipEntity.location, maxDistance: 300, families: ["scrap"] });
    const players = OVERWORLD.getPlayers({ location: shipEntity.location, maxDistance: 300 });

    const validItems = items.filter((item) => {
      if (item.location.y < 55) return false;

      const itemId = item.typeId.split("_ent")[0];
      return ITEM_LIST[itemId] && ITEM_LIST[itemId].conductive === true;
    });

    let inventorySlot = null
    players.forEach((player) => {
      const inventory = player.getComponent("inventory").container;
      if (player.location.y >= 55) {
        for (let slot = 2; slot <= 6; slot++) {
          const item = inventory.getItem(slot);
          inventorySlot = slot
          if (item && ITEM_LIST[item.typeId] && ITEM_LIST[item.typeId].conductive === true) {
            validItems.push(player);
            break;
          }
        }
      }
    });

    if (validItems.length > 0) {
      const randomIndex = Math.floor(Math.random() * validItems.length);
      const chosenEntity = validItems[randomIndex];
      const strikeLocation = chosenEntity.typeId == "minecraft:player" ? chosenEntity.location : chosenEntity.location;

      console.warn(`Chosen entity: ${chosenEntity.typeId || "Player"} at location ${JSON.stringify(strikeLocation)}`);

      // Start a new lightning strike
      startLightningStrike(Date.now(), strikeLocation, chosenEntity, inventorySlot);
    }
  }

  if (event.id == "js:stop_ambience") {
    if (entity.outdoorAmbient) {
      system.clearRun(entity.outdoorAmbient);
    }
    if (entity.factoryAmbient) {
      system.clearRun(entity.factoryAmbient);
    }
    entity.runCommand(`stopsound @s game.ambient.factory`);
    entity.runCommand(`stopsound @s game.ambient.factory2`);
    entity.runCommand(`stopsound @s game.ambient.factory3`);
    entity.runCommand(`stopsound @s game.ambient.factory4`);
    entity.runCommand(`stopsound @s game.ambient.outside`);
    entity.runCommand(`stopsound @s game.ambient.outside_snow`);
    console.warn(`stopping  ambience`);
  }

  if (event.id == "js:ambience") {
    if (entity.outdoorAmbient) {
      system.clearRun(entity.outdoorAmbient);
    }
    if (entity.eclipsedAmbient) {
      system.clearRun(entity.eclipsedAmbient);
    }
    if (entity.factoryAmbient) {
      system.clearRun(entity.factoryAmbient);
    }
    entity.runCommand(`stopsound @s game.ambient.factory`);
    entity.runCommand(`stopsound @s game.ambient.factory2`);
    entity.runCommand(`stopsound @s game.ambient.factory3`);
    entity.runCommand(`stopsound @s game.ambient.factory4`);
    entity.runCommand(`stopsound @s game.ambient.eclipsed`);
    entity.runCommand(`stopsound @s game.ambient.outside`);
    entity.runCommand(`stopsound @s game.ambient.outside_snow`);

    const inOrbit = world.getDynamicProperty("inOrbit");
    if (inOrbit == true) return;

    const moonId = world.getDynamicProperty("moon");
    const weather = world.getDynamicProperty('currentWeather')

    const inFacility = entity.hasTag("inFacility");

    if (moonId !== 0) {
      if (inFacility) {
        entity.playSound('game.ambient.factory') //28.7
        entity.playSound('game.ambient.factory2') //27.1

        entity.factoryAmbient = system.runTimeout(() => {
          function playIndoorAmbience() {
            console.warn(`playing indoor ambience`);
            let ambience;
            let duration;

            const chance = Math.random() * 100;

            if (chance >= 0 && chance < 33) {
              ambience = "game.ambient.factory2";
              duration = 27.1;
            } else if (chance >= 33 && chance < 66) {
              ambience = "game.ambient.factory3";
              duration = 18.9;
            } else if (chance >= 66) {
              ambience = "game.ambient.factory4";
              duration = 14.3;
            }

            entity.playSound(ambience)

            if (entity.hasTag("inFacility")) {
              entity.factoryAmbient = system.runTimeout(playIndoorAmbience, duration * 20);
            } else {
              if (entity.factoryAmbient) {
                entity.clearRun(entity.factoryAmbient);
              }
            }
          }

          playIndoorAmbience();
        }, 27.1 * 20);
      } else {
        function playOutdoorAmbience() {
          console.warn(`playing outdoor ambience`);
          let ambience;
          let duration;

          if (moonId > 0 && moonId !== 7 && moonId !== 9) {
            ambience = "game.ambient.outside";
            duration = 13.7;
          }
          if (moonId == 7 || moonId == 9) {
            ambience = "game.ambient.outside_snow";
            duration = 15.9;
          }

          entity.playSound(ambience)

          entity.outdoorAmbient = system.runTimeout(playOutdoorAmbience, duration * 20);
        }

        playOutdoorAmbience();
        
        if (weather == 5) {
          function playEclipsedAmbience() {
            console.warn(`playing eclipsed ambience`);
            let ambience;
            let duration;

            ambience = "game.ambient.eclipsed";
            duration = 33.7;

            entity.playSound(ambience)

            entity.eclipsedAmbient = system.runTimeout(playEclipsedAmbience, duration * 20);
          }

          playEclipsedAmbience();
        }
      }
    } else {
      entity.playSound('game.ambient.factory2')
      entity.factoryAmbient = system.runTimeout(() => {
        function playIndoorAmbience2() {
          console.warn(`playing indoor ambience2`);
          const inOrbit = world.getDynamicProperty("inOrbit");
          let ambience;
          let duration;

          const chance = Math.random() * 100;

          if (chance >= 0 && chance < 33) {
            ambience = "game.ambient.factory2";
            duration = 27.1;
          } else if (chance >= 33 && chance < 66) {
            ambience = "game.ambient.factory3";
            duration = 18.9;
          } else if (chance >= 66) {
            ambience = "game.ambient.factory4";
            duration = 14.3;
          }

          entity.playSound(ambience)

          if (inOrbit == false) {
            entity.factoryAmbient = system.runTimeout(playIndoorAmbience2, duration * 20);
          } else {
            if (entity.factoryAmbient) {
              entity.clearRun(entity.factoryAmbient);
            }
          }
        }

        playIndoorAmbience2();
      }, 27.1 * 20);
    }
  }

  if (event.id == "js:playsound_ship_ambience") {
    const inOrbit = world.getDynamicProperty("inOrbit");
    let shipLocation = entity.location;
    if (inOrbit == true) {
      shipLocation = {
        x: shipLocation.x,
        y: shipLocation.y + 44,
        z: shipLocation.z - 40,
      };
    }
    OVERWORLD.playSound("misc.ship.ambience", shipLocation);
    OVERWORLD.playSound("misc.ship.ambience2", shipLocation);
    playLoopedSound("misc.ship.ambience", parseFloat(8.24), shipLocation);
    playLoopedSound("misc.ship.ambience2", parseFloat(9.36), shipLocation);
  }

  if (event.id == "js:stopsound_ship_ambience") {
    entity.runCommand(`stopsound @a[r=20] misc.ship.ambience`);
    entity.runCommand(`stopsound @a[r=20] misc.ship.ambience2`);
    stopLoopedSound("misc.ship.ambience");
    stopLoopedSound("misc.ship.ambience2");
  }

  if (event.id === "js:playsound") {
    const messageParts = event.message.split(" ");

    const soundNameInput = messageParts[0];
    const soundDurationInput = parseFloat(messageParts[1]);
    const locationInputs = messageParts.slice(2).map(parseFloat);

    console.warn(`${soundNameInput} ${soundDurationInput} ${locationInputs}`);

    let location = entity.location;
    if (locationInputs.length === 3) {
      // Assuming the input format is "x y z"
      location = new Vector3(locationInputs[0], locationInputs[1], locationInputs[2]);
    }

    OVERWORLD.playSound(soundNameInput, location);
    playLoopedSound(soundNameInput, soundDurationInput, location);
  }

  if (event.id == "js:stopsound") {
    const soundName = event.message.split(" ")[0].toString();
    entity.runCommand(`stopsound @a[r=12] soundName`);
    stopLoopedSound(soundName);
  }
});


export function getDirectionVector(start, end) {
  // Calculate the difference between the end and start coordinates
  const direction = {
    x: end.x - start.x,
    y: end.y - start.y,
    z: end.z - start.z,
  };

  // Normalize the direction vector (convert to unit vector)
  const magnitude = Math.sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z);
  if (magnitude > 0) {
    direction.x /= magnitude;
    direction.y /= magnitude;
    direction.z /= magnitude;
  }

  return direction;
}

function applyDamageAndEffects(hitEntity, sourceEntity, damage, newerHead, newLocation) {
  hitEntity.applyDamage(damage, { damagingEntity: sourceEntity, cause: EntityDamageCause.entityAttack });
  // hitEntity.dimension.spawnParticle("bd:ink_hit", newerHead);
  //OVERWORLD.playSound('mob.ink_demon_bd_dr.hurt', newLocation);
}

// Utility function to handle knockback
function handleKnockback(hitEntity, sourceEntity, force) {
  if (hitEntity.typeId === "minecraft:player") return;
  const direction = sourceEntity.getViewDirection();
  hitEntity.applyImpulse(new Vector3(direction.x * force, 0.01, direction.z * force));
}

// Utility function to calculate the direction vector with a spread angle
function calculateSpreadDirection(baseDirection, horizontalAngle, verticalAngle) {
  const radianHorizontal = horizontalAngle * (Math.PI / 180);
  const radianVertical = verticalAngle * (Math.PI / 180);

  const cosVert = Math.cos(radianVertical);
  const sinVert = Math.sin(radianVertical);

  return {
    x: baseDirection.x * Math.cos(radianHorizontal) - baseDirection.z * Math.sin(radianHorizontal),
    y: baseDirection.y * cosVert + sinVert,
    z: baseDirection.x * Math.sin(radianHorizontal) + baseDirection.z * Math.cos(radianHorizontal),
  };
}

function spawnRayParticles(dimension, startLocation, direction, maxDistance) {
  const stepSize = 4; // Distance between each particle
  for (let i = 0; i < maxDistance; i += stepSize) {
    const particleLocation = {
      x: startLocation.x + direction.x * i,
      y: startLocation.y + direction.y * i,
      z: startLocation.z + direction.z * i,
    };
    dimension.spawnParticle("minecraft:endrod", particleLocation);
  }
}

system.afterEvents.scriptEventReceive.subscribe((data) => {
  const { id, sourceEntity, message } = data;
  if (!message) return;

  const parm = message.split(" ");
  const dist = parseFloat(parm[0]);
  const spreadAngle = parseFloat(parm[1]) || 0; // Spread angle in degrees, default to 0 if not provided
  const verticalSpreadAngle = parseFloat(parm[2]) || 0; // Vertical spread angle in degrees, default to 0 if not provided
  const rayCount = parseInt(parm[3]) || 1; // Number of rays to cast, default to 1 if not provided

  switch (id) {
    case "js:scan":
      const baseDirection1 = sourceEntity.getViewDirection();
      const stepAngleHorizontal1 = spreadAngle / (rayCount - 1);
      const stepAngleVertical1 = verticalSpreadAngle / (rayCount - 1);

      const hitEntitiesSet1 = new Set();
      let totalValue = 0;

      for (let i = 0; i < rayCount; i++) {
        for (let j = 0; j < rayCount; j++) {
          const horizontalAngle = -spreadAngle / 2 + i * stepAngleHorizontal1;
          const verticalAngle = -verticalSpreadAngle / 2 + j * stepAngleVertical1;
          const spreadDirection = calculateSpreadDirection(baseDirection1, horizontalAngle, verticalAngle);
          const sourceDir = {
            x: spreadDirection.x,
            y: spreadDirection.y,
            z: spreadDirection.z,
          };

          // spawnRayParticles(sourceEntity.dimension, sourceEntity.getHeadLocation(), sourceDir, dist);

          const hitEntities = sourceEntity.dimension
            .getEntitiesFromRay(sourceEntity.getHeadLocation(), sourceDir, {
              maxDistance: dist,
              includePassableBlocks: true,
            })
            .map((result) => result.entity)
            .filter((hitEntity) => hitEntity !== sourceEntity);

          hitEntities.forEach((hitEntity) => {
            if (!hitEntitiesSet1.has(hitEntity)) {
              hitEntitiesSet1.add(hitEntity);

              const itemTypeId = hitEntity.typeId.split("_ent")[0];

              if (!ITEM_LIST[itemTypeId] && !ENTITIES[itemTypeId]) return;

              if (ITEM_LIST[itemTypeId]) {
                const value = hitEntity.getDynamicProperty("scrapValue");
                totalValue += value;

                const display = OVERWORLD.spawnEntity("js:scan_ent", {
                  x: hitEntity.location.x,
                  y: hitEntity.location.y - 0.25,
                  z: hitEntity.location.z,
                });

                let name = "§a" + ITEM_LIST[itemTypeId].name + ": " + value;

                if (itemTypeId == "js:apparatus" && hitEntity.getDynamicProperty("collected") == false) {
                  name = "§a" + ITEM_LIST[itemTypeId].name + ": " + "???";
                }

                if (itemTypeId == "js:body") {
                  const deathCause = DEATH_CAUSES[hitEntity.getDynamicProperty("causeOfDeath")];
                  name = "§c" + hitEntity.nameTag + "'s Body: " + value + `\n§cCause of Death: ${deathCause}`;
                }
                display.nameTag = name.toString();

                OVERWORLD.spawnParticle("ding:scan", {
                  x: hitEntity.location.x,
                  y: hitEntity.location.y + 0.25,
                  z: hitEntity.location.z,
                });
                display.addEffect("levitation", 5, { amplifier: 3, showParticles: false });
                system.runTimeout(() => {
                  display.clearVelocity();
                }, 5);
              } else if (ENTITIES[itemTypeId] && !(hitEntity instanceof Player)) {
                let display;
                if (itemTypeId == "js:ghost_girl") return;
                if (itemTypeId == "js:mine") {
                  display = OVERWORLD.spawnEntity("js:scan_ent", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y - 0.5,
                    z: hitEntity.location.z,
                  });

                  OVERWORLD.spawnParticle("ding:scan2", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y + 0.25,
                    z: hitEntity.location.z,
                  });
                } else if (itemTypeId == "js:turret") {
                  display = OVERWORLD.spawnEntity("js:scan_ent", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y + 1,
                    z: hitEntity.location.z,
                  });

                  OVERWORLD.spawnParticle("ding:scan2", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y + 1.25,
                    z: hitEntity.location.z,
                  });
                } else if (itemTypeId == "js:old_bird") {
                  display = OVERWORLD.spawnEntity("js:scan_ent", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y + 5,
                    z: hitEntity.location.z,
                  });

                  OVERWORLD.spawnParticle("ding:scan2", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y + 5.25,
                    z: hitEntity.location.z,
                  });
                }else {
                  display = OVERWORLD.spawnEntity("js:scan_ent", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y + 0.75,
                    z: hitEntity.location.z,
                  });

                  OVERWORLD.spawnParticle("ding:scan2", {
                    x: hitEntity.location.x,
                    y: hitEntity.location.y + 1.25,
                    z: hitEntity.location.z,
                  });
                }
                let name = "§c" + ENTITIES[itemTypeId].display_name;
                display.nameTag = name.toString();

                display.addEffect("levitation", 5, { amplifier: 3, showParticles: false });
                system.runTimeout(() => {
                  display.clearVelocity();
                }, 5);
              }
            }
          });
        }
      }
      sourceEntity.sendMessage(`dingUI:scan______${totalValue}`)

      hitEntitiesSet1.clear();

      break;
    case "js:swingShovel":
      const baseDirection = sourceEntity.getViewDirection();
      const stepAngleHorizontal = spreadAngle / (rayCount - 1);
      const stepAngleVertical = verticalSpreadAngle / (rayCount - 1);

      const rideComponent = sourceEntity.getComponent("minecraft:rideable");
      const riders = rideComponent.getRiders();
      for (let rider of riders) {
        if (rider.typeId == "js:snare_flea") {
          rideComponent.ejectRider(rider);

          rider.applyDamage(1, {
            cause: EntityDamageCause.entityAttack,
            damagingEntity: sourceEntity,
          });

          if (rider.getComponent("minecraft:health").currentValue <= 0) {
            OVERWORLD.playSound("monster.snare_flea.death", rider.location, SOUND_OPTIONS);
            rider.remove();
          }
        }
      }

      const hitEntitiesSet = new Set();

      for (let i = 0; i < rayCount; i++) {
        for (let j = 0; j < rayCount; j++) {
          const horizontalAngle = -spreadAngle / 2 + i * stepAngleHorizontal;
          const verticalAngle = -verticalSpreadAngle / 2 + j * stepAngleVertical;
          const spreadDirection = calculateSpreadDirection(baseDirection, horizontalAngle, verticalAngle);
          const sourceDir = {
            x: spreadDirection.x,
            y: spreadDirection.y,
            z: spreadDirection.z,
          };

          //spawnRayParticles(sourceEntity.dimension, sourceEntity.getHeadLocation(), sourceDir, dist);

          const hitEntities = sourceEntity.dimension
            .getEntitiesFromRay(sourceEntity.getHeadLocation(), sourceDir, { maxDistance: dist })
            .map((result) => result.entity)
            .filter((hitEntity) => hitEntity !== sourceEntity);

          hitEntities.forEach((hitEntity) => {
            if (!hitEntitiesSet.has(hitEntity)) {
              hitEntitiesSet.add(hitEntity);

              const hitLocation = hitEntity.getHeadLocation();
              const item = hitEntity.getComponent("minecraft:item")?.itemStack;
              const inOrbit = world.getDynamicProperty("inOrbit");

              const familyComponent = hitEntity.getComponent("minecraft:type_family");
              if (familyComponent && familyComponent.hasTypeFamily("scrap")) {
                return;
              }

              if (hitEntity !== sourceEntity && !item) {
                let damage;
                if (!(hitEntity instanceof Player)) {
                  damage = 1;
                  hitEntity.applyDamage(damage, {
                    cause: EntityDamageCause.entityAttack,
                    damagingEntity: sourceEntity,
                  });
                  console.warn(`Dealt ${damage} damage to ${hitEntity.typeId}`);
                } else {
                  damage = 20;
                  if (inOrbit == false) {
                    damagePlayer(hitEntity, damage, sourceEntity, EntityDamageCause.entityAttack);
                  }
                }

                if (ENTITIES[hitEntity.typeId]) {
                  OVERWORLD.playSound(ENTITIES[hitEntity.typeId].shovel_sound, hitLocation, SOUND_OPTIONS);

                  if (hitEntity.typeId == "js:thumper") {
                    hitEntity.speedTimer = 0;
                    hitEntity.playAnimation('animation.thumper.hurt')
                  }
                } else {
                  OVERWORLD.playSound("items.shovel.hit", hitLocation, SOUND_OPTIONS);
                }

                const range = SOUNDS["shovel"].range;
                const volume = SOUNDS["shovel"].volume;
                broadcastNoise("shovel", hitLocation, range, volume);
              }
            }
          });
        }
      }

      const block = sourceEntity.getBlockFromViewDirection({
        includeLiquidBlocks: true,
        includePassableBlocks: true,
        maxDistance: 3,
      });

      console.warn(block && block.block.typeId);
      if (block && BLOCKS[block.block.typeId]) {
        OVERWORLD.playSound(BLOCKS[block.block.typeId].hit_sound, block.block.location, SOUND_OPTIONS);
      } else if (block && !BLOCKS[block.block.typeId]) {
        OVERWORLD.playSound("items.shovel.hit", block.block.location, SOUND_OPTIONS);
      }
      if (block && block.block.typeId == "minecraft:web") {
        OVERWORLD.setBlockType(block.block.location, "minecraft:air");
        //sourceEntity.runCommand(`damage @e[type=js:bunker_spider] 0 entity_attack entity @s`);
        for (let spider of OVERWORLD.getEntities({ type: "js:bunker_spider" })) {
          if (!spider.target) {
            spider.applyDamage(1, {
              cause: EntityDamageCause.entityAttack,
              damagingEntity: sourceEntity,
            });
            const health = spider.getComponent("minecraft:health");
            health.setCurrentValue(health.currentValue + 1);
          }
        }
      }

      // Clear the temporary storage after processing all rays
      hitEntitiesSet.clear();

      break;

    default:
      break;
  }
});


export function ghostGirlSelectTarget() {
  // Filter out players with the "spectator" tag
  const eligiblePlayers = worldPlayers.filter(
    (player) => !player.hasTag("spectator")
  );

  if (eligiblePlayers.length === 0) {
    console.warn("No eligible players available (all are spectators).");
    return null;
  }

  // Assign initial ghostGirlScore
  for (let player of eligiblePlayers) {
    player.ghostGirlScore = 80;

    if (player.wasCriticallyInjured == false) {
      player.ghostGirlScore += 10;
    }
  }

  function ghostGirlPlayerCheck() {
    let highestParanoiaPlayer = null;
    let highestInsanityPlayer = null;
    let playersWithHighScrapValue = [];

    let maxParanoia = -Infinity;
    let maxInsanity = -Infinity;

    for (let player of eligiblePlayers) {
      const paranoia = player.paranoia || 0;
      const insanity = player.insanity || 0;

      // Find player with the highest paranoia
      if (paranoia > maxParanoia) {
        maxParanoia = paranoia;
        highestParanoiaPlayer = player;
      }

      // Find player with the highest insanity
      if (insanity > maxInsanity) {
        maxInsanity = insanity;
        highestInsanityPlayer = player;
      }

      // Calculate total scrap value in slots 2 to 6
      const inventory = player.getComponent("inventory").container;
      let totalScrapValue = 0;

      for (let slot = 2; slot <= 6; slot++) {
        const item = inventory.getItem(slot);
        if (item) {
          const scrapValue = item.getDynamicProperty("scrapValue") || 0;
          totalScrapValue += scrapValue;
        }
      }

      // Check if the total scrap value exceeds 150
      if (totalScrapValue > 150) {
        playersWithHighScrapValue.push({ player, totalScrapValue });
      }
    }

    return {
      highestParanoiaPlayer,
      maxParanoia,
      highestInsanityPlayer,
      maxInsanity,
      playersWithHighScrapValue,
    };
  }

  const results = ghostGirlPlayerCheck();

  if (results.highestParanoiaPlayer) {
    results.highestParanoiaPlayer.ghostGirlScore += 30;
  }
  if (results.highestInsanityPlayer) {
    results.highestInsanityPlayer.ghostGirlScore += 50;
  }
  if (results.playersWithHighScrapValue.length > 0) {
    results.playersWithHighScrapValue.forEach(({ player }) => {
      player.ghostGirlScore += 30;
    });
  } else {
    console.warn("No players with scrap value above 150.");
  }

  // Randomly select a player based on ghostGirlScore
  const totalWeight = eligiblePlayers.reduce(
    (sum, player) => sum + player.ghostGirlScore,
    0
  );
  let randomWeight = Math.random() * totalWeight;

  for (let player of eligiblePlayers) {
    if (randomWeight < player.ghostGirlScore) {
      console.log(`Selected player: ${player.name}`);
      return player; // Return the selected player
    }
    randomWeight -= player.ghostGirlScore;
  }

  console.warn("No player selected (this shouldn't happen).");
  return null;
}


const NON_SOLID_BLOCKS = ["minecraft:air", "minecraft:water", "minecraft:lava", "ding:hand_rail", "ding:march_grass_foilage"]; // Add more as needed
const MAX_RADIUS = 8;

function isSolidBlock(block) {
  return block && !NON_SOLID_BLOCKS.includes(block.typeId);
}

function calculateDirectionVector(start, end) {
  const dx = end.x - start.x;
  const dy = end.y - start.y;
  const dz = end.z - start.z;
  const magnitude = Math.sqrt(dx * dx + dy * dy + dz * dz);
  return { x: dx / magnitude, y: dy / magnitude, z: dz / magnitude };
}


// system.runInterval(() => {

//     let girl = OVERWORLD.getEntities({type:'js:ghost_girl'})[0]

//     if (girl) {
//       for (let player of worldPlayers) {
//         player.onScreenDisplay.setActionBar(`
//           Target ${girl.targettedPlayer.nameTag}  
//           State ${girl.getProperty('js:state')}
//           Seen ${girl.getDynamicProperty('timesSeen')}
//           Not Seen ${girl.getDynamicProperty('timesNotSeen')}
//           Looked ${girl.lookedAt}
//           Stared ${girl.stared}
//           Close ${girl.playerClose}
//           Timer ${girl.timer}
//         `)
//       }
//     }
// })


function findValidXZLocation(targetPlayer, allowLineOfSight = false) {
  const targetLocation = targetPlayer.location;
  const targetRotation = targetPlayer.getRotation();

  let candidateLocation = null;

  for (let attempts = 0; attempts < 100; attempts++) {
    const randomAngleRange = Math.floor(Math.random() * (25 - (-25) + 1)) - 25
    const angle = (targetRotation.y + randomAngleRange) / 180 * Math.PI //Player angle rad
    const distance = Math.floor(Math.random() * (16 - 6 + 1)) + 6

    // const angle = Math.random() * 2 * Math.PI; // Random angle in radians
    const offsetX = Math.sin(angle) * distance;
    const offsetZ = -Math.cos(angle) * distance;

    // Candidate position
    const x = targetLocation.x + offsetX;
    const z = targetLocation.z + offsetZ;
    let y = targetLocation.y;

    // Check for valid block conditions
    let block = OVERWORLD.getBlock({ x, y, z });
    while (!block || NON_SOLID_BLOCKS.includes(block.typeId)) {
      y -= 1; // Move downward
      block = OVERWORLD.getBlock({ x, y, z });
    }

    if (allowLineOfSight == false) {
      const directionVector = calculateDirectionVector(targetLocation, { x, y, z });
      const lineOfSightBlock = OVERWORLD.getBlockFromRay(targetLocation, directionVector, { maxDistance: distance });
  
      
      if (!lineOfSightBlock) {
        console.log(`Attempt ${attempts}: No clear line of sight to location (${x}, ${y}, ${z}).`);
        continue;
      }
  
      if (
        lineOfSightBlock.location &&
        (Math.floor(lineOfSightBlock.location.x) !== Math.floor(x) ||
          Math.floor(lineOfSightBlock.location.z) !== Math.floor(z))
      ) {
        console.log(
          `Attempt ${attempts}: Block at (${x}, ${y}, ${z}) is obstructed by another block.`
        );
        continue;
      }
    }

    // Valid candidate
    candidateLocation = { x: x, y: y + 1, z: z};
    console.log(`Valid location found on attempt ${attempts}: (${x}, ${y}, ${z})`);
    break;
  }

  if (!candidateLocation) {
    console.warn("Failed to find a valid location after 100 attempts.");
  }

  return candidateLocation;
}







export function ghostGirlSelectTargetLocation(ghostGirl, targetPlayer, chase = false) {
  ghostGirl.runCommand(`stopsound @a monster.ghost_girl.breathing`)
  let location
  if (chase == false) {
    location = findValidXZLocation(targetPlayer, false);
  } else {
    location = findValidXZLocation(targetPlayer, true);
  }
  if (location) {
    console.log(`Selected location: ${location.x}, ${location.y}, ${location.z}`);
    ghostGirl.teleport(location)
    if (chase == false) {
      ghostGirl.triggerEvent('staring')
      ghostGirl.runCommand(`execute as @s at @s run playsound monster.ghost_girl.stare @a[tag=ghostGirlTarget] ~~~`)
    }
    return location;
  } else {
    console.warn("No valid location found after 100 attempts.");
    return null;
  }
}




export function shotgunShoot(entity) {
  OVERWORLD.playSound('items.shotgun.shoot', entity.location, SOUND_OPTIONS)

  if (entity.typeId == "minecraft:player") {
    entity.runCommand(`camerashake add @s 0.05 0.12 rotational`)
    system.runTimeout(() => {
      entity.playSound('items.shotgun.ears_ringing')
    }, 0.56 * 20)
  }
  entity.runCommand(`camerashake add @a[r=12] 0.25 0.12 rotational`)

  let spreadAngle = 40
  let rayCount = 16
  let verticalSpreadAngle = 12
  let dist = 18
  const baseDirection = entity.getViewDirection();
  const stepAngleHorizontal = spreadAngle / (rayCount - 1);
  const stepAngleVertical = verticalSpreadAngle / (rayCount - 1);
  const hitEntitiesSet = new Set();
  
  for (let i = 0; i < rayCount; i++) {
    for (let j = 0; j < rayCount; j++) {
      const horizontalAngle = -spreadAngle / 2 + i * stepAngleHorizontal;
      const verticalAngle = -verticalSpreadAngle / 2 + j * stepAngleVertical;
      const spreadDirection = calculateSpreadDirection(baseDirection, horizontalAngle, verticalAngle);
      const sourceDir = {
        x: spreadDirection.x,
        y: spreadDirection.y,
        z: spreadDirection.z,
      };
  
      // spawnRayParticles(entity.dimension, entity.getHeadLocation(), sourceDir, dist);
  
      const hitEntities = entity.dimension
        .getEntitiesFromRay(entity.getHeadLocation(), sourceDir, { maxDistance: dist })
        .map((result) => result.entity)
        .filter((hitEntity) => hitEntity !== entity);
  
      hitEntities.forEach((hitEntity) => {
        if (!hitEntitiesSet.has(hitEntity)) {
          hitEntitiesSet.add(hitEntity);
  
          const hitLocation = hitEntity.getHeadLocation();
          const item = hitEntity.getComponent("minecraft:item")?.itemStack;
          const inOrbit = world.getDynamicProperty("inOrbit");
  
          const familyComponent = hitEntity.getComponent("minecraft:type_family");
          if (familyComponent && familyComponent.hasTypeFamily("scrap")) {
            return;
          }
  
          if (hitEntity !== entity && !item) {
            let damage;
            const distance = getDistance(hitEntity.location, entity.location)
            if (!(hitEntity instanceof Player)) {
              if (distance < 3.7) {
                damage = 5
              } else if (distance >= 3.7 && distance < 6) {
                damage = 3
              } else if (distance >= 6) {
                damage = 2
              }
              hitEntity.applyDamage(damage, {
                cause: EntityDamageCause.entityAttack,
                damagingEntity: entity,
              });
              console.warn(`Dealt ${damage} damage to ${hitEntity.typeId}`);
            } else {
              if (distance < 4.5) {
                damage = 100
              } else if (distance >= 4.5 && distance < 12) {
                damage = 40
              } else if (distance >= 12) {
                damage = 20
              }
              
              if (inOrbit == false) {
                damagePlayer(hitEntity, damage, entity, EntityDamageCause.maceSmash);
              }
            }
  
            const range = SOUNDS["shotgun"].range;
            const volume = SOUNDS["shotgun"].volume;
            broadcastNoise("shotgun", hitLocation, range, volume);
          }
        }
      });
    }
  }
}

export function shotgunWieldedMisfire(entity) {
  const inventory = entity.getComponent('inventory').container
  const item = inventory.getItem(entity.selectedSlotIndex)
  if (item && item.typeId == "js:shotgun") {
    const safety = item.getDynamicProperty('safetyOn') ?? false
    const bullets = item.getDynamicProperty('bullets') ?? 0
    
    if (safety == false && bullets > 0) {
      const random = Math.random()
      if (random < 0.05) {
        shotgunShoot(entity)
        item.setDynamicProperty('bullets', item.getDynamicProperty('bullets') - 1)
        inventory.setItem(entity.selectedSlotIndex, item)
      }
    }
  }
}




function debugRaycast(player, direction, distance, Interval = 3) {
  const start = player.getHeadLocation();
  const hitEntities = [];

  //particle visualization
  const rayEndPos = {
      x: start.x + direction.x * distance,
      y: start.y + direction.y * distance,
      z: start.z + direction.z * distance
  };
  debugLine(start, rayEndPos, 1, 1, 1)


  for (let i = 0; i <= distance; i += Interval) {
      const rayPos = {
          x: start.x + direction.x * i,
          y: start.y + direction.y * i,
          z: start.z + direction.z * i
      };
  }
}

function debugLine(loc1, loc2, r, g, b) {
  const pos1 = { x: loc1.x, y: loc1.y, z: loc1.z }
  const pos2 = { x: loc2.x, y: loc2.y, z: loc2.z }
  let variable = new MolangVariableMap()
  variable.setFloat('red', r)
  variable.setFloat('green', g)
  variable.setFloat('blue', b)
  const xRelative = ((pos2.x) - (pos1.x)) * 1
  const yRelative = ((pos2.y) - (pos1.y)) * -1
  const zRelative = ((pos2.z) - (pos1.z)) * 1
  variable.setFloat('target_pos_x', xRelative)
  variable.setFloat('target_pos_y', yRelative)
  variable.setFloat('target_pos_z', zRelative)
  world.getDimension('overworld').spawnParticle("ding:line", pos1, variable)
}



world.beforeEvents.chatSend.subscribe((ev) => {
  const sender = ev.sender
  const message = ev.message
  if (message.startsWith("!")) return
  ev.cancel = true
  
  let newMessage = `§9<${sender.nameTag}> ${message}`

  let receivingPlayers = []
  let nearbyTargets

  if (sender.hasTag("spectator")) {
    nearbyTargets = world.getPlayers({tags: ["spectator"]})
  } else {
    nearbyTargets = OVERWORLD.getPlayers({location: sender.location, maxDistance: 32})
  }

  // const operators = world.getPlayers({tags: ["debug"]})

  // receivingPlayers = [...nearbyTargets, ...operators]
  receivingPlayers = [...nearbyTargets]

  for (let player of receivingPlayers) {
    player.sendMessage(newMessage)

    if (sender.hasTag("inGame") && sender.textCooldown == 0) {
      const otherPlayers = OVERWORLD.getPlayers({location: sender.location, minDistance: 32, excludeTags: ["spectator"]})
      if (!sender.hasTag("spectator") && otherPlayers.length > 0) {
        sender.sendMessage(`§e(Some were too far to receive your message.)`)
        sender.textCooldown = 100
      }
    }
  }

})










