import * as mc from "@minecraft/server"
import V3 from "./utilities/V3";
import * as ui from "@minecraft/server-ui";
import './cyd_dino_pack';
import { menuMain, setupStaticMenus, showFogMenuSettings } from "./cyd_dino_menu";
import { handleRiderInteract } from "./cyd_dino_rider_interact";

import './bosses/cyd_dino_bosses_attack'
import './bosses/dark_draxar_boss'

import './cyd_dino_wind';
import './cyd_dino_trust';
import { updateTrust } from "./cyd_dino_trust";
import './cyd_dino_attack_effects';

import "./items/spike_trap";
import "./items/net_crossbow"
import "./items/bolas";
import "./items/slingshot";
import "./items/drak_horn";
import "./items/dark_scale_armor";
import "./items/skull_hook";
import "./items/spiked_shield";
import "./items/skull_axe";

import './hunters/hunters_main'

import "./misc/attacks"
import './misc/biome_toggler'

import './cyd_dino_structures'
import './cyd_dino_log'
import { fumerolleBlockBehavior } from "./blocks/fumerolle_block";
import { pahoehoeBlockBehavior } from "./blocks/pahoehoe_block";
import './blocks/bone_spikes';
import { getBiome } from "./misc/biome_settings";

export const VERSION = "1.0.0";
const PLAYER_TICK_INTERVAL = 1;

const SOUND_PROJECTILE_THROW = "cyd_lbb.bath_bomb_throw";
const SOUND_OPTIONS = { pitch: 1.0, volume: 1.0, };
const IMPULSE = 1.8;

const ancientFruitDropChances = new Map([
    ["cyd_dino:dicksonia_leaves", 0.20], // 20%
    ["cyd_dino:fan_palm_leaves", 0.20], // 20%
    ["cyd_dino:gunnera", 0.05], // 5%

    // TODO: check if these are gunnera plants
    ["cyd_dino:dicroidium", 0.05], // 5%
    ["cyd_dino:fiddlehead_fern", 0.05], // 5%
    ["cyd_dino:giant_horsetail", 0.05], // 5%
    ["cyd_dino:lycopod", 0.05], // 5%
    ["cyd_dino:neuropteris", 0.05], // 5%
])
const ANCIENT_FRUIT_ITEM_ID = "cyd_dino:ancient_fruit"

const trees = {
    araucaria: [
        `structure load "cyd_dino/trees/araucaria/big_araucaria_01" ~-5~~-5`,
        `structure load "cyd_dino/trees/araucaria/big_araucaria_02" ~-4~~-5`,
        `structure load "cyd_dino/trees/araucaria/big_araucaria_03" ~-5~~-5`,
        `structure load "cyd_dino/trees/araucaria/little_araucaria_01" ~-3~~-3`,
        `structure load "cyd_dino/trees/araucaria/little_araucaria_02" ~-2~~-2`,
        `structure load "cyd_dino/trees/araucaria/little_araucaria_03" ~-4~~-4`
    ],
    bennettitales: [
        `structure load "cyd_dino/trees/benne/big_benne_01" ~-1~~-1`,
        `structure load "cyd_dino/trees/benne/big_benne_02" ~-1~~-3`,
        `structure load "cyd_dino/trees/benne/big_benne_03" ~-1~~-3`,
        `structure load "cyd_dino/trees/benne/little_benne_01" ~-1~~-1`,
        `structure load "cyd_dino/trees/benne/little_benne_02" ~-1~~-1`,
        `structure load "cyd_dino/trees/benne/little_benne_03" ~-1~~-1`,
    ],
    cheirolepidiceae: [
        `structure load "cyd_dino/trees/cheiro/cheiro_01" ~-1~~-1`,
        `structure load "cyd_dino/trees/cheiro/cheiro_02" ~-1~~-1`,
        `structure load "cyd_dino/trees/cheiro/cheiro_03" ~-1~~-1`,
        `structure load "cyd_dino/trees/cheiro/cheiro_04" ~-1~~-1`,
        `structure load "cyd_dino/trees/cheiro/cheiro_05" ~-1~~-1`
    ],
    conifer: [
        `structure load "cyd_dino/trees/conifer/big_conifer_01" ~-3~~-3`,
        `structure load "cyd_dino/trees/conifer/big_conifer_02" ~-4~~-4`,
        `structure load "cyd_dino/trees/conifer/big_conifer_03" ~-3~~-3`,
        `structure load "cyd_dino/trees/conifer/little_conifer_01" ~-1~~-1`,
        `structure load "cyd_dino/trees/conifer/little_conifer_02" ~-1~~-1`,
        `structure load "cyd_dino/trees/conifer/little_conifer_03" ~-1~~-1`
    ],
    cordaites: [
        `structure load "cyd_dino/trees/cordaites/big_cordaites_01" ~-2~~-2`,
        `structure load "cyd_dino/trees/cordaites/big_cordaites_02" ~-2~~-2`,
        `structure load "cyd_dino/trees/cordaites/big_cordaites_03" ~-2~~-2`,
        `structure load "cyd_dino/trees/cordaites/little_cordaites_01" ~-1~~-1`,
        `structure load "cyd_dino/trees/cordaites/little_cordaites_02" ~-1~~-1`,
        `structure load "cyd_dino/trees/cordaites/little_cordaites_03" ~-1~~-1`
    ],
    cycads: [
        `structure load "cyd_dino/trees/cycads/cycads_01" ~-1~~-1`,
        `structure load "cyd_dino/trees/cycads/cycads_02" ~-1~~-1`,
        `structure load "cyd_dino/trees/cycads/cycads_03" ~-1~~-1`,
        `structure load "cyd_dino/trees/cycads/cycads_04" ~-2~~-2`,
        `structure load "cyd_dino/trees/cycads/cycads_05" ~-1~~-2`
    ],
    dicksonia: [
        `structure load "cyd_dino/trees/dicksonia/big_dicksonia_01" ~-2~~-2`,
        `structure load "cyd_dino/trees/dicksonia/big_dicksonia_02" ~-2~~-2`,
        `structure load "cyd_dino/trees/dicksonia/big_dicksonia_03" ~-2~~-2`,
        `structure load "cyd_dino/trees/dicksonia/little_dicksonia_01" ~-2~~-2`,
        `structure load "cyd_dino/trees/dicksonia/little_dicksonia_02" ~-2~~-2`
    ],
    fan_palm: [
        `structure load "cyd_dino/trees/fanpalm/fanpalm_01" ~-2~~-3`,
        `structure load "cyd_dino/trees/fanpalm/fanpalm_02" ~-2~~-2`,
        `structure load "cyd_dino/trees/fanpalm/fanpalm_03" ~-2~~-2`,
        `structure load "cyd_dino/trees/fanpalm/fanpalm_04" ~-2~~-2`,
        `structure load "cyd_dino/trees/fanpalm/fanpalm_05" ~-2~~-2`
    ],
    ginkgo: [
        `structure load "cyd_dino/trees/ginkgo/big_ginkgo_01" ~-7~~-6`,
        `structure load "cyd_dino/trees/ginkgo/big_ginkgo_02" ~-4~~-5`,
        `structure load "cyd_dino/trees/ginkgo/big_ginkgo_03" ~-6~~-6`,
        `structure load "cyd_dino/trees/ginkgo/little_ginkgo_01" ~-3~~-3`,
        `structure load "cyd_dino/trees/ginkgo/little_ginkgo_02" ~-2~~-2`,
        `structure load "cyd_dino/trees/ginkgo/little_ginkgo_03" ~-2~~-1`
    ],
    metasequoia: [
        `structure load "cyd_dino/trees/meta/big_meta_01" ~-3~~-2`,
        `structure load "cyd_dino/trees/meta/big_meta_02" ~-3~~-3`,
        `structure load "cyd_dino/trees/meta/big_meta_03" ~-2~~-2`,
        `structure load "cyd_dino/trees/meta/little_meta_01" ~-1~~-1`,
        `structure load "cyd_dino/trees/meta/little_meta_02" ~-1~~-1`,
        `structure load "cyd_dino/trees/meta/little_meta_03" ~-2~~-2`
    ],
    podocarps: [
        `structure load "cyd_dino/trees/podo/big_podo_01" ~-4~~-5`,
        `structure load "cyd_dino/trees/podo/big_podo_02" ~-4~~-4`,
        `structure load "cyd_dino/trees/podo/big_podo_03" ~-4~~-5`,
        `structure load "cyd_dino/trees/podo/little_podo_01" ~-3~~-4`,
        `structure load "cyd_dino/trees/podo/little_podo_02" ~-4~~-4`,
        `structure load "cyd_dino/trees/podo/little_podo_03" ~-2~~-2`
    ],
    sigillaria: [
        `structure load "cyd_dino/trees/sigillaria/big_sigillaria_01" ~-3~~-3`,
        `structure load "cyd_dino/trees/sigillaria/big_sigillaria_02" ~-5~~-4`,
        `structure load "cyd_dino/trees/sigillaria/big_sigillaria_03" ~-5~~-3`,
        `structure load "cyd_dino/trees/sigillaria/little_sigillaria_01" ~-3~~-4`,
        `structure load "cyd_dino/trees/sigillaria/little_sigillaria_02" ~-2~~-1`,
        `structure load "cyd_dino/trees/sigillaria/little_sigillaria_03" ~-2~~-2`
    ]
}

const dinos = ['nest_allosaurus', 'nest_anky', 'nest_archeo', 'nest_bary', 'nest_brachio', 'nest_carno', 'nest_ceratosaurus', 'nest_conchoraptor', 'nest_dime', 'nest_gigantspinosaurus', 'nest_iguan', 'nest_majungosaurus', 'nest_micro', 'nest_ohmdenosaurus', 'nest_pachy', 'nest_para', 'nest_psitacosaurus', 'nest_ptera', 'nest_ptera', 'nest_sinotyrannus', 'nest_spino', 'nest_stego', 'nest_stegouros', 'nest_styrac', 'nest_suchomimus', 'nest_trex', 'nest_trice', 'nest_tupandactylus', 'nest_utah', 'nest_velo', 'nest_zhanghenlong', 'nest_skull_velo', 'nest_skull_stegospheryx', 'nest_spycomellus']
const aquatics = ['nest_cameroceras', 'nest_dunkleosteus', 'nest_icty', 'nest_onchopristis', 'nest_prognathodon', 'nest_simolestes', 'nest_xiphactinus']
const flyers = ['cyd_dino:quetz', 'cyd_dino:ptera', 'cyd_dino:tupandactylus']
const herbivores = ["cyd_dino:anky", "cyd_dino:brachio", "cyd_dino:gigantspinosaurus", "cyd_dino:micro", "cyd_dino:ohmdenosaurus", "cyd_dino:pachy", "cyd_dino:para", "cyd_dino:psitacosaurus", "cyd_dino:stego", "cyd_dino:stegouros", "cyd_dino:styrac", "cyd_dino:trice", "cyd_dino:zhanghenlong", "cyd_dino:spycomellus"]

const bosses = ['cyd_dino:carnankylos', 'cyd_dino:stegospheryx', 'cyd_dino:mega', 'cyd_dino:empy', 'cyd_dino:dark_draxar_boss', "cyd_dino:skull_trex"]
const npcs = ['cyd_dino:gaiakin', 'cyd_dino:draxer', 'cyd_dino:green_merchant', 'cyd_dino:red_merchant', "cyd_dino:dark_draxar_npc", "cyd_dino:dark_draxar_merchant"]

export const dinoData = new Map([
    ["cyd_dino:allosaurus", { name: "Allosaurus", tier: "large" }],
    ["cyd_dino:anky", { name: "Ankylosaurus", tier: "medium" }],
    ["cyd_dino:archeo", { name: "Pyroraptor", tier: "small" }],
    ["cyd_dino:bary", { name: "Baryonyx", tier: "large" }],
    ["cyd_dino:brachio", { name: "Brachiosaurus", tier: "large" }],
    ["cyd_dino:cameroceras", { name: "Cameroceras", tier: "large" }],
    ["cyd_dino:carno", { name: "Carnotaurus", tier: "large" }],
    ["cyd_dino:ceratosaurus", { name: "Ceratosaurus", tier: "large" }],
    ["cyd_dino:conchoraptor", { name: "Conchoraptor", tier: "small" }],
    ["cyd_dino:dime", { name: "Dimetrodon", tier: "small" }],
    ["cyd_dino:dunkleosteus", { name: "Dunkleosteus", tier: "large" }],
    ["cyd_dino:gigantspinosaurus", { name: "Gigantspinosaurus", tier: "large" }],
    ["cyd_dino:icty", { name: "Ichtyosaurus", tier: "large" }],
    ["cyd_dino:iguan", { name: "Iguanodon", tier: "medium" }],
    ["cyd_dino:majungosaurus", { name: "Majungosaurus", tier: "medium" }],
    ["cyd_dino:micro", { name: "Microceratus", tier: "small" }],
    ["cyd_dino:ohmdenosaurus", { name: "Ohmdenosaurus", tier: "large" }],
    ["cyd_dino:onchopristis", { name: "Onchopristis", tier: "medium" }],
    ["cyd_dino:pachy", { name: "Pachycephalosaurus", tier: "medium" }],
    ["cyd_dino:para", { name: "Parasaurolophus", tier: "medium" }],
    ["cyd_dino:prognathodon", { name: "Prognathodon", tier: "large" }],
    ["cyd_dino:psitacosaurus", { name: "Psitacosaurus", tier: "small" }],
    ["cyd_dino:ptera", { name: "Pteranodon", tier: "large" }],
    ["cyd_dino:quetz", { name: "Quetzalcoatlus", tier: "large" }],
    ["cyd_dino:simolestes", { name: "Simolestes", tier: "large" }],
    ["cyd_dino:sinotyrannus", { name: "Sinotyrannus", tier: "medium" }],
    ["cyd_dino:spino", { name: "Spinosaurus", tier: "large" }],
    ["cyd_dino:stego", { name: "Stegosaurus", tier: "large" }],
    ["cyd_dino:stegouros", { name: "Stegouros", tier: "medium" }],
    ["cyd_dino:styrac", { name: "Styracosaurus", tier: "medium" }],
    ["cyd_dino:suchomimus", { name: "Suchomimus", tier: "medium" }],
    ["cyd_dino:trex", { name: "Tyranosaurus", tier: "large" }],
    ["cyd_dino:trice", { name: "Triceratops", tier: "medium" }],
    ["cyd_dino:tupandactylus", { name: "Tupandactylus", tier: "large" }],
    ["cyd_dino:utah", { name: "Utahraptor", tier: "small" }],
    ["cyd_dino:velo", { name: "Velociraptor", tier: "small" }],
    ["cyd_dino:xiphactinus", { name: "Xiphactinus", tier: "large" }],
    ["cyd_dino:zhanghenlong", { name: "Zhanghenlong", tier: "medium" }],
    ["cyd_dino:skull_velo", { name: "Skull Velociraptor", tier: "small_ash" }],
    ["cyd_dino:skull_trex", { name: "Skull Tyranosaurus", tier: "large_ash" }],
    ["cyd_dino:skull_stegospheryx", { name: "Skull Stegospheryx", tier: "medium_ash" }],
    ["cyd_dino:spycomellus", { name: "Spycomellus", tier: "medium" }],
])

export const actionNames = new Map([
    ["storage", "Storage"],
    ["stone_breaker", "Stone Breaker"],
    ["fisher", "Fisher"],
    ["flying", "Flying"],
    ["stomp", "Stomp"],
    ["none", "None"]
])

export const personalityNames = new Map([
    ["loyal", "Loyal"],
    ["hot_headed", "Hot Headed"],
    ["playful", "Playful"],
    ["brave", "Brave"],
    ["shy", "Shy"],
    ["rebellious", "Rebellious"],
    ["nurturing", "Nurturing"],
    ["peaceful", "Peaceful"]
])

mc.world.afterEvents.itemUse.subscribe((eventData) => {
    let player = eventData.source;
    let itemUse = eventData.itemStack;
    if (itemUse === undefined) return;

    if (itemUse.typeId == "cyd_dino:guidebook_g") menuMain(player);
})

mc.world.afterEvents.entitySpawn.subscribe(event => {
    if (!event.entity.typeId.startsWith('cyd_dino:')) return

    const entity = event.entity
    if (!entity) return
    if (!entity.getComponent('minecraft:type_family')?.hasTypeFamily('dino')) return

    for (const player of entity.dimension.getPlayers({
        location: entity.location,
        maxDistance: 6
    })) {
        if (player.getGameMode() === mc.GameMode.creative) return
    }

    const { x, y, z } = entity.location

    mc.system.run(() => entity.runCommand(`spreadplayers ${Math.floor(x)} ${Math.floor(z)} 3 4 @e[type=${entity.typeId},r=4] ${Math.floor(y)}`))
})

mc.world.beforeEvents.playerInteractWithEntity.subscribe(async eventData => {
    const { player, target, itemStack } = eventData;
    if (itemStack) {
        if (itemStack.typeId == 'cyd_dino:giant_bone') { showTameCommandMenu(player, target); eventData.cancel = true; return }
        if (itemStack.typeId == 'cyd_dino:totem') {
            if (!target.typeId.startsWith('cyd_dino:')) return
            eventData.cancel = true

            await mc.system.waitTicks(1)

            const lore = []
            const tier = target.getProperty('cyd_dino:tier')

            if (aquatics.map(x => x.replace('nest_', 'cyd_dino:')).includes(target.typeId)) lore.push('Water Breathing')
            if (flyers.includes(target.typeId)) lore.push('Levitation')
            if (tier == 5) lore.push('Strength & Resistance')
            if (tier == 4) lore.push('Strength')
            if (herbivores.includes(target.typeId)) lore.push('Regeneration')

            itemStack.setLore(lore)
            if (lore.length) player.getComponent('equippable').setEquipment(mc.EquipmentSlot.Mainhand, itemStack)

            return
        }
        if (!target.getComponent('is_tamed') && isCorrectDietaryItem(diets.get(target.typeId), itemStack)) {
            const health = target.getComponent('health')
            const prev = health.currentValue
            mc.system.run(() => {
                if (health.currentValue > prev) updateTrust(target, player, 50)

                switch (dinoData.get(target.typeId)?.tier ?? 'small') {
                    case 'small':
                        updateTrust(target, player, 40)
                        break
                    case 'medium':
                        updateTrust(target, player, 17)
                        break
                    case 'large':
                        updateTrust(target, player, 10)
                        break
                    case 'small_ash':
                        //random between 6 and 10
                        const amount = Math.random() * 4 + 6
                        updateTrust(target, player, amount)
                        break
                    case 'medium_ash':
                        //random between 3 and 5
                        const amount2 = Math.random() * 2 + 3
                        updateTrust(target, player, amount2)
                        break
                    case 'large_ash':
                        updateTrust(target, player, 1)
                        break
                }
            })
        }
    }
    if (target.getComponent('rideable')?.getRiders()?.includes(player)) { handleRiderInteract(target, player, itemStack) }
})

const armored = ["cyd_dino:anky", "cyd_dino:cameroceras", "cyd_dino:dunkleosteus", "cyd_dino:stegouros", "cyd_dino:spycomellus"]

mc.world.afterEvents.entityHurt.subscribe(event => {
    const { damageSource, hurtEntity, damage } = event;

    const entity = damageSource.damagingEntity;
    if (!entity || !hurtEntity) return;
    const hurtEntityId = hurtEntity.typeId

    if (hurtEntityId == 'minecraft:player') { playerHurtHandler(entity); return }
    if (bosses.includes(hurtEntityId)) return
    if (npcs.includes(hurtEntityId)) return
    if (hurtEntityId.startsWith('cyd_dino:hunter_')) return
    if (hurtEntity.typeId === 'cyd_dino:primeval_merchant') return
    if (hurtEntity.getComponent('type_family')?.hasTypeFamily('critter')) return

    try {
        if (hurtEntityId.startsWith('cyd_dino:')) {
            if (entity.typeId == 'minecraft:player') {
                if (hurtEntity.getComponent('is_tamed')) return
                if (!hurtEntity.getProperty('cyd_dino:tier')) return
                if (hurtEntity.getProperty('cyd_dino:tier') == 5) updateTrust(hurtEntity, entity, 1)

                updateTrust(hurtEntity, entity, -60)

                if (hurtEntity.getComponent('health').currentValue <= 0 && hurtEntity.getProperty('cyd_dino:is_alpha'))
                    hurtEntity.dimension.getEntities({
                        type: hurtEntityId,
                        tags: entity.getTags().map(x => parseInt(x)).filter(x => !Number.isNaN(x))
                    }).forEach(member => updateTrust(member, entity, 50))

                if (armored.includes(hurtEntityId) && entity.getComponent('equippable')
                    .getEquipment(mc.EquipmentSlot.Mainhand)?.typeId == 'cyd_dino:dinosaur_tooth_dagger')
                    hurtEntity.applyDamage(damage * 1.5)

                return
            }

            try {
                speedBoostPersonalityBonus(entity, entity.getProperty('cyd_dino:personality'))
            } catch ($) { }
        }
    } catch ($) { }
})

mc.world.afterEvents.playerSpawn.subscribe((eventData) => {
    const player = eventData.player
    if (player.hasTag('cyd_dino_book')) return

    player.dimension.spawnItem(new mc.ItemStack("cyd_dino:guidebook_g", 1), player.location)

    // fog settings on by default
    mc.world.setDynamicProperty('cyd_dino:enabled_fog', true)
    mc.world.setDynamicProperty('cyd_dino:enabled_ash', true)
    player.addTag('cyd_dino_book')
});

setupStaticMenus();

mc.world.afterEvents.projectileHitEntity.subscribe(eventData => {
    const { projectile } = eventData;
    if (projectile.typeId == 'cyd_dino:tranq_dart') dartHit(eventData.getEntityHit().entity)
})

mc.world.beforeEvents.worldInitialize.subscribe(async (eventData) => {
    const { blockComponentRegistry, itemComponentRegistry } = eventData;

    const tryGetBlock = (dimension, coords) => {
        try {
            return dimension.getBlock(coords);
        } catch {
            return null;
        }
    };

    const checkBlockType = (block, targetType) => {
        if (!block) return 0;
        if (block.matches(targetType)) return 2;
        return (block.isAir || block.isLiquid) ? 0 : 1;
    };

    const removeBlockWithDrop = (block) => {
        const { x, y, z } = block.location;
        block.dimension.runCommandAsync(`setblock ${x} ${y} ${z} air destroy`);
    };

    const recentlyRefreshed = new Set();
    const refreshCooldown = 2;

    const canRefresh = (block) => {
        const key = `${block.location.x},${block.location.y},${block.location.z}`;
        if (recentlyRefreshed.has(key)) return false;

        recentlyRefreshed.add(key);
        mc.system.runTimeout(() => {
            recentlyRefreshed.delete(key);
        }, refreshCooldown);

        return true;
    };
    const gatherSpikeColumn = (baseBlock, growthDir) => {
        const blocks = [baseBlock];
        const { dimension, location, typeId } = baseBlock;
        const visitedLocations = new Set([`${location.x},${location.y},${location.z}`]);
        const MAX_COLUMN_SIZE = 32;

        if (!growthDir || !['up', 'down', 'north', 'south', 'east', 'west'].includes(growthDir)) {
            return blocks;
        }

        let xStep = 0, yStep = 0, zStep = 0;
        switch (growthDir) {
            case "up": yStep = 1; break;
            case "down": yStep = -1; break;
            case "north": zStep = 1; break;
            case "south": zStep = -1; break;
            case "east": xStep = 1; break;
            case "west": xStep = -1; break;
        }

        for (const dir of [-1, 1]) {
            let currentX = location.x + (dir * xStep);
            let currentY = location.y + (dir * yStep);
            let currentZ = location.z + (dir * zStep);
            let iterations = 0;

            while (iterations < MAX_COLUMN_SIZE) {
                iterations++;

                const locationKey = `${currentX},${currentY},${currentZ}`;
                if (visitedLocations.has(locationKey)) break;

                const checkBlock = tryGetBlock(dimension, {
                    x: currentX,
                    y: currentY,
                    z: currentZ
                });

                if (!checkBlock ||
                    checkBlock.typeId !== typeId ||
                    checkBlock.permutation.getState("cyd_dino:direction") !== growthDir) break;

                visitedLocations.add(locationKey);
                dir === -1 ? blocks.push(checkBlock) : blocks.unshift(checkBlock);

                currentX += dir * xStep;
                currentY += dir * yStep;
                currentZ += dir * zStep;
            }
        }

        return blocks;
    };

    const refreshSpikeColumn = (block) => {
        try {
            if (!canRefresh(block)) return;

            let growthDirection = block.permutation.getState("cyd_dino:direction");

            if (!growthDirection || !['up', 'down', 'north', 'south', 'east', 'west'].includes(growthDirection)) {
                const adjacentDirections = [
                    { block: block.above(), dir: 'down' },
                    { block: block.below(), dir: 'up' },
                    { block: block.north(), dir: 'south' },
                    { block: block.south(), dir: 'north' },
                    { block: block.east(), dir: 'west' },
                    { block: block.west(), dir: 'east' }
                ];

                for (const { block: adjBlock, dir } of adjacentDirections) {
                    if (adjBlock?.typeId === block.typeId) {
                        const adjDir = adjBlock.permutation.getState("cyd_dino:direction");
                        if (adjDir && ['up', 'down', 'north', 'south', 'east', 'west'].includes(adjDir)) {
                            growthDirection = adjDir;
                            break;
                        }
                    }
                }

                if (!growthDirection || !['up', 'down', 'north', 'south', 'east', 'west'].includes(growthDirection)) {
                    growthDirection = 'up';
                }

                if (block && block.typeId === 'cyd_dino:bone_spike' && !block.isAir) {
                    block.setPermutation(
                        block.permutation.withState("cyd_dino:direction", growthDirection)
                    );
                }
            }

            const columnBlocks = gatherSpikeColumn(block, growthDirection);
            const totalBlocks = columnBlocks.length;

            // Safety check
            if (totalBlocks === 0 || totalBlocks > 32) {
                console.warn("Invalid column size:", totalBlocks);
                return;
            }

            columnBlocks.forEach((currentBlock, index) => {
                const thicknessValue = (() => {
                    if (totalBlocks <= 1) return 0;
                    if (index === 0) return 0;
                    if (index === 1) return 1;
                    if (index === totalBlocks - 1) return 3;
                    return 2;
                })();

                if (currentBlock && currentBlock.typeId === block.typeId && !currentBlock.isAir) {
                    try {
                        currentBlock.setPermutation(
                            currentBlock.permutation
                                .withState("cyd_dino:thickness", thicknessValue)
                                .withState("cyd_dino:direction", growthDirection)
                        );
                    } catch (blockError) {
                    }
                }
            });
        } catch (error) {
            console.warn("Error in refreshSpikeColumn:", error);
        }
    };

    const cascadeBreakSpikes = (currentBlock, growthDir) => {
        const adjacentBlock = growthDir === "up" ? currentBlock.above() : currentBlock.below();
        removeBlockWithDrop(currentBlock);

        if (adjacentBlock?.typeId === currentBlock.typeId &&
            adjacentBlock.permutation.getState("cyd_dino:direction") === growthDir) {
            mc.system.runTimeout(() => cascadeBreakSpikes(adjacentBlock, growthDir), 1);
        }
    };
    const getAdjacentSpikeBlocks = (block, direction) => {
        switch (direction) {
            case "up": return [block.below(), block.above()];
            case "down": return [block.above(), block.below()];
            case "north": return [block.south(), block.north()];
            case "south": return [block.north(), block.south()];
            case "east": return [block.west(), block.east()];
            case "west": return [block.east(), block.west()];
        }
        return [null, null];
    };

    const getOppositeDirection = (direction) => {
        const opposites = {
            "up": "down", "down": "up",
            "north": "south", "south": "north",
            "east": "west", "west": "east"
        };
        return opposites[direction];
    };

    itemComponentRegistry.registerCustomComponent('cyd_dino:dart_gun', {
        onUse(e) {
            spawnProjectile(e.source)
        }
    })
    itemComponentRegistry.registerCustomComponent('cyd_dino:aoe', {
        onHitEntity(e) {
            const { attackingEntity, hitEntity } = e;
            hitEntity.dimension.getEntities({ location: hitEntity.location, maxDistance: 3 }).forEach(entity => {
                if (attackingEntity == entity) return;
                if (hitEntity == entity) return;
                try {
                    entity.applyDamage(5, { cause: mc.EntityDamageCause.entityAttack, damagingEntity: attackingEntity })
                } catch (error) { }
            })
        }
    })
    itemComponentRegistry.registerCustomComponent('cyd_dino:bonus_knockback', {
        onHitEntity(e) {
            const { hadEffect, attackingEntity, hitEntity } = e;
            const l = attackingEntity.location
            const l2 = hitEntity.location
            if (hadEffect) hitEntity.applyKnockback(-l.x + l2.x, -l.z + l2.z, 1, 0.2)
        }
    })
    itemComponentRegistry.registerCustomComponent('cyd_dino:totem', {
        onUse(e) {
            const { source, itemStack } = e;
            const lore = itemStack.getLore()
            if (!lore) return

            if (lore.length === 0) return

            lore.forEach(entry => {
                switch (entry) {
                    case 'Water Breathing': source.addEffect('water_breathing', 600)
                        break;
                    case 'Levitation': source.addEffect('levitation', 100)
                        break;
                    case 'Regeneration': source.addEffect('regeneration', 600)
                        break;
                    case 'Strength & Resistance': source.addEffect('resistance', 600)
                    case 'Strength': source.addEffect('strength', 600)
                        break;
                }
            })
            source.getComponent('equippable').setEquipment(mc.EquipmentSlot.Mainhand)
        }
    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:fumerolle_block', fumerolleBlockBehavior)

    blockComponentRegistry.registerCustomComponent('cyd_dino:volcano_block', {
        onRandomTick(e) {
            const { block, dimension } = e
            const location = block.center()

            dimension.spawnParticle('cyd_dino:lava_burst_1', location)
            dimension.spawnParticle('cyd_dino:lava_burst_2', location)
            dimension.spawnParticle('cyd_dino:lava_burst_3', location)
        },
        onTick(e) {
            const { block, dimension } = e

            dimension.getEntities({
                location: block.above().center(),
                maxDistance: 1
            }).forEach(entity => entity.applyDamage(3, { cause: mc.EntityDamageCause.fireTick }))
        }
    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:biome_nest', {
        onTick(e) {
            const { block } = e;
            block.setType('cyd_dino:' + dinos[Math.floor(Math.random() * dinos.length)])
        }
    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:flag', {
        /**
         * 
         * @param {mc.BlockComponentPlayerPlaceBeforeEvent} e 
         */
        beforeOnPlayerPlace(e) {
            e.permutationToPlace = e.permutationToPlace.withState('cyd_dino:skin', Math.floor(Math.random() * 4))
        }

    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:aquatic_nest', {
        onTick(e) {
            const { block } = e;
            block.setType('cyd_dino:' + aquatics[Math.floor(Math.random() * aquatics.length)])
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:green_hunter', {
        onTick(e) {
            const { block, dimension } = e;
            const spawnLocation = block.bottomCenter()
            const hunter = dimension.spawnEntity('cyd_dino:gaiakin', spawnLocation)
            block.setType('air')
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:red_hunter', {
        onTick(e) {
            const { block, dimension } = e;
            const spawnLocation = block.bottomCenter()
            const hunter = dimension.spawnEntity('cyd_dino:draxer', spawnLocation)
            block.setType('air')
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_draxar_boss', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const spawnLocation = { x: block.location.x, y: block.location.y + 2, z: block.location.z }
            const boss = dimension.spawnEntity('cyd_dino:skull_trex_boss', spawnLocation)
            block.setType('deepslate')
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_draxar_minion_0', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const spawnLocation = { x: block.location.x, y: block.location.y + 2, z: block.location.z }
            const minion = dimension.spawnEntity('cyd_dino:skull_velo', spawnLocation)
            const minion2 = dimension.spawnEntity('cyd_dino:skull_velo', spawnLocation)
            const minion3 = dimension.spawnEntity('cyd_dino:skull_velo', spawnLocation)
            minion.triggerEvent('cyd_dino:as_minion')
            minion2.triggerEvent('cyd_dino:as_minion')
            minion3.triggerEvent('cyd_dino:as_minion')
            block.setType('deepslate')
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_draxar_minion_1', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const boss = block.dimension.getEntities({ type: 'cyd_dino:dark_draxar_boss', closest: 1, location: block.location, maxDistance: 50, tags: ['cyd_dino:trex_defeated'] })[0]
            if (!boss) return;

            const spawnLocation = { x: block.location.x, y: block.location.y + 2, z: block.location.z }
            const minion = dimension.spawnEntity('cyd_dino:skull_stegospheryx', spawnLocation)
            minion.triggerEvent('cyd_dino:as_minion')
            block.setType('deepslate')
        }
    })


    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_dark_draxar', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const player = block.dimension.getEntities({ type: 'minecraft:player', closest: 1, location: block.location, maxDistance: 50 })[0];
            const spawned = block.permutation.getState('cyd_dino:spawned');
            const tag = `${block.location.x},${block.location.y},${block.location.z}`

            if (!spawned && player) {
                const spawnLocation = { x: block.location.x, y: block.location.y + 1, z: block.location.z }
                const entity = dimension.spawnEntity('cyd_dino:dark_draxar_npc', spawnLocation)
                entity.addTag(tag)
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', true));
            }

            const entities = block.dimension.getEntities({ type: 'cyd_dino:dark_draxar_npc', location: block.location, maxDistance: 50, tags: [tag] });
            if (!player && entities.length === 0) {
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', false));
            }
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_dark_draxar_merchant', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const player = block.dimension.getEntities({ type: 'minecraft:player', closest: 1, location: block.location, maxDistance: 50 })[0];
            const spawned = block.permutation.getState('cyd_dino:spawned');
            const tag = `${block.location.x},${block.location.y},${block.location.z}`

            if (!spawned && player) {
                const spawnLocation = { x: block.location.x, y: block.location.y + 1, z: block.location.z }
                const entity = dimension.spawnEntity('cyd_dino:dark_draxar_merchant', spawnLocation)
                entity.addTag(tag)
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', true));
            }

            const entities = block.dimension.getEntities({ type: 'cyd_dino:dark_draxar_merchant', location: block.location, maxDistance: 50, tags: [tag] });
            if (!player && entities.length === 0) {
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', false));
            }
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_draxar', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const player = block.dimension.getEntities({ type: 'minecraft:player', closest: 1, location: block.location, maxDistance: 50 })[0];
            const spawned = block.permutation.getState('cyd_dino:spawned');
            const tag = `${block.location.x},${block.location.y},${block.location.z}`

            if (!spawned && player) {
                const spawnLocation = { x: block.location.x, y: block.location.y + 1, z: block.location.z }
                const entity = dimension.spawnEntity('cyd_dino:draxer', spawnLocation)
                entity.addTag(tag)
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', true));
            }

            const entities = block.dimension.getEntities({ type: 'cyd_dino:draxer', location: block.location, maxDistance: 50, tags: [tag] });
            if (!player && entities.length === 0) {
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', false));
            }
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_skull_stegospheryx', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const player = block.dimension.getEntities({ type: 'minecraft:player', closest: 1, location: block.location, maxDistance: 50 })[0];
            const spawned = block.permutation.getState('cyd_dino:spawned');
            const tag = `${block.location.x},${block.location.y},${block.location.z}`

            if (!spawned && player) {
                const spawnLocation = { x: block.location.x, y: block.location.y + 1, z: block.location.z }
                const entity = dimension.spawnEntity('cyd_dino:skull_stegospheryx', spawnLocation)
                entity.addTag(tag)
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', true));
            }

            const entities = block.dimension.getEntities({ type: 'cyd_dino:skull_stegospheryx', location: block.location, maxDistance: 20, tags: [tag] });

            if (!player && entities.length === 0) {
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', false));
            }
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:spawn_skull_velo', {
        /**
         * @param {mc.BlockComponentTickEvent} e
         */
        onTick(e) {
            const { block, dimension } = e;
            const player = block.dimension.getEntities({ type: 'minecraft:player', closest: 1, location: block.location, maxDistance: 50 })[0];
            const spawned = block.permutation.getState('cyd_dino:spawned');
            const tag = `${block.location.x},${block.location.y},${block.location.z}`

            if (!spawned && player) {
                const spawnLocation = { x: block.location.x, y: block.location.y + 1, z: block.location.z }
                const entity = dimension.spawnEntity('cyd_dino:skull_velo', spawnLocation)
                entity.addTag(tag)
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', true));
            }

            const entities = block.dimension.getEntities({ type: 'cyd_dino:skull_velo', location: block.location, maxDistance: 20, tags: [tag] });

            if (!player && entities.length === 0) {
                block.setPermutation(block.permutation.withState('cyd_dino:spawned', false));
            }
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:nest_spawner', {
        onTick(e) {
            const { block, dimension } = e;
            const { typeId } = block;
            const nestLocation = block.bottomCenter()
            const spawnCenter = block.above().center()

            const type = typeId.slice(14)
            const chance = Math.random()

            if (chance < 75) {
                const parentLocation1 = findSafeSpawnLocation(dimension, spawnCenter, 10)
                const parent = dimension.spawnEntity('cyd_dino:' + type + '<cyd_dino:as_nesting_parent>', parentLocation1)
                const parentLocation2 = findSafeSpawnLocation(dimension, spawnCenter, 14)
                const parent2 = dimension.spawnEntity('cyd_dino:' + type + '<cyd_dino:as_nesting_parent>', parentLocation2)

                mc.system.runTimeout(() => {
                    parent.triggerEvent('cyd_dino:is_asleep')
                    parent2.triggerEvent('cyd_dino:is_asleep')
                });

                const nest = dimension.spawnEntity('cyd_dino:nest', nestLocation)

                const rideableComponent = nest.getComponent('rideable')
                for (let i = 0; i < Math.floor(Math.random() * 3) + 1; i++) {
                    const egg = dimension.spawnEntity('cyd_dino:baby_' + type + '<cyd_dino:as_egg>', nestLocation)
                    egg.setDynamicProperty('parent', parent.id)
                    egg.setDynamicProperty('parent2', parent2.id)

                    rideableComponent.addRider(egg)
                }
            } else {
                const babyCount = Math.floor(Math.random() * 3) + 1

                for (let i = 0; i < babyCount; i++) {
                    const babyLocation = findSafeSpawnLocation(dimension, spawnCenter, 5)
                    dimension.spawnEntity(`cyd_dino:baby_${type}`, babyLocation)
                }
            }

            block.setType('air')
        }
    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:leaves', {
        beforeOnPlayerPlace: playerPlacedLeaf,
        onRandomTick(e) {
            handleDecay(e, [{ x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }])
        }
    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:vertical_leaves', {
        beforeOnPlayerPlace: playerPlacedLeaf,
        onRandomTick(e) {
            handleDecay(e, [{ x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }])
        }
    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:cardinal_leaves', {
        beforeOnPlayerPlace: playerPlacedLeaf,
        onRandomTick(e) {
            handleDecay(e, [{ x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 0, y: 0, z: -1 }, { x: -1, y: 0, z: 1 }])
        }
    })
    blockComponentRegistry.registerCustomComponent('cyd_dino:cardinally_down_leaves', {
        beforeOnPlayerPlace: playerPlacedLeaf,
        onRandomTick(e) {
            handleDecay(e, [{ x: 1, y: -1, z: 0 }, { x: -1, y: -1, z: 0 }, { x: 0, y: -1, z: -1 }, { x: 0, y: -1, z: 1 }])
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:drop_ancient_fruit', {
        onPlayerDestroy(event) {
            const { block, dimension } = event
            const blockTypeId = block.typeId

            if (ancientFruitDropChances.has(blockTypeId)) {
                const dropChance = ancientFruitDropChances.get(blockTypeId)
                if (Math.random() < dropChance) dimension.spawnItem(
                    new mc.ItemStack(ANCIENT_FRUIT_ITEM_ID, 1), block.center()
                )
            }
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:diagonal_leaves', {
        beforeOnPlayerPlace: playerPlacedLeaf,
        onRandomTick(e) {
            handleDecay(e, [{ x: 1, y: 0, z: 1 }, { x: -1, y: 0, z: -1 }, { x: 1, y: 0, z: -1 }, { x: -1, y: 0, z: 1 }])
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:sapling', {
        onPlayerInteract(e) {
            const { player, block } = e
            const itemStack = player.getComponent('equippable').getEquipment(mc.EquipmentSlot.Mainhand)
            if (itemStack?.typeId != 'minecraft:bone_meal') return;
            const age = block.permutation.getState('cyd_dino:age_bit')
            const treeType = block.typeId.split(':').pop().replace('_sapling', '')
            block.dimension.spawnParticle('minecraft:crop_growth_emitter', block.center());
            if (player.getGameMode() != 'creative') {
                try {
                    itemStack.amount -= 1
                    player?.getComponent('equippable').setEquipment('Mainhand', itemStack)
                } catch (error) {
                    player?.getComponent('equippable').setEquipment('Mainhand')
                }
            }
            player.playSound('item.bone_meal.use', { location: block.location })
            if (Math.random() > 0.45) return;
            if (age) block.dimension.runCommand(`execute positioned ${block.x} ${block.y} ${block.z} run ` + trees[treeType][Math.floor(Math.random() * trees[treeType].length)])
            else block.setPermutation(block.permutation.withState('cyd_dino:age_bit', !age))
        },
        onRandomTick(e) {
            const { block } = e
            if (Math.floor(Math.random() * 7) > 0) return;
            const treeType = block.typeId.split(':').pop().replace('_sapling', '')
            const age = block.permutation.getState('cyd_dino:age_bit')
            if (age) block.dimension.runCommand(`execute positioned ${block.x} ${block.y} ${block.z} run ` + trees[treeType][Math.floor(Math.random() * trees[treeType].length)])
            else block.setPermutation(block.permutation.withState('cyd_dino:age_bit', !age))
        }
    })

    blockComponentRegistry.registerCustomComponent('cyd_dino:grass_to_dirt', {
        onTick: e => {
            const block = e.block
            if (block.below(1).typeId == 'minecraft:grass') {
                block.below(1).setPermutation(mc.BlockPermutation.resolve("minecraft:dirt"))
            }
        },
    });
    blockComponentRegistry.registerCustomComponent('cyd_dino:double_slab', {
        onPlayerInteract: e => {
            const block = e.block
            const player = e.player
            const item = e.player.getComponent('equippable').getEquipment('Mainhand')

            // Detect which type of slab this is
            if (block.typeId.includes('thick_bones')) {
                double_slab(block, item, player, "thick_bones")
            } else if (block.typeId.includes('thin_bones')) {
                double_slab(block, item, player, "thin_bones")
            }
        },
    });


    blockComponentRegistry.registerCustomComponent('cyd_dino:pahoehoe_block', pahoehoeBlockBehavior);

    blockComponentRegistry.registerCustomComponent('cyd_dino:bonespike', {
        onPlace({ block, player }) {
            const nearbyPlayers = block.dimension.getEntities({
                location: block.location,
                maxDistance: 8,
                type: "minecraft:player",
            });
            if (nearbyPlayers.length === 0) return;

            try {
                let growthDirection = null;

                const blockAbove = block.above();
                const blockBelow = block.below();
                const blockNorth = block.north();
                const blockSouth = block.south();
                const blockEast = block.east();
                const blockWest = block.west();

                if (blockAbove?.typeId === block.typeId) {
                    growthDirection = blockAbove.permutation.getState("cyd_dino:direction");
                } else if (blockBelow?.typeId === block.typeId) {
                    growthDirection = blockBelow.permutation.getState("cyd_dino:direction");
                } else if (blockNorth?.typeId === block.typeId) {
                    growthDirection = blockNorth.permutation.getState("cyd_dino:direction");
                } else if (blockSouth?.typeId === block.typeId) {
                    growthDirection = blockSouth.permutation.getState("cyd_dino:direction");
                } else if (blockEast?.typeId === block.typeId) {
                    growthDirection = blockEast.permutation.getState("cyd_dino:direction");
                } else if (blockWest?.typeId === block.typeId) {
                    growthDirection = blockWest.permutation.getState("cyd_dino:direction");
                }

                if (!growthDirection) {
                    growthDirection = "up"; // default

                    if (blockBelow && !blockBelow.isAir && !blockBelow.isLiquid && blockBelow.typeId !== block.typeId) {
                        growthDirection = "up";
                    } else if (blockAbove && !blockAbove.isAir && !blockAbove.isLiquid && blockAbove.typeId !== block.typeId) {
                        growthDirection = "down";
                    } else if (blockNorth && !blockNorth.isAir && !blockNorth.isLiquid && blockNorth.typeId !== block.typeId) {
                        growthDirection = "north";
                    } else if (blockSouth && !blockSouth.isAir && !blockSouth.isLiquid && blockSouth.typeId !== block.typeId) {
                        growthDirection = "south";
                    } else if (blockWest && !blockWest.isAir && !blockWest.isLiquid && blockWest.typeId !== block.typeId) {
                        growthDirection = "east";
                    } else if (blockEast && !blockEast.isAir && !blockEast.isLiquid && blockEast.typeId !== block.typeId) {
                        growthDirection = "west";
                    }
                }

                if (block && block.typeId === 'cyd_dino:bone_spike' && !block.isAir) {
                    block.setPermutation(
                        block.permutation.withState("cyd_dino:direction", growthDirection)
                    );
                }

                mc.system.runTimeout(() => {
                    const refreshedBlock = tryGetBlock(block.dimension, block.location);
                    if (refreshedBlock && refreshedBlock.typeId === 'cyd_dino:bone_spike') {
                        refreshSpikeColumn(refreshedBlock);

                        const [backBlock, forwardBlock] = getAdjacentSpikeBlocks(refreshedBlock, growthDirection);

                        if (backBlock?.typeId === 'cyd_dino:bone_spike') {
                            const backDir = backBlock.permutation.getState("cyd_dino:direction");
                            if (backDir === growthDirection) {
                                refreshSpikeColumn(backBlock);
                            }
                        }

                        if (forwardBlock?.typeId === 'cyd_dino:bone_spike') {
                            const forwardDir = forwardBlock.permutation.getState("cyd_dino:direction");
                            if (forwardDir === growthDirection) {
                                refreshSpikeColumn(forwardBlock);
                            }
                        }
                    }
                }, 1);
            } catch (error) {
                console.warn("Error in bonespike onPlace:", error);
            }
        },

        onPlayerDestroy({ block, destroyedBlockPermutation }) {
            try {
                const destroyedType = destroyedBlockPermutation.type.id;
                let growthDirection = destroyedBlockPermutation.getState("cyd_dino:direction");

                if (!growthDirection || !['up', 'down', 'north', 'south', 'east', 'west'].includes(growthDirection)) {
                    growthDirection = 'up';
                }

                const [backBlock, forwardBlock] = getAdjacentSpikeBlocks(block, growthDirection);
                const stateBack = checkBlockType(backBlock, destroyedType);
                const stateForward = checkBlockType(forwardBlock, destroyedType);

                if (stateBack === 2) {
                    const backDir = backBlock.permutation.getState("cyd_dino:direction");
                    if (backDir && ['up', 'down', 'north', 'south', 'east', 'west'].includes(backDir)) {
                        growthDirection === backDir
                            ? refreshSpikeColumn(backBlock)
                            : cascadeBreakSpikes(backBlock, getOppositeDirection(growthDirection));
                    }
                }

                if (stateForward === 2) {
                    const forwardDir = forwardBlock.permutation.getState("cyd_dino:direction");
                    if (forwardDir && ['up', 'down', 'north', 'south', 'east', 'west'].includes(forwardDir)) {
                        growthDirection === getOppositeDirection(forwardDir)
                            ? refreshSpikeColumn(forwardBlock)
                            : cascadeBreakSpikes(forwardBlock, growthDirection);
                    }
                }
            } catch (error) {
                console.warn("Error in bonespike onPlayerDestroy:", error);
            }
        },
    });
})

function decrementstack(item, user) {
    if (!(item?.typeId == undefined)) {
        if (item.amount > 1) {
            item.amount--;
            user.getComponent('equippable').setEquipment('Mainhand', item)
        }
        else if (item.amount === 1) {
            user.getComponent('equippable').setEquipment('Mainhand', undefined)
        }
    }
}

function double_slab(block, item, player, type) {
    let req = "cyd_dino:" + type + "_slab"
    if (block.typeId == req && item?.typeId == req) {
        decrementstack(item, player)
        block.setPermutation(mc.BlockPermutation.resolve("cyd_dino:" + type + "_double_slab"))
    }
}

function update_stairs_around(block) {
    update_stair(block)
    if (block.north(1).hasTag("cyd_dino:stairs")) {
        update_stair(block.north(1))
    }
    if (block.south(1).hasTag("cyd_dino:stairs")) {
        update_stair(block.south(1))
    }
    if (block.east(1).hasTag("cyd_dino:stairs")) {
        update_stair(block.east(1))
    }
    if (block.west(1).hasTag("cyd_dino:stairs")) {
        update_stair(block.west(1))
    }
}
function update_stair(block) {
    //Inner Corners
    if (block.north(1).hasTag("cyd_dino:stairs_east") && !(block.north(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_north")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 6))

        }
    }
    if (block.north(1).hasTag("cyd_dino:stairs_west") && !(block.north(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_north")) {

            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 7))
        }
    }
    if (block.east(1).hasTag("cyd_dino:stairs_south") && !(block.north(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_east")) {

            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 4))
        }
    }

    if (block.east(1).hasTag("cyd_dino:stairs_south") && !(block.east(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_east")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 4))

        }
    }
    if (block.east(1).hasTag("cyd_dino:stairs_north") && !(block.east(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_east")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 6))

        }
    }
    if (block.west(1).hasTag("cyd_dino:stairs_north") && !(block.west(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_west")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 7))

        }
    }
    if (block.west(1).hasTag("cyd_dino:stairs_south") && !(block.west(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_west")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 5))

        }
    }
    if (block.south(1).hasTag("cyd_dino:stairs_west") && !(block.south(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_south")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 5))

        }
    }
    if (block.south(1).hasTag("cyd_dino:stairs_east") && !(block.south(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_south")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 4))

        }
    }
    //Extra
    if (block.south(1).hasTag("cyd_dino:stairs_east") && !(block.south(1).hasTag("cyd_dino:stairs_inner"))) {
        if (block.hasTag("cyd_dino:stairs_south")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 4))

        }
    }
    //Inner Corners

    if (block.north(1).hasTag("cyd_dino:stairs_east")) {
        if (block.hasTag("cyd_dino:stairs_south")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 8))

        }
    }
    if (block.south(1).hasTag("cyd_dino:stairs_east")) {
        if (block.hasTag("cyd_dino:stairs_north")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 11))

        }
    }
    if (block.west(1).hasTag("cyd_dino:stairs_north")) {
        if (block.hasTag("cyd_dino:stairs_east")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 11))

        }
    }
    if (block.south(1).hasTag("cyd_dino:stairs_west")) {
        if (block.hasTag("cyd_dino:stairs_north")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 10))

        }
    }
    if (block.east(1).hasTag("cyd_dino:stairs_south")) {
        if (block.hasTag("cyd_dino:stairs_west")) {
            block.setPermutation(block.permutation.withState('cyd_dino:stairs_direction', 9))

        }
    }
}


function playerPlacedLeaf(e) {
    e.permutationToPlace = e.permutationToPlace.withState('cyd_dino:can_decay', false);
}

function findSafeSpawnLocation(dimension, center, radius) {
    for (let i = 0; i < 10; i++) {
        const randomOffset = {
            x: center.x + (Math.random() * radius * 2 - radius),
            y: center.y,
            z: center.z + (Math.random() * radius * 2 - radius)
        }

        const block = dimension.getBlock(randomOffset)
        if (block && block.isAir) return randomOffset
    }

    return center
}

function handleDecay(e, offsets) {
    // const { block } = e;

    // const permutation = block.permutation;
    // if (!permutation.getState('cyd_dino:can_decay')) return;

    // let maxTier = 0;
    // for (const offset of offsets) {
    //     const b = block.offset(offset)
    //     if (!b) continue;
    //     if (b.hasTag('log')) { maxTier = 4; break }
    //     const tier = (b.permutation.getState('cyd_dino:decay_tier') ?? 1) - 1
    //     if (tier > maxTier) maxTier = tier;
    // }

    // try {
    //     if (maxTier === 0) block.setType('air');
    //     else if (permutation.getState('cyd_dino:decay_tier') !== maxTier) block.setPermutation(permutation.withState('cyd_dino:decay_tier', maxTier));
    // } catch (error) { }
}

/**
 * @param {mc.Entity} hitEntity 
 */
function dartHit(hitEntity) {
    if (!hitEntity.typeId.startsWith('cyd_dino:')) return;
    if (!hitEntity.getComponent('type_family').hasTypeFamily('wild')) return;
    hitEntity.addEffect('slowness', 200, { amplifier: 255, showParticles: false })
    hitEntity.triggerEvent('cyd_dino:is_asleep')
}

const GLIDE_FORCE = 0.4
const GLIDE_INTERVAL = 5
const BOOST_VERTICAL_FORCE = 0.8
const BOOST_HORIZONTAL_FORCE = 2.0

const fltyingEntities = new Map()

function startGliding(player = mc.Player.prototype, vehicle = mc.Entity.prototype) {
    if (fltyingEntities.has(vehicle.id)) mc.system.clearRun(fltyingEntities.get(vehicle.id))

    vehicle.setDynamicProperty('cyd_dino:is_flying', true)
    player.playSound('entity.phantom.flap')

    const glideInterval = mc.system.runInterval(() => {
        const currVehicle = player.getComponent('riding')?.entityRidingOn
        if (!currVehicle || currVehicle.id !== vehicle.id) {
            stopGliding(vehicle)
            return
        }

        if (vehicle.isOnGround) {
            stopGliding(vehicle)
            return
        }

        const viewDirection = player.getViewDirection();
        const pitch = Math.asin(viewDirection.y) * (180 / Math.PI)

        const yaw = (player.getRotation().y * Math.PI) / 180
        const forward = {
            x: -Math.sin(yaw), y: 0,
            z: Math.cos(yaw)
        }

        if (pitch > -20 && pitch < 0) {
            //            vehicle.applyImpulse({
            //                x: forward.x * 0.1, y: 0.03, z: forward.z * 0.1
            //            })

            vehicle.addEffect('levitation', GLIDE_INTERVAL + 5, {
                showParticles: false,
                amplifier: 0
            });
            vehicle.addEffect('speed', GLIDE_INTERVAL + 5, {
                showParticles: false,
                amplifier: 28
            })

        }

        if (pitch > 0) {
            vehicle.addEffect('levitation', GLIDE_INTERVAL + 5, {
                showParticles: false,
                amplifier: 20
            });
            vehicle.addEffect('speed', GLIDE_INTERVAL + 5, {
                showParticles: false,
                amplifier: 18
            })

        }

        if (pitch <= -20) {
            //           vehicle.applyImpulse({
            //               x: forward.x * 0.2, y: -0.01, z: forward.z * 0.2
            //           })

            vehicle.addEffect('slow_falling', GLIDE_INTERVAL + 5, {
                showParticles: false,
                amplifier: 1
            })
        }

        // vehicle.applyImpulse(viewDirection)
        // vehicle.addEffect('levitation', GLIDE_INTERVAL + 5, {
        //     showParticles: false,
        //     amplifier: 1
        // })

        fltyingEntities.set(vehicle.id, glideInterval)
    }, GLIDE_INTERVAL)
}

function stopGliding(vehicle = mc.Entity.prototype) {
    if (!vehicle) return

    if (fltyingEntities.has(vehicle.id)) {
        mc.system.clearRun(fltyingEntities.get(vehicle.id))
        fltyingEntities.delete(vehicle.id)
    }

    vehicle.setDynamicProperty('cyd_dino:is_flying', false)
    vehicle.removeEffect('levitation')
    vehicle.removeEffect('slow_falling')
    vehicle.removeEffect('speed')
}

mc.world.afterEvents.playerButtonInput.subscribe(event => {
    const { newButtonState, player } = event

    const vehicle = player.getComponent('riding')?.entityRidingOn
    if (!vehicle) return

    if (newButtonState == mc.ButtonState.Pressed) {
        const isGliding = vehicle.getDynamicProperty('cyd_dino:is_flying') ?? false

        if (vehicle.hasTag('swimming')) {
            // if (!isGliding) startGliding(player, vehicle)
            // else {
            //     const viewDirection = player.getViewDirection()
            //     vehicle.applyKnockback(viewDirection.x, viewDirection.y, BOOST_HORIZONTAL_FORCE, BOOST_VERTICAL_FORCE)
            // }

            vehicle.applyImpulse(player.getViewDirection())
            vehicle.addEffect('levitation', 20 * 3, { showParticles: false, amplifier: 1 })
            return
        }

        if (!vehicle.getComponent('type_family').hasTypeFamily('flying')) return
        if (!isGliding) startGliding(player, vehicle)
        else {
            const viewDirection = player.getViewDirection()
            vehicle.applyKnockback(viewDirection.x, viewDirection.y, BOOST_HORIZONTAL_FORCE, BOOST_VERTICAL_FORCE)
        }
    }
}, { buttons: [mc.InputButton.Jump] })

/**
 * @typedef Diet
 * @property {string[] | undefined} items 
 * @property {string[] | undefined} tags 
 * @property {string[] | undefined} regex 
 */

/**
 * @type {Map<string,Diet>}
 */
const diets = new Map([
    ["cyd_dino:allosaurus", { items: ['cyd_dino:large_meat', 'cyd_dino:small_meat'] }],
    ["cyd_dino:anky", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:archeo", { tags: ["minecraft:is_meat"], items: ['cyd_dino:bug'] }],
    ["cyd_dino:bary", { tags: ["minecraft:is_fish", "minecraft:is_meat"] }],
    ["cyd_dino:brachio", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves'] }],
    ["cyd_dino:cameroceras", { tags: ["minecraft:is_fish"] }],
    ["cyd_dino:carno", { tags: ["minecraft:is_meat"] }],
    ["cyd_dino:ceratosaurus", { items: ['cyd_dino:large_meat', 'cyd_dino:small_meat'] }],
    ["cyd_dino:conchoraptor", { items: ['cyd_dino:small_meat'] }],
    ["cyd_dino:dime", { tags: ["minecraft:is_fish", "minecraft:is_meat"] }],
    ["cyd_dino:dunkleosteus", { tags: ["minecraft:is_fish"] }],
    ["cyd_dino:gigantspinosaurus", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:icty", { tags: ["minecraft:is_fish"] }],
    ["cyd_dino:iguan", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:majungosaurus", { items: ['cyd_dino:large_meat', 'cyd_dino:small_meat'] }],
    ["cyd_dino:micro", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:ohmdenosaurus", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:onchopristis", { tags: ["minecraft:is_fish"] }],
    ["cyd_dino:pachy", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:para", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:prognathodon", { tags: ["minecraft:is_fish"], items: ['cyd_dino:large_meat'] }],
    ["cyd_dino:psitacosaurus", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:ptera", { tags: ["minecraft:is_fish", "minecraft:is_meat"] }],
    ["cyd_dino:quetz", { tags: ["minecraft:is_fish", "minecraft:is_meat"] }],
    ["cyd_dino:simolestes", { tags: ["minecraft:is_fish"] }],
    ["cyd_dino:sinotyrannus", { items: ['cyd_dino:large_meat', 'cyd_dino:small_meat'] }],
    ["cyd_dino:spino", { tags: ["minecraft:is_fish", "minecraft:is_meat"] }],
    ["cyd_dino:stego", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:stegouros", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:styrac", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:suchomimus", { tags: ["minecraft:is_fish"], items: ['cyd_dino:large_meat'] }],
    ["cyd_dino:trex", { tags: ["minecraft:is_meat"] }],
    ["cyd_dino:trice", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:tupandactylus", { tags: ["minecraft:is_fish"], items: ['cyd_dino:small_meat'] }],
    ["cyd_dino:utah", { tags: ["minecraft:is_meat"] }],
    ["cyd_dino:velo", { tags: ["minecraft:is_meat"] }],
    ["cyd_dino:xiphactinus", { tags: ["minecraft:is_fish"] }],
    ["cyd_dino:zhanghenlong", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
    ["cyd_dino:skull_trex", { items: ['cyd_dino:large_meat'] }],
    ["cyd_dino:skull_velo", { tags: ["minecraft:is_meat"], items: ['cyd_dino:small_meat', 'cyd_dino:large_meat'] }],
    ["cyd_dino:skull_stegospheryx", { items: ['cyd_dino:large_meat'] }],
    ["cyd_dino:spycomellus", { items: ['minecraft:apple', 'minecraft:golden_apple', 'minecraft:enchanted_golden_apple', 'minecraft:sweet_berries', 'cyd_dino:club_moss', 'cyd_dino:dicroidium', 'cyd_dino:giant_horsetail', 'cyd_dino:gunnera', 'cyd_dino:fiddlehead_fern', 'cyd_dino:lycopod'], regex: ['_leaves', '_sapling'] }],
])

const mobsIds = Array.from(diets.keys())

mc.world.afterEvents.dataDrivenEntityTrigger.subscribe(event => {
    const { entity } = event;
    entity.dimension.spawnItem(new mc.ItemStack(entity.typeId.replace('baby_', '') + '_egg'), entity.location)
    const player = entity.dimension.getEntities({ closest: 1, location: entity.location, type: 'minecraft:player' })[0];
    try {
        updateTrust(mc.world.getEntity(entity.getDynamicProperty('parent') || ''), player, -100)
    } catch (error) { } //Incase entity is not loaded; entity is dead, egg is placed by player
    try {
        updateTrust(mc.world.getEntity(entity.getDynamicProperty('parent2') || ''), player, -100)
    } catch (error) { } //Incase entity is not loaded; entity is dead, egg is placed by player
    entity.remove()
}, { eventTypes: ['cyd_dino:picked_up'] })

/**
 * @param {Diet} diet 
 * @param {mc.ItemStack} itemStack 
 */
function isCorrectDietaryItem(diet, itemStack) {
    if (diet === undefined) return false;
    if (diet.tags && itemStack.getTags().some(tag => diet.tags.includes(tag))) return true;
    if (diet.items?.includes(itemStack.typeId)) return true;
    if (diet.regex && diet.regex.some(rgx => itemStack.typeId.match(new RegExp(rgx))?.length > 0)) return true;
    return false;
}

const tameCommandEvents = new Map([
    ['cyd_dino:tame_command_menu.stay', 'cyd_dino:stay'],
    ['cyd_dino:tame_command_menu.follow', 'cyd_dino:follow'],
    ['cyd_dino:tame_command_menu.attack', 'cyd_dino:attack'],
    ['cyd_dino:tame_command_menu.guard', 'cyd_dino:guard'],
    ['cyd_dino:tame_command_menu.hurt_nearby_entities', 'cyd_dino:enable_nearby_mob_hurt'],
    ['cyd_dino:tame_command_menu.stop_hurting_nearby_entities', 'cyd_dino:disable_nearby_mob_hurt'],
    ['cyd_dino:tame_command_menu.destroy_trees', 'cyd_dino:toggle_tree_destruction'],
    ['cyd_dino:tame_command_menu.stop_destroying_trees', 'cyd_dino:toggle_tree_destruction'],
    ['cyd_dino:tame_command_menu.fish', 'cyd_dino:try_fish'],
    ['cyd_dino:tame_command_menu.stop_fishing', 'cyd_dino:stop_trying_to_fish'],
])

/**
 * @param {mc.Player} player 
 * @param {mc.Entity} creature 
 */
async function showTameCommandMenu(player, creature) {
    const creatureName = creature.nameTag ? creature.nameTag : { translate: 'entity.' + creature.typeId + '.name' };
    const actions = [
        'cyd_dino:tame_command_menu.stay',
        'cyd_dino:tame_command_menu.follow',
        'cyd_dino:tame_command_menu.attack',
        'cyd_dino:tame_command_menu.guard'
    ]

    await mc.system.waitTicks(1);

    if (!creature.isValid()) return;
    if (!creature.typeId.startsWith('cyd_dino:')) return;

    const tameableComponent = creature.getComponent('tameable')

    if (!tameableComponent || !tameableComponent.isTamed || tameableComponent.tamedToPlayerId != player.id) return;

    if (creature.getComponent('rideable')?.getRiders()?.includes(player)) {
        if (creature.getProperty('cyd_dino:is_tree_cutter')) {
            if (!creature.getProperty('cyd_dino:is_cutting_trees')) actions.push('cyd_dino:tame_command_menu.destroy_trees')
            else actions.push('cyd_dino:tame_command_menu.stop_destroying_trees')
        }

        if (!creature.getProperty('cyd_dino:is_hurting_nearby_mobs')) actions.push('cyd_dino:tame_command_menu.hurt_nearby_entities')
        else actions.push('cyd_dino:tame_command_menu.stop_hurting_nearby_entities')
    }

    if (creature.getProperty('cyd_dino:utility') == 'fisher') {
        if (creature.getProperty('cyd_dino:is_trying_fishing') || creature.getProperty('cyd_dino:is_fishing')) actions.push('cyd_dino:tame_command_menu.stop_fishing')
        else actions.push('cyd_dino:tame_command_menu.fish')
    }

    const form = new ui.ActionFormData()
    form.title(creatureName)
    actions.forEach(action => form.button({ translate: action }))

    const result = await form.show(player);
    if (result.canceled) return;

    const personality = creature.getProperty('cyd_dino:personality')

    if (personality == 'rebellious' && Math.random() < 0.3) { player.onScreenDisplay.setActionBar(['§6', creatureName, '§r', { translate: 'cyd_dino:tame_command_menu.disobeyed' }]); return }

    if (result.selection === 0) creature.getComponent('rideable')?.ejectRiders()

    creature.triggerEvent(tameCommandEvents.get(actions[result.selection]))
    player.onScreenDisplay.setActionBar(['§6', creatureName, '§r', { translate: 'cyd_dino:tame_command_menu.will_now' }, '§6', { translate: actions[result.selection] }])
}

/**
 * @param {mc.Player} player
 */
function spawnProjectile(player) {
    player.runCommandAsync("ride @s stop_riding");
    player.playSound(SOUND_PROJECTILE_THROW, SOUND_OPTIONS);
    const spawnLocation = player.getHeadLocation()
    mc.system.run(() => {
        const proj = player.dimension.spawnEntity("cyd_dino:tranq_dart", spawnLocation);
        const playerViewDirection = new V3(player.getViewDirection());
        proj.addEffect('slow_falling', 1000, { amplifier: 1, showParticles: false });
        // proj.clearVelocity();
        const projComp = (proj.getComponent(mc.EntityComponentTypes.Projectile));
        projComp.owner = player;
        projComp.shoot(playerViewDirection.scale(IMPULSE));
    });
}


/** @param {mc.Player} player  */
function playerHurtHandler(player) {
    try {
        player.dimension.getEntities({ families: ['tamed'], location: player.location, maxDistance: 16 }).filter(x => x.getComponent('tameable')?.tamedToPlayerId == player.id).forEach(entity => {
            if (entity.getProperty('cyd_dino:personality') == 'shy') entity.addEffect('strength')
        })
    } catch (error) {

    }
}

function speedBoostPersonalityBonus(entity, personality) {
    if (personality != 'loyal' && personality != 'hot_headed' && personality != 'brave') return;
    entity.addEffect('speed', 40)
}

mc.world.afterEvents.entityHealthChanged.subscribe(event => {
    try {
        const { newValue, entity } = event;
        const percentage = newValue / entity.getComponent('health')?.effectiveMin;
        if (percentage <= 0.5 && percentage > 0.25) {
            entity.addEffect("speed", 1000, { amplifier: 3, showParticles: false });
            entity.addEffect("strength", 1000, { amplifier: 3, showParticles: false })
        } else if (percentage <= 0.25) {
            entity.addEffect("slowness", 1000, { amplifier: 3, showParticles: false });
        }
    } catch ($) { }
}, { entityTypes: mobsIds })

mc.world.afterEvents.dataDrivenEntityTrigger.subscribe(event => {
    if (event.eventId != 'cyd_dino:tamed') return

    const entity = event.entity
    if (!entity || !entity.typeId.startsWith('cyd_dino:')) return

    const ownerNametag = entity.getComponent('minecraft:tameable')?.tamedToPlayer?.nameTag
    if (!ownerNametag) return

    const name = `${ownerNametag}'s ${dinoData.get(entity.typeId)?.name ?? 'Dino'}`
    entity.nameTag = name
})

mc.world.beforeEvents.worldInitialize.subscribe(eventData => {
    const { blockComponentRegistry, itemComponentRegistry } = eventData;
    blockComponentRegistry.registerCustomComponent('cyd_dino:remove_snow', {
        onTick(e) {
            const { block, dimension } = e;
            const spawnLocation = block.above()
            const SNOW_MAP = {
                thin: 'cyd_dino:cheirolepidiceae_thin_snow',
                thick: 'cyd_dino:cheirolepidiceae_thick_snow'
            };

            if (spawnLocation.typeId.includes('minecraft:snow_layer')) {
                spawnLocation.setType('air');

                let curr = block;
                let depth = 0;
                while (depth < 5) {
                    const { typeId, x, y, z } = curr;
                    const density = typeId.includes('thin') ? 'thin'
                        : typeId.includes('thick') ? 'thick'
                            : null;
                    if (!density) break;

                    curr.setType(SNOW_MAP[density]);
                    curr = dimension.getBlock({ x, y: y - 1, z });
                    depth++;
                }
            }
        }
    })
})

mc.world.afterEvents.playerPlaceBlock.subscribe(event => {
    const { block, dimension } = event

    // Handle stairs updates
    if (block.hasTag('cyd_dino:stairs')) {
        update_stairs_around(block)
    }

    if (block.typeId != 'cyd_dino:water_lily') return
    const above = {
        x: block.location.x,
        y: block.location.y + 1,
        z: block.location.z
    }

    dimension.getBlock(above).setPermutation(mc.BlockPermutation.resolve('cyd_dino:water_lily'))
    block.setPermutation(mc.BlockPermutation.resolve('minecraft:air'))

    dimension.playSound('dig.grass', above, { volume: 0.8 })
})


mc.world.afterEvents.entityDie.subscribe(event => {
    const entity = event.deadEntity;

    if (entity.typeId === 'cyd_dino:skull_trex') {
        const boss = entity.dimension.getEntities({ type: 'cyd_dino:dark_draxar_boss', closest: 1, location: entity.location, maxDistance: 50 })[0];
        boss?.addTag('trex_defeated');
    }
})

mc.system.runInterval(() => {
    const ashEnabled = mc.world.getDynamicProperty('cyd_dino:enabled_ash') ?? true;

    if (!ashEnabled) return;
    mc.world.getAllPlayers().forEach(async player => {
        const biome = await getBiome(player);

        if (biome && biome === 'ash') {
            player.dimension.spawnParticle("cyd_dino:volcanic_ash", player.location)
        }
    })
}, 0.25 * mc.TicksPerSecond)