import { world, system, ItemStack, BlockPermutation, Direction } from "@minecraft/server";
import { Vector } from './vector.js';
import { ActionFormData } from "@minecraft/server-ui";

//console.warn("Console loaded");

const RARITIES = [
    "common",
    "rare",
    "epic",
    "legendary",
]

const ABILITY_INFO = [
    //lumus
    { ability: "lumus", element: "light", cd: 10, duration: [10, 20, 30, 40], damage: [], range: [12, 12, 12, 12], effects: [] },
    { ability: "lumus", element: "dark", cd: 20, duration: [15, 15, 15, 15], damage: [], range: [10, 20, 30, 40], effects: ["blindness"] },
    //balling
    { ability: "balling", element: "nature", cd: 3, duration: [], damage: [10, 15, 20, 25], range: [], effects: ["poison", "oozing"] },
    { ability: "balling", element: "water", cd: 3, duration: [], damage: [10, 15, 20, 25], range: [], effects: ["weakness", "slowness"] },
    { ability: "balling", element: "fire", cd: 3, duration: [], damage: [10, 15, 20, 25], range: [], effects: ["burning"] },
    //tornado
    { ability: "tornado", element: "fire", cd: 9, duration: [4, 4, 4, 4], damage: [4, 6, 8, 10], range: [3, 4, 4, 5], effects: ["burning"] },
    { ability: "tornado", element: "water", cd: 9, duration: [4, 4, 4, 4], damage: [4, 6, 8, 10], range: [3, 4, 4, 5], effects: ["nausea"] },
    { ability: "tornado", element: "wind", cd: 9, duration: [4, 4, 4, 4], damage: [4, 6, 8, 10], range: [3, 4, 4, 5], effects: ["wind_charged"] },
    //shield
    { ability: "shield", element: "fire", cd: 15, duration: [5, 7, 9, 11], damage: [], range: [], effects: ["burning", "thorn"] },
    { ability: "shield", element: "water", cd: 15, duration: [5, 7, 9, 11], damage: [], range: [], effects: ["water_breathing", "thorn"] },
    { ability: "shield", element: "wind", cd: 15, duration: [5, 7, 9, 11], damage: [], range: [], effects: ["slow_falling", "thorn"] },
    { ability: "shield", element: "dark", cd: 15, duration: [5, 7, 9, 11], damage: [], range: [], effects: ["wither", "resistance", "thorn"] },
    //gayser
    { ability: "geyser", element: "fire", cd: 5, duration: [], damage: [15, 20, 25, 30], range: [], effects: ["burning"] },
    { ability: "geyser", element: "water", cd: 5, duration: [], damage: [15, 20, 25, 30], range: [], effects: ["nausea", "slowness"] },
    { ability: "geyser", element: "wind", cd: 5, duration: [], damage: [15, 20, 25, 30], range: [], effects: ["wind_charged"] },
    //afterlife
    { ability: "afterlife", element: "light", cd: 10, duration: [], damage: [], range: [3, 5, 7, 9], effects: ["lumus"] },
    { ability: "afterlife", element: "dark", cd: 10, duration: [], damage: [], range: [3, 5, 7, 9], effects: ["wither", "blindness", "lifesteal"] },
    { ability: "afterlife", element: "fire", cd: 10, duration: [], damage: [], range: [3, 5, 7, 9], effects: ["burning"] },
    { ability: "afterlife", element: "nature", cd: 10, duration: [], damage: [], range: [3, 5, 7, 9], effects: ["oozing"] },
    { ability: "afterlife", element: "wind", cd: 10, duration: [], damage: [], range: [3, 5, 7, 9], effects: ["knockback"] },
    { ability: "afterlife", element: "water", cd: 10, duration: [], damage: [], range: [3, 5, 7, 9], effects: ["nausea", "slowness"] },
    //fury
    { ability: "fury", element: "fire", cd: 5, duration: [], damage: [5, 10, 15, 20], range: [], effects: ["burning"] },
    { ability: "fury", element: "water", cd: 5, duration: [], damage: [5, 10, 15, 20], range: [], effects: ["nausea", "slowness"] },
    { ability: "fury", element: "wind", cd: 5, duration: [], damage: [5, 10, 15, 20], range: [], effects: ["wind_charged"] },
    //power ring
    { ability: "power_ring", element: "fire", cd: 2, duration: [], damage: [2, 4, 6, 8], range: [], effects: ["burning"] },
    { ability: "power_ring", element: "wind", cd: 2, duration: [], damage: [2, 4, 6, 8], range: [], effects: ["oozing", "knockback"] },
    { ability: "power_ring", element: "water", cd: 2, duration: [], damage: [2, 4, 6, 8], range: [], effects: ["nausea", "mining_fatigue", "slowness"] },
    { ability: "power_ring", element: "nature", cd: 2, duration: [], damage: [2, 4, 6, 8], range: [], effects: ["root"] },
    { ability: "power_ring", element: "light", cd: 2, duration: [], damage: [2, 4, 6, 8], range: [], effects: ["lumus"] },
    { ability: "power_ring", element: "dark", cd: 2, duration: [], damage: [2, 4, 6, 8], range: [], effects: ["lifesteal"] },
    //rifle
    { ability: "rifle", element: "fire", cd: 5, duration: [], damage: [8, 10, 12, 14], range: [], effects: ["burning"] },
    { ability: "rifle", element: "wind", cd: 5, duration: [], damage: [8, 10, 12, 14], range: [], effects: ["wind_charged", "knockback"] },
    { ability: "rifle", element: "water", cd: 5, duration: [], damage: [8, 10, 12, 14], range: [], effects: ["slowness"] },
    { ability: "rifle", element: "nature", cd: 5, duration: [], damage: [8, 10, 12, 14], range: [], effects: ["oozing", "root"] },
    { ability: "rifle", element: "light", cd: 5, duration: [], damage: [8, 10, 12, 14], range: [], effects: ["burning"] },
    { ability: "rifle", element: "dark", cd: 5, duration: [], damage: [8, 10, 12, 14], range: [], effects: ["blindness", "wither"] },
    //blaster
    { ability: "blaster", element: "fire", cd: 10, duration: [4, 4, 4, 4], damage: [1, 2, 3, 4], range: [], effects: ["burning"] },
    { ability: "blaster", element: "water", cd: 10, duration: [4, 4, 4, 4], damage: [1, 2, 3, 4], range: [], effects: ["slowness"] },
    { ability: "blaster", element: "wind", cd: 10, duration: [4, 4, 4, 4], damage: [1, 2, 3, 4], range: [], effects: ["knockback", "wind_charged"] },
    { ability: "blaster", element: "light", cd: 10, duration: [4, 4, 4, 4], damage: [1, 2, 3, 4], range: [], effects: ["regeneration"] },
    { ability: "blaster", element: "dark", cd: 10, duration: [4, 4, 4, 4], damage: [1, 2, 3, 4], range: [], effects: ["wither", "blindness", "slowness"] },
    //lifesteal
    { ability: "lifesteal", element: "fire", cd: 10, duration: [5, 5, 5, 5], damage: [2, 4, 6, 8], range: [5, 10, 15, 20], effects: ["burning"] },
    { ability: "lifesteal", element: "dark", cd: 10, duration: [5, 5, 5, 5], damage: [2, 4, 6, 8], range: [5, 10, 15, 20], effects: ["wither", "blindness"] },
    { ability: "lifesteal", element: "nature", cd: 10, duration: [5, 5, 5, 5], damage: [2, 4, 6, 8], range: [5, 10, 15, 20], effects: ["root"] },
    { ability: "lifesteal", element: "light", cd: 10, duration: [5, 5, 5, 5], damage: [2, 4, 6, 8], range: [5, 10, 15, 20], effects: ["regeneration"] },
    //wings
    // { ability: "wings", element: "fire", cd: [40, 55, 70, 100], duration: [30, 45, 60, 90], damage: [], range: [], effects: ["burning"] },
    // { ability: "wings", element: "water", cd: [40, 55, 70, 100], duration: [30, 45, 60, 90], damage: [], range: [], effects: ["water_breathing"] },
    // { ability: "wings", element: "wind", cd: [40, 55, 70, 100], duration: [30, 45, 60, 90], damage: [], range: [], effects: ["speed"] },
    // { ability: "wings", element: "light", cd: [40, 55, 70, 100], duration: [30, 45, 60, 90], damage: [], range: [], effects: ["regeneration"] },
    // { ability: "wings", element: "dark", cd: [40, 55, 70, 100], duration: [30, 45, 60, 90], damage: [], range: [], effects: ["resistance"] },
    // { ability: "wings", element: "nature", cd: [40, 55, 70, 100], duration: [30, 45, 60, 90], damage: [], range: [], effects: ["strength", "haste"] },
    //war fog
    { ability: "war_fog", element: "dark", cd: 10, duration: [3, 4, 5, 6], damage: [], range: [6, 8, 10, 12], effects: ["blindness", "wither"] },
    { ability: "war_fog", element: "light", cd: 10, duration: [3, 4, 5, 6], damage: [], range: [6, 8, 10, 12], effects: ["regeneration", "lumus"] },
    //wall
    { ability: "wall", element: "light", cd: 5, duration: [4, 4, 4, 4], damage: [2, 4, 6, 8], range: [], effects: ["knockback", "regeneration"] },
    { ability: "wall", element: "dark", cd: 5, duration: [4, 4, 4, 4], damage: [2, 4, 6, 8], range: [], effects: ["knockback", "wither"] },
    { ability: "wall", element: "water", cd: 5, duration: [4, 4, 4, 4], damage: [2, 4, 6, 8], range: [], effects: ["knockback", "slowness"] },
    { ability: "wall", element: "wind", cd: 5, duration: [4, 4, 4, 4], damage: [2, 4, 6, 8], range: [], effects: ["knockback", "slow_falling"] },
    { ability: "wall", element: "fire", cd: 5, duration: [4, 4, 4, 4], damage: [2, 4, 6, 8], range: [], effects: ["knockback", "burning"] },
    { ability: "wall", element: "nature", cd: 5, duration: [4, 4, 4, 4], damage: [2, 4, 6, 8], range: [], effects: ["knockback", "oozing"] },

]



function getAbilityInfo(ability, element, rarity) {
    const abilityInfo = ABILITY_INFO.find(info => info.ability === ability && info.element === element);
    if (!abilityInfo) return null;

    const rarityLevel = RARITIES.indexOf(rarity);
    const { cd, duration, damage, range, effects } = abilityInfo;

    return {
        cd: Array.isArray(cd) ? cd[rarityLevel] : cd,
        duration: Array.isArray(duration) && duration.length > 0 ? duration[rarityLevel] : null,
        damage: Array.isArray(damage) && damage.length > 0 ? damage[rarityLevel] : null,
        range: Array.isArray(range) && range.length > 0 ? range[rarityLevel] : null,
        effects: effects.length > 0 ? effects : null,
    };
}








system.runInterval(() => {
    for (const player of world.getAllPlayers()) {

        const selectedSlot = player.selectedSlotIndex;
        const handItem = player.getComponent("inventory").container.getItem(selectedSlot);
        if (!handItem) return;
        if (!handItem.typeId.includes("scepter") && !handItem.typeId.includes("orb") && !handItem.typeId.includes("tome")) return;

        const { weapon, element, rarity, tomeDurability } = getWeaponInfo(handItem);

        const abilities = [];
        for (const line of handItem.getLore()) {
            if (line.startsWith("§l§eAbility")) continue;
            abilities.push(line.replace("-§d§l", "").replace("§r", ""));
        }
        if (abilities.length == 0) return;

        const selectedAbility = handItem.getDynamicProperty("mf_mg:weapon_selected_ability") ?? 0;
        const cd = player.getDynamicProperty(`mf_mg:weapon_cooldown:${abilities[selectedAbility].replace(" ", "_").toLocaleLowerCase()}:${element}`) ?? 0; // time in second
        const abilityCount = abilities.length;

        if (player.isSneaking) {
            if (weapon == "scepter" || weapon == "orb") {
                const abilityDisplay = abilities
                    .map((ability, index) => (index === selectedAbility ? `§d§l${ability}§r` : ability))
                    .join(" | ");

                player.onScreenDisplay.setActionBar(abilityDisplay);
            }

        } else {
            if (weapon == "scepter" || weapon == "orb")
                player.onScreenDisplay.setActionBar(`§l${abilities[selectedAbility]}§r${cd > 0 ? `\nCooldown: §l${cd}§rs` : ""}`);
            else if (weapon == "tome")
                player.onScreenDisplay.setActionBar(`Durability: §l${tomeDurability}/4§r${cd > 0 ? `\nCooldown: §l${cd}§rs` : ""}`);

        }
    }
});



function switchAbility(itemStack, abilities, player) {
    const currentAbilityIndex = itemStack.getDynamicProperty("mf_mg:weapon_selected_ability") ?? 0;
    // console.warn(currentAbilityIndex)
    const nextAbilityIndex = (currentAbilityIndex + 1) % abilities.length;
    itemStack.setDynamicProperty("mf_mg:weapon_selected_ability", nextAbilityIndex);

    const selectedSlot = player.selectedSlotIndex;
    player.getComponent("inventory").container.setItem(selectedSlot, itemStack)
}


class Cooldown {
    constructor(player, ability, element) {
        this.player = player
        this.ability = ability
        this.element = element
    }

    startCooldown(time) {
        let cd = time
        this.player.setDynamicProperty(`mf_mg:weapon_cooldown:${this.ability}:${this.element}`, cd)

        const Launcher = system.runInterval(() => {

            if (!this.player || cd <= 0) {
                system.clearRun(Launcher);
                this.stopCooldown()
                return;
            }
            cd--
            this.player.setDynamicProperty(`mf_mg:weapon_cooldown:${this.ability}:${this.element}`, cd)

        }, 20 * 1)
    }

    stopCooldown() {
        this.player.setDynamicProperty(`mf_mg:weapon_cooldown:${this.ability}:${this.element}`, null)
    }
}

class ProjectileDist {
    constructor(player, projectile, ability, element) {
        this.player = player
        this.projectile = projectile
        this.ability = ability
        this.element = element
    }

    checkDist(dist) {
        const Launcher = system.runInterval(() => {
            try {
                //  console.warn(Vector.distanceBetween(this.player.location, this.projectile.location))
                if (!this.player || !this.projectile || Vector.distanceBetween(this.player.location, this.projectile.location) > dist) {
                    system.clearRun(Launcher);
                    this.removeProjectile()
                    return;
                }
            } catch {
                system.clearRun(Launcher);
                this.removeProjectile()
                return;
            }

        }, 10 * 1)
    }

    removeProjectile() {
        // play ability

        try {
            if (this.projectile) this.projectile.remove()
        } catch (e) {
            //console.error(e)
        }
    }
}













world.afterEvents.itemUse.subscribe((e) => {
    const { itemStack, source: player } = e

    if (player.typeId !== "minecraft:player") return;
    if (!itemStack.typeId.includes("scepter") && !itemStack.typeId.includes("orb") && !itemStack.typeId.includes("tome")) return;

    const interactFamilies = [
        "mf_mg:fountain",
        "mf_mg:altar",
        "mf_mg:forge",
        "mf_mg:trader",
        "mf_mg:fountain_dummy",
        "mf_mg:altar_dummy",
        "mf_mg:forge_dummy",
        "mf_mg:weapon_entity"
    ]

    for (const interactEntity of player.dimension.getEntities({ location: player.location, maxDistance: 8 })) {
        if (interactFamilies.includes(interactEntity.getComponent("minecraft:type_family")?.getTypeFamilies()[0])) {
            if (isEntityInPlayerCursor(player, interactEntity, { h: 3, l: 2 })) {
                return;
            }
        }
    }

    const { weapon, element, rarity, tomeDurability } = getWeaponInfo(itemStack)

    const abilities = []
    for (const line of itemStack.getLore()) {
        if (line.startsWith("§l§eAbility")) continue;
        abilities.push(line.replace("-§d§l", "").replace(" ", "_").replace("§r", "").toLocaleLowerCase())
    }
    if (abilities.length == 0) return;

    if (player.isSneaking && ["scepter", "orb"].includes(weapon)) {
        switchAbility(itemStack, abilities, player)
        return;
    }

    const selectedAbility = itemStack.getDynamicProperty("mf_mg:weapon_selected_ability") ?? 0;
    const ability = abilities[selectedAbility]
    const cd = player.getDynamicProperty(`mf_mg:weapon_cooldown:${ability}:${element}`) ?? 0; // time in second
    const abilityCount = abilities.length;

    if (cd > 0) return

    let weaponTimeout = 0
    if (weapon == "tome") weaponTimeout = 15
    if (weapon == "scepter") weaponTimeout = 1
    if (weapon == "orb") weaponTimeout = 12

    system.runTimeout(() => {

        system.runTimeout(() => {
            if (weapon == "tome")
                handleTomeBurability(itemStack, tomeDurability, element, rarity, player)
        }, 20)

        //  console.warn(ability)
        switch (ability) {
            case "tornado":
                new Tornado(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:tornado", player.location)
                break;

            case "geyser":
                new Geyser(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:geyser", player.location)
                break;

            case "lumus":
                new Lumus(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:lumus", player.location)
                break;

            case "shield":
                new Shield(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:shield", player.location)
                break;

            case "balling":
                new Balling(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:balling", player.location)
                break;
            case "afterlife":
                new Afterlife(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:after_life", player.location)
                break;
            case "fury":
                new Fury(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:fury", player.location)
                break;
            // case "wings":
            //     new Wings(player, itemStack, element, rarity, ability).launchspell()

            //     break;
            case "rifle":
                new Rifle(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:rifle", player.location)
                break;
            case "blaster":
                new Blaster(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:blaster", player.location)
                break;
            case "lifesteal":
                new Lifesteal(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:life_steal", player.location)
                break;
            case "power_ring":
                new PowerRings(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:power_ring", player.location)
                break;
            case "war_fog":
                new WarFog(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:war_fog", player.location)
                break;
            case "wall":
                new Wall(player, itemStack, element, rarity, ability).launchspell()
                player.dimension.playSound("mf_mg:wall", player.location)
                break;

        }

    }, weaponTimeout)



})










function handleTomeBurability(item, tomeDurability, element, rarity, player) {
    const newDura = tomeDurability - 1
    let newTome = null

    if (newDura >= 1) {
        newTome = new ItemStack(`mf_mg:tome_${element}_${rarity}_${newDura}`, 1)

        const currentAbilityIndex = item.getDynamicProperty("mf_mg:weapon_selected_ability") ?? 0;
        newTome.setDynamicProperty("mf_mg:weapon_selected_ability", currentAbilityIndex);
        const currentLore = item.getLore()
        newTome.setLore(currentLore)
    }

    const selectedSlot = player.selectedSlotIndex;
    player.getComponent("inventory").container.setItem(selectedSlot, newTome)
}




function getWeaponInfo(item) {
    const itemParts = item.typeId.split("_")
    const element = itemParts[2]
    const rarity = itemParts[3]
    const tomeDurability = itemParts[4] ?? null
    const weapon = itemParts[1].split(":")[1]

    return { weapon: weapon, element: element, rarity: rarity, tomeDurability: tomeDurability }

}

























// PROJECTILES

class Rifle {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const shootAngles = [-10, 0, 10]

        for (const angle of shootAngles) {
            const projectile = this.player.dimension.spawnEntity("mf_mg:projectile_trail", spawnLoc)
            projectile.setProperty("mf_mg:element", this.element)
            projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
            projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
            projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
            projectile.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null)
            projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
            projectile.setDynamicProperty("mf_mg:projectile_range", range)

            projectile.addEffect("slow_falling", 20 * 10, { amplifier: 3, showParticles: false })
            projectile.applyImpulse(Vector.multiply(getSideOffsetViewDirection(this.player, angle), power))
            new ProjectileDist(this.player, projectile, this.ability, this.element).checkDist(100)
        }

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

    }

}

class Balling {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const projectile = this.player.dimension.spawnEntity("mf_mg:projectile_trail", spawnLoc)
        projectile.setProperty("mf_mg:element", this.element)
        projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
        projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
        projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
        projectile.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null)
        projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
        projectile.setDynamicProperty("mf_mg:projectile_range", range)

        projectile.setDynamicProperty("mf_mg:projectile_bounce", 3) // bounce number

        projectile.addEffect("slow_falling", 20 * 10, { amplifier: 3, showParticles: false })
        projectile.applyImpulse(Vector.multiply(this.player.getViewDirection(), power))
        new ProjectileDist(this.player, projectile, this.ability, this.element).checkDist(100)


        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)
    }
}


class Geyser {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const projectile = this.player.dimension.spawnEntity("mf_mg:projectile_trail", spawnLoc)
        projectile.setProperty("mf_mg:element", this.element)
        projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
        projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
        projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
        projectile.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null)
        projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
        projectile.setDynamicProperty("mf_mg:projectile_range", range)

        projectile.addEffect("slow_falling", 20 * 10, { amplifier: 3, showParticles: false })
        projectile.applyImpulse(Vector.multiply(this.player.getViewDirection(), power))
        new ProjectileDist(this.player, projectile, this.ability, this.element).checkDist(100)


        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)
    }
}

class Fury { // geyser x3 bounce -> launch Balling class en mode fury qui pour chaque rebond lance le geyser
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const projectile = this.player.dimension.spawnEntity("mf_mg:projectile_trail", spawnLoc)
        projectile.setProperty("mf_mg:element", this.element)
        projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
        projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
        projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
        projectile.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null)
        projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
        projectile.setDynamicProperty("mf_mg:projectile_range", range)

        projectile.setDynamicProperty("mf_mg:projectile_bounce", 3) // bounce number

        projectile.addEffect("slow_falling", 20 * 10, { amplifier: 3, showParticles: false })
        projectile.applyImpulse(Vector.multiply(this.player.getViewDirection(), power))
        new ProjectileDist(this.player, projectile, this.ability, this.element).checkDist(100)


        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)
    }
}

class Wall { // animation for out of ground ????
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const projectile = this.player.dimension.spawnEntity("mf_mg:projectile_trail", spawnLoc)
        projectile.setProperty("mf_mg:element", this.element)
        projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
        projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
        projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
        projectile.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null)
        projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
        projectile.setDynamicProperty("mf_mg:projectile_range", range)

        projectile.addEffect("slow_falling", 20 * 10, { amplifier: 3, showParticles: false })
        projectile.applyImpulse(Vector.multiply(this.player.getViewDirection(), power))
        new ProjectileDist(this.player, projectile, this.ability, this.element).checkDist(100)

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

    }
}

class PowerRings {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const projectile = this.player.dimension.spawnEntity("mf_mg:projectile_power_ring", spawnLoc)
        projectile.setProperty("mf_mg:element", this.element)
        projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
        projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
        projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
        projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
        projectile.setDynamicProperty("mf_mg:projectile_range", range)
        projectile.setDynamicProperty("mf_mg:projectile_owner", this.player.id)

        projectile.setRotation(this.player.getRotation())

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

        new ProjectileDist(this.player, projectile, this.ability, this.element).checkDist(100)
        this.powerRingBehavior(projectile, damage, range, effects, this.player.getViewDirection())
    }

    powerRingBehavior(projectileEntity, damage, range, effects, dirVec) {

        let time = 0
        const Launcher = system.runInterval(() => {
            try {
                if (!projectileEntity) {
                    system.clearRun(Launcher);
                    return;
                }
                const accesRing = projectileEntity.location
            } catch {
                system.clearRun(Launcher);
                return;
            }

            let nextLoc = Vector.add(projectileEntity.location, Vector.multiply(dirVec, 1))

            try {
                const nextLocBlock = projectileEntity.dimension.getBlock(nextLoc)
                if (nextLocBlock) {
                    if (!nextLocBlock.isAir) {

                        projectileEntity.remove()
                        system.clearRun(Launcher);
                        return;
                    }
                }
            } catch {
                system.clearRun(Launcher);
                return;
            }

            projectileEntity.teleport(nextLoc)

            try {
                const power = 0.05
                for (let hitEntity of projectileEntity.dimension.getEntities({ location: projectileEntity.location, maxDistance: 4 })) {
                    if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton')) || hitEntity.typeId == "mf_mg:trader" && hitEntity.typeId !== "minecraft:item" && hitEntity.id !== this.player.id && Vector.distanceBetween(Vector.add(hitEntity.location, { x: 0, y: 1, z: 0 }), projectileEntity.location) < 2) {
                        if (hitEntity.hasTag("mf_mg:has_power_ring")) {
                            try {
                                hitEntity.addTag("mf_mg:power_ring_bonus_explode")
                            } catch { }

                        } else {
                            this.teleportRingMob(hitEntity, damage, effects)

                        }

                        try {
                            projectileEntity.remove()
                        } catch { }


                    }
                }
            } catch { }


            time++


        }, 1)

    }

    teleportRingMob(hitEntity, damage, effects) {
        hitEntity.addTag("mf_mg:has_power_ring")

        const ringLoc = Vector.add(hitEntity.location, { x: 0, y: 0, z: 0 })
        const ringMob = hitEntity.dimension.spawnEntity("mf_mg:ring_mob", ringLoc)
        ringMob.setProperty("mf_mg:element", this.element)
        ringMob.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)

        let time = 0
        const Launcher = system.runInterval(() => {
            try {
                if (hitEntity.hasTag("mf_mg:power_ring_bonus_explode")) {
                    hitEntity.removeTag("mf_mg:has_power_ring")

                    const impactEntity = hitEntity.dimension.spawnEntity("mf_mg:projectile_impact", ringMob.location);
                    impactEntity.setProperty("mf_mg:element", this.element);
                    impactEntity.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1);
                    if (this.element == "dark") {
                        lifestealHandler(this.player, hitEntity, damage, false)
                    }
                    damageAndEffect(hitEntity, damage * 1.5, effects, 3, 1)
                    ringMob.remove()
                    system.clearRun(Launcher);
                    return;
                }
                if (!ringMob || time > (5 * 20)) {
                    hitEntity.removeTag("mf_mg:has_power_ring")
                    if (this.element == "dark") {
                        lifestealHandler(this.player, hitEntity, damage, false)
                    }
                    damageAndEffect(hitEntity, damage, effects, 3, 1)
                    ringMob.remove()
                    system.clearRun(Launcher);
                    return;
                }
                const accesRingMob = ringMob.location
            } catch {
                hitEntity.removeTag("mf_mg:has_power_ring")
                if (this.element == "dark") {
                    lifestealHandler(this.player, hitEntity, damage, false)
                }
                damageAndEffect(hitEntity, damage, effects, 3, 1)
                ringMob.remove()
                system.clearRun(Launcher);
                return;
            }

            ringMob.teleport(Vector.add(hitEntity.location, { x: 0, y: 0, z: 0 }))
            time++

        }, 1)

    }
}


class Lumus {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const projectile = this.player.dimension.spawnEntity("mf_mg:entity_trail", spawnLoc)
        projectile.setProperty("mf_mg:element", this.element)
        projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
        projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
        projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
        projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
        projectile.setDynamicProperty("mf_mg:projectile_range", range)
        projectile.setDynamicProperty("mf_mg:projectile_owner", this.player.id)

        projectile.addEffect("levitation", 20 * 10, { amplifier: 1, showParticles: false })
        projectile.applyImpulse(new Vector(0, 0.5, 0))

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)
    }

    lumusExplode(lumusEntity, range, duration) {
        const ownerId = lumusEntity.getDynamicProperty("mf_mg:projectile_owner")
        let time = duration

        const players = lumusEntity.dimension.getEntities({ location: lumusEntity.location, maxDistance: 50, type: "minecraft:player" })
        if (players.length == 0) return

        const Launcher = system.runInterval(() => {
            try {
                if (time == 0) {
                    system.clearRun(Launcher);
                    this.removeLumus(lumusEntity)
                    return;
                }
            } catch {
                system.clearRun(Launcher);
                this.removeLumus(lumusEntity)
                return;
            }

            if (this.element == "dark") {
                const blindedPlayers = lumusEntity.dimension.getEntities({ location: lumusEntity.location, maxDistance: range, type: "minecraft:player" })
                for (const blindPlayer of blindedPlayers) {
                    if (blindPlayer.id !== ownerId)
                        blindPlayer.addEffect("blindness", 50)
                }
            }
            time--

        }, 20 * 1)

    }

    removeLumus(lumusEntity) {
        if (this.element == "light") {
            lumusEntity.addTag("mf_mg:projectile_lumus_is_removed")
            lumusEntity.runCommand(`fill ~-8 ~-8 ~-8 ~8 ~8 ~8 air replace light_block_15`)
        } else {
            lumusEntity.remove()
        }

    }


}

class Tornado {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)
        let spawnLoc = Vector.add(this.player.getHeadLocation(), Vector.multiply(this.player.getViewDirection(), 2))
        const power = 3

        const projectile = this.player.dimension.spawnEntity("mf_mg:projectile_tornado", spawnLoc)
        projectile.setProperty("mf_mg:element", this.element)
        projectile.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)
        projectile.setDynamicProperty("mf_mg:projectile_ability", this.ability)
        projectile.setDynamicProperty("mf_mg:projectile_damage", damage)
        projectile.setDynamicProperty("mf_mg:projectile_duration", duration)
        projectile.setDynamicProperty("mf_mg:projectile_range", range)
        projectile.setDynamicProperty("mf_mg:projectile_owner", this.player.id)

        projectile.addTag("mf_mg:projectile_tornado")
        projectile.setProperty("mf_mg:range", range)
        projectile.setRotation(this.player.getRotation())

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

        this.tornadoBehavior(projectile, damage, range, effects, this.player.getViewDirection())
    }

    tornadoBehavior(tornadoEntity, damage, range, effects, dirVec) {

        let time = 0
        const Launcher = system.runInterval(() => {
            try {
                if (!tornadoEntity) {
                    system.clearRun(Launcher);
                    return;
                }
                const accesTornado = tornadoEntity.location
            } catch {
                system.clearRun(Launcher);
                return;
            }

            let nextLoc = Vector.add(tornadoEntity.location, Vector.multiply(dirVec, 0.5))
            try {
                const nextLocBlock = tornadoEntity.dimension.getBlock(nextLoc)
                if (nextLocBlock) {
                    if (!nextLocBlock.isAir) nextLoc = Vector.add(nextLoc, { x: 0, y: 0.5, z: 0 })
                }
            } catch {
                system.clearRun(Launcher);
                return;
            }


            tornadoEntity.teleport(nextLoc)

            let canDamage = false
            if (time == 10) {
                time = 0
                canDamage = true
            }

            const power = 0.05
            for (let hitEntity of tornadoEntity.dimension.getEntities({ location: tornadoEntity.location, maxDistance: range })) {
                if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton')) || hitEntity.typeId == "mf_mg:trader" && hitEntity.id !== this.player.id) {
                    try {
                        hitEntity.applyImpulse(Vector.multiply(Vector.subtract(Vector.add({ x: 0, y: 2, z: 0 }, tornadoEntity.location), hitEntity.location), power))
                    } catch {
                        try {
                            hitEntity.applyKnockback(tornadoEntity.getViewDirection().x, tornadoEntity.getViewDirection().z, 6 * power, 6 * power)
                        } catch { }
                    }

                    if (canDamage) {
                        try {
                            damageAndEffect(hitEntity, damage / 2, effects, 1, 1);
                        } catch { }
                    }

                }
            }

            time++

        }, 1)

    }

}

class Lifesteal { // projectile that effect the hit entity -> entity qui viennent vers le joueur comme des xp orb
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

        this.handleLifesteal(duration, effects, range, damage)
    }

    handleLifesteal(duration, effects, range, damage) {

        let time = 0
        let rarityNB = RARITIES.indexOf(this.rarity) + 1

        const Launcher = system.runInterval(() => {
            try {
                if (time > (duration * 2)) {
                    system.clearRun(Launcher);
                    return;
                }
            } catch {
                system.clearRun(Launcher);
                return;

            }

            for (let nearEntity of this.player.dimension.getEntities({ location: this.player.location, maxDistance: range, closest: rarityNB })) {
                if ((!nearEntity.typeId.startsWith("mf_mg") || nearEntity.typeId.includes('skeleton')) && nearEntity.id !== this.player.id && nearEntity.typeId !== "minecraft:item") {

                    const orb = this.player.dimension.spawnEntity("mf_mg:lifesteal_entity", Vector.add(nearEntity.location, { x: 0, y: 1.5, z: 0 }))
                    orb.setProperty("mf_mg:element", this.element)
                    orb.setProperty("mf_mg:rarity", rarityNB)
                    damageAndEffect(nearEntity, 0, effects, 3, 1, this.player)
                    this.teleportOrb(orb, damage / 2, this.player, nearEntity)

                }
            }

            time++

        }, 10)

    }


    teleportOrb(orb, value, player, target) {
        const power = 0.3
        const Launcher = system.runInterval(() => {
            try {
                const playerPosition = Vector.add(player.location, { x: 0, y: 1, z: 0 });
                const orbPosition = orb.location;

                if (Vector.distanceBetween(playerPosition, orbPosition) < 0.2) {
                    if (this.element !== "light")
                        lifestealHandler(player, target, value, true);
                    else
                        lifestealHandler(player, target, value, false);
                    orb.remove();
                    system.clearRun(Launcher);
                    return;
                }

                const direction = Vector.subtract(playerPosition, orbPosition);
                const normalizedDirection = Vector.normalize(direction);
                const movement = Vector.multiply(normalizedDirection, power);

                const newOrbPosition = Vector.add(orbPosition, movement);
                orb.teleport(newOrbPosition);

            } catch {
                system.clearRun(Launcher);
                return;
            }
        }, 1);

    }
}
















// OTHERS

class Blaster { // is it a projectile ? is there a beam particle ? -> kamehameha ??
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

        this.teleportBlaster(duration, effects, range, damage)
    }

    teleportBlaster(duration, effects, range, damage) {

        const ray = this.player.dimension.spawnEntity("mf_mg:blaster_entity", this.player.location)
        ray.setProperty("mf_mg:element", this.element)
        ray.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)

        //   this.player.addEffect("slowness", 20 * duration, { amplifier: 3, showParticles: false })
        this.player.runCommand(`inputpermission set @s movement disabled`)

        let time = 0
        const Launcher = system.runInterval(() => {
            try {
                if (!ray || time > (duration * 20)) {
                    ray.remove()
                    this.player.runCommand(`inputpermission set @s movement enabled`)

                    system.clearRun(Launcher);
                    return;
                }
            } catch {
                ray.remove()
                this.player.runCommand(`inputpermission set @s movement enabled`)

                system.clearRun(Launcher);
                return;

            }

            const rayLoc = Vector.add(this.player.location, { x: this.player.getViewDirection().x * 1, y: 1 + this.player.getViewDirection().y, z: this.player.getViewDirection().z * 1 })
            ray.teleport(rayLoc)

            let beamSize = 15
            let hitEntities = []

            const blockRay = this.player.getBlockFromViewDirection({ includeLiquidBlocks: false, includePassableBlocks: false, maxDistance: 40 })
            if (blockRay) {
                const block = blockRay.block
                let distSize = Vector.distanceBetween(ray.location, getBlockCenter(block))
                if (distSize <= 15) beamSize = distSize

                for (const entityRay of this.player.dimension.getEntities({ location: block.location, maxDistance: 1.5 })) {
                    hitEntities.push(entityRay)
                }

            }

            const entitiesRay = this.player.getEntitiesFromViewDirection({ includeLiquidBlocks: false, includePassableBlocks: false, maxDistance: beamSize })
            for (const entityRay of entitiesRay) {
                if (!hitEntities.includes(entityRay))
                    hitEntities.push(entityRay.entity)
            }

            //  console.warn(beamSize)
            ray.setProperty("mf_mg:size", beamSize - 0.6)
            ray.setProperty("mf_mg:yaw", this.player.getRotation().y)
            ray.setProperty("mf_mg:pitch", this.player.getRotation().x)

            let canDamage = false
            if (time % 2 == 0) {
                canDamage = true
            }

            for (const hitEntity of hitEntities) {

                if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton')) || hitEntity.typeId == "mf_mg:trader" && hitEntity.id !== this.player.id) {

                    try {
                        if (canDamage) {
                            damageAndEffect(hitEntity, damage, effects, 1, 1, this.player);
                        }
                    } catch { }

                }

            }

            time++

        }, 1)

    }
}


class WarFog {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

        this.teleportFog(duration, effects, range)
    }

    teleportFog(duration, effects, range) {

        const fog = this.player.dimension.spawnEntity("mf_mg:war_fog", this.player.location)
        fog.setProperty("mf_mg:element", this.element)
        fog.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)

        this.player.addEffect("slowness", 20 * duration, { amplifier: 1, showParticles: false })
        this.player.addTag("mf_mg:fog_owner")

        let time = 0
        const Launcher = system.runInterval(() => {
            try {
                if (!fog || time > (duration * 20)) {
                    this.player.removeTag("mf_mg:fog_owner")
                    fog.remove()
                    system.clearRun(Launcher);
                    return;
                }
            } catch {
                this.player.removeTag("mf_mg:fog_owner")
                fog.remove()
                system.clearRun(Launcher);
                return;

            }

            const fogLoc = Vector.add(this.player.location, { x: 0, y: 0, z: 0 })
            fog.teleport(fogLoc)

            for (let hitEntity of fog.dimension.getEntities({ location: fog.location, maxDistance: range })) {
                if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton')) || hitEntity.typeId == "mf_mg:trader" && hitEntity.id !== this.player.id) {
                    try {
                        damageAndEffect(hitEntity, 0, effects, 1, 1);
                    } catch { }

                }
            }

            time++

        }, 1)

    }


}

class Shield {
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }

    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)

        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

        this.teleportShield(duration)
    }

    teleportShield(duration) {

        const shield = this.player.dimension.spawnEntity("mf_mg:shield", this.player.location)
        shield.setProperty("mf_mg:element", this.element)
        shield.setProperty("mf_mg:rarity", RARITIES.indexOf(this.rarity) + 1)

        this.player.addEffect("slowness", 20 * duration, { amplifier: 2, showParticles: false })
        this.player.addTag("mf_mg:shield_owner")

        switch (this.element) {
            case "fire": this.player.addEffect("fire_resistance", 20 * duration, { amplifier: 1, showParticles: false }); break;
            case "water": this.player.addEffect("water_breathing", 20 * duration, { amplifier: 1, showParticles: false }); break;
            case "wind": this.player.addEffect("slow_falling", 20 * duration, { amplifier: 1, showParticles: false }); break;
            case "dark": this.player.addEffect("resistance", 20 * duration, { amplifier: 1, showParticles: false }); break;
        }

        let time = 0
        const Launcher = system.runInterval(() => {
            try {
                if (!shield || time > (duration * 20)) {
                    this.player.removeTag("mf_mg:shield_owner")
                    shield.remove()
                    system.clearRun(Launcher);
                    return;
                }
            } catch {
                this.player.removeTag("mf_mg:shield_owner")
                shield.remove()
                system.clearRun(Launcher);
                return;

            }

            // console.warn(this.player.location.x - shield.location.x)
            // shield.setProperty("mf_mg:x_dist", 1 * ((this.player.location.x + (this.player.getVelocity().x * 3)) - shield.location.x))
            // shield.setProperty("mf_mg:y_dist", 1 * ((this.player.location.y + (this.player.getVelocity().y * 3)) - shield.location.y))
            // shield.setProperty("mf_mg:z_dist", -1 * ((this.player.location.z + (this.player.getVelocity().z * 3)) - shield.location.z))


            const shieldLoc = Vector.add(this.player.location, { x: 0, y: 0, z: 0 })
            shield.teleport(shieldLoc)

            time++

        }, 1)

    }


}

class Afterlife { // summon skeleton on the player or where the projectile hit ??
    constructor(player, itemStack, element, rarity, ability) {
        this.player = player
        this.item = itemStack
        this.element = element
        this.rarity = rarity
        this.ability = ability
    }
    launchspell() {
        const { cd, duration, damage, range, effects } = getAbilityInfo(this.ability, this.element, this.rarity)

        this.player.addTag("mf_mg:skeleton_summoner")

        const skeletonId = `mf_mg:skeleton_${this.element}_${["angry", "terrified", "sad", "normal"][Math.floor(Math.random() * 4)]}`
        new Cooldown(this.player, this.ability, this.element).startCooldown(cd)

        const positions = getPositionsInCircle(4, this.player.location, 2)
        const skeletonsToSpawn = range;
        const validPositions = [];

        for (const pos of positions) {
            const block = this.player.dimension.getBlock(pos);

            if (block.isAir && !block.below().isAir) {
                validPositions.push(pos);
            }
        }

        if (validPositions.length === 0) {
            //console.warn("no valid blocks");
        } else {
            let spawnedSkeletons = 0;

            while (spawnedSkeletons < skeletonsToSpawn) {

                const spawnPos = validPositions[Math.floor(Math.random() * validPositions.length)];

                const skeletonEntity = this.player.dimension.spawnEntity(skeletonId, spawnPos);
                skeletonEntity.setDynamicProperty("mf_mg:skeleton_element", this.element);
                skeletonEntity.setDynamicProperty("mf_mg:skeleton_owner", this.player.name);
                skeletonEntity.setDynamicProperty("mf_mg:skeleton_effects", effects ? JSON.stringify(effects) : null)
                skeletonEntity.addTag("mf_mg:is_skeleton_safe")
                system.runTimeout(() => {
                    try {
                        skeletonEntity.applyDamage(10)
                    } catch { }
                }, 20 * 20)

                spawnedSkeletons++;
            }
        }





    }
}






world.afterEvents.projectileHitBlock.subscribe((result) => {
    const { projectile, source: player, location, dimension, hitVector } = result
    const block = result.getBlockHit().block
    const face = result.getBlockHit().face
    const faceLoc = result.getBlockHit().faceLocation

    try {
        if (!projectile.typeId.startsWith("mf_mg")) return
        if (projectile.hasTag("mf_mg:projectile_deactivated")) return

        const element = projectile.getProperty("mf_mg:element")
        const rarity = projectile.getProperty("mf_mg:rarity")
        const ability = projectile.getDynamicProperty("mf_mg:projectile_ability")
        const damage = projectile.getDynamicProperty("mf_mg:projectile_damage")
        const effects = JSON.parse(projectile.getDynamicProperty("mf_mg:projectile_effects"))
        const duration = projectile.getDynamicProperty("mf_mg:projectile_duration")
        const range = projectile.getDynamicProperty("mf_mg:projectile_range")

        projectile.addTag("mf_mg:projectile_deactivated")

        if (ability === "geyser") {
            //GEYSER
            if (face == Direction.Up) {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_geyser", location);
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);
                impactEntity.setDynamicProperty("mf_mg:projectile_damage", damage);
                impactEntity.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null);

            } else {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);

                for (let hitEntity of dimension.getEntities({ location: location, maxDistance: 2 })) {
                    if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton') || hitEntity.typeId == "mf_mg:trader")) {
                        damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);
                    }
                }
            }


        } else if (ability === "balling") {
            //BALLING
            const bounce = projectile.getDynamicProperty("mf_mg:projectile_bounce");
            if (bounce > 0 && !projectile.hasTag("mf_mg:projectile_already_bounced")) {
                //   console.warn(hitVector.x);
                const spawnLoc = Vector.add(block.location, Vector.multiply(faceLoc, 1.005));

                const newProjectile = dimension.spawnEntity("mf_mg:projectile_trail", spawnLoc);
                newProjectile.setProperty("mf_mg:element", element);
                newProjectile.setProperty("mf_mg:rarity", rarity);
                newProjectile.setDynamicProperty("mf_mg:projectile_ability", ability);
                newProjectile.setDynamicProperty("mf_mg:projectile_damage", damage);
                newProjectile.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null);
                newProjectile.setDynamicProperty("mf_mg:projectile_duration", duration);
                newProjectile.setDynamicProperty("mf_mg:projectile_range", range);
                newProjectile.setDynamicProperty("mf_mg:projectile_bounce", bounce - 1);

                const velocity = { x: projectile.getVelocity().x * (4 - bounce), y: hitVector.y, z: projectile.getVelocity().z * (4 - bounce) };
                const impulseVector = Vector.multiply(velocity, new Vector(directionMultiplications[face])).multiply(0.5);
                //  console.warn(bounce);
                const p = newProjectile.getComponent("projectile");
                p.gravity = 0.015;
                p.airInertia = 0.99;

                p.shoot(impulseVector);
                p.gravity = p.gravity + 0.001;

                projectile.addTag("mf_mg:projectile_already_bounced");
            }

            const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
            impactEntity.setProperty("mf_mg:element", element);
            impactEntity.setProperty("mf_mg:rarity", rarity);

            for (let hitEntity of dimension.getEntities({ location: location, maxDistance: 2 })) {
                if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton') || hitEntity.typeId == "mf_mg:trader")) {
                    damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);
                }
            }

        } else if (ability === "fury") {
            //FURY

            const bounce = projectile.getDynamicProperty("mf_mg:projectile_bounce");
            if (bounce > 0 && !projectile.hasTag("mf_mg:projectile_already_bounced")) {
                //   console.warn(hitVector.x);
                const spawnLoc = Vector.add(block.location, Vector.multiply(faceLoc, 1.005));

                const newProjectile = dimension.spawnEntity("mf_mg:projectile_trail", spawnLoc);
                newProjectile.setProperty("mf_mg:element", element);
                newProjectile.setProperty("mf_mg:rarity", rarity);
                newProjectile.setDynamicProperty("mf_mg:projectile_ability", ability);
                newProjectile.setDynamicProperty("mf_mg:projectile_damage", damage);
                newProjectile.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null);
                newProjectile.setDynamicProperty("mf_mg:projectile_duration", duration);
                newProjectile.setDynamicProperty("mf_mg:projectile_range", range);
                newProjectile.setDynamicProperty("mf_mg:projectile_bounce", bounce - 1);

                const velocity = { x: projectile.getVelocity().x * (4 - bounce), y: hitVector.y, z: projectile.getVelocity().z * (4 - bounce) };
                const impulseVector = Vector.multiply(velocity, new Vector(directionMultiplications[face])).multiply(0.5);
                //  console.warn(bounce);
                const p = newProjectile.getComponent("projectile");
                p.gravity = 0.015;
                p.airInertia = 0.99;

                p.shoot(impulseVector);
                p.gravity = p.gravity + 0.001;

                projectile.addTag("mf_mg:projectile_already_bounced");
            }

            if (face == Direction.Up) {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_geyser", location);
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);
                impactEntity.setDynamicProperty("mf_mg:projectile_damage", damage);
                impactEntity.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null);

            } else {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);

                for (let hitEntity of dimension.getEntities({ location: location, maxDistance: 2 })) {
                    if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton') || hitEntity.typeId == "mf_mg:trader")) {
                        damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);
                    }
                }
            }


        } else if (ability === "rifle") {
            //RIFLE
            const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
            impactEntity.setProperty("mf_mg:element", element);
            impactEntity.setProperty("mf_mg:rarity", rarity);

            for (const hitEntity of dimension.getEntities({ location: location, maxDistance: 2 })) {
                if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton') || hitEntity.typeId == "mf_mg:trader")) {
                    damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);
                }
            }

        } else if (ability === "wall") {
            //WALL
            system.runTimeout(() => {
                try {
                    const wallEntity = dimension.spawnEntity("mf_mg:barrier", location);
                    wallEntity.setProperty("mf_mg:element", element);
                    wallEntity.setProperty("mf_mg:rarity", rarity);
                    wallEntity.setDynamicProperty("mf_mg:wall_effects", effects ? JSON.stringify(effects) : null);
                } catch { }

            }, 5)

        }


    } catch (e) {
        //  console.error(e + " (can't acces projectile)")
    }




    system.runTimeout(() => {
        try {
            if (!projectile.hasTag("mf_mg:projectile_stay"))
                projectile.remove()
        } catch { }

    }, 5)
})





world.afterEvents.projectileHitEntity.subscribe((result) => {
    const { projectile, source: player, location, dimension, hitVector } = result
    const hitEntity = result.getEntityHit().entity


    try {

        if (!projectile.typeId.startsWith("mf_mg")) return
        if (projectile.hasTag("mf_mg:projectile_deactivated")) return
        if ((hitEntity.typeId.startsWith("mf_mg") && !hitEntity.typeId.includes('skeleton') && !hitEntity.typeId == "mf_mg:trader")) return;

        const element = projectile.getProperty("mf_mg:element")
        const rarity = projectile.getProperty("mf_mg:rarity")
        const ability = projectile.getDynamicProperty("mf_mg:projectile_ability")
        const damage = projectile.getDynamicProperty("mf_mg:projectile_damage")
        const effects = JSON.parse(projectile.getDynamicProperty("mf_mg:projectile_effects"))
        const duration = projectile.getDynamicProperty("mf_mg:projectile_duration")
        const range = projectile.getDynamicProperty("mf_mg:projectile_range")

        projectile.addTag("mf_mg:projectile_deactivated")


        if (ability === "geyser") {
            if (hitEntity.isOnGround) {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_geyser", { x: location.x, y: hitEntity.location.y, z: location.z });
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);
                impactEntity.setDynamicProperty("mf_mg:projectile_damage", damage);
                impactEntity.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null);

            } else {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);

                damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);
            }


        } else if (ability === "balling") {
            const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
            impactEntity.setProperty("mf_mg:element", element);
            impactEntity.setProperty("mf_mg:rarity", rarity);

            damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);

        } else if (ability === "fury") {
            if (hitEntity.isOnGround) {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_geyser", { x: location.x, y: hitEntity.location.y, z: location.z });
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);
                impactEntity.setDynamicProperty("mf_mg:projectile_damage", damage);
                impactEntity.setDynamicProperty("mf_mg:projectile_effects", effects ? JSON.stringify(effects) : null);

            } else {
                const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
                impactEntity.setProperty("mf_mg:element", element);
                impactEntity.setProperty("mf_mg:rarity", rarity);

                damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);
            }

        } else if (ability === "rifle") {
            const impactEntity = dimension.spawnEntity("mf_mg:projectile_impact", location);
            impactEntity.setProperty("mf_mg:element", element);
            impactEntity.setProperty("mf_mg:rarity", rarity);

            damageAndEffect(hitEntity, damage, effects, 3, 1, projectile);

        } else if (ability === "wall") {
            if (hitEntity.isOnGround) {
                system.runTimeout(() => {
                    try {
                        const wallEntity = dimension.spawnEntity("mf_mg:barrier", hitEntity.location);
                        wallEntity.setProperty("mf_mg:element", element);
                        wallEntity.setProperty("mf_mg:rarity", rarity);
                    } catch { }

                }, 5)

            }

        }

    } catch (e) {
        //  console.error(e + " (can't acces projectile)")
    }




    system.runTimeout(() => {
        try {
            projectile.remove()
        } catch { }

    }, 5)
})





function damageAndEffect(entity, damage, effects, duration = 3, amplifier = 1, projectile = null) {
    entity.applyDamage(damage)

    try {
        for (const effect of effects) {
            if (effect == "burning") {
                entity.setOnFire(duration, false)

            } else if (effect == "root") {
                // root effect

            } else if (effect == "knockback") {
                if (projectile) {
                    entity.applyKnockback(projectile.getViewDirection().x, projectile.getViewDirection().z, 1.3, 0.5)
                }

            } else if (effect == "lumus") {
                // spawn lumus entity for Duration second (despawn via timer event)

            } else if (effect !== "lifesteal" && effect !== "thorn") {
                entity.addEffect(effect, duration * 20, { amplifier: amplifier, showParticle: false })
            }


        }
    } catch { }
}


function getSideOffsetViewDirection(player, angleDegrees) {
    const viewDir = player.getViewDirection();
    const angleRadians = (angleDegrees * Math.PI) / 180;

    const x = viewDir.x;
    const y = viewDir.y;
    const z = viewDir.z;

    const horizontalAngle = Math.atan2(z, x);
    const newHorizontalAngle = horizontalAngle + angleRadians;

    const newX = Math.cos(newHorizontalAngle);
    const newZ = Math.sin(newHorizontalAngle);

    return {
        x: newX,
        y: y,
        z: newZ,
    };
}



const directionMultiplications = {
    [Direction.Down]: { x: 1, y: -1, z: 1 },
    [Direction.Up]: { x: 1, y: -1, z: 1 },

    [Direction.West]: { x: -1, y: 1, z: 1 },
    [Direction.East]: { x: -1, y: 1, z: 1 },

    [Direction.North]: { x: 1, y: 1, z: -1 },
    [Direction.South]: { x: 1, y: 1, z: -1 },
}


function clamp(value, min, max) {
    return Math.max(Math.min(min, value), max)
}








system.afterEvents.scriptEventReceive.subscribe((data) => {
    const { id, sourceEntity, message } = data;

    if (id === "mf_mg:geyser_explode") {
        if (!sourceEntity) return;

        const damage = sourceEntity.getDynamicProperty("mf_mg:projectile_damage")
        const effects = JSON.parse(sourceEntity.getDynamicProperty("mf_mg:projectile_effects"))

        for (let hitEntity of sourceEntity.dimension.getEntities({ location: sourceEntity.location, maxDistance: 2 })) {
            if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton')) || hitEntity.typeId == "mf_mg:trader" && hitEntity.location.y >= sourceEntity.location.y) {
                try {
                    hitEntity.applyImpulse({ x: 0, y: 1.5, z: 0 })
                } catch {
                    try {
                        hitEntity.applyKnockback(0, 0, 0, 1.4)
                    } catch { }
                }

                system.runTimeout(() => {
                    try {
                        damageAndEffect(hitEntity, damage, effects, 3, 1);
                    } catch { }
                }, 10)

            }
        }

        system.runTimeout(() => {
            try {
                sourceEntity.remove()
            } catch { }
        }, 20)
    }

    if (id === "mf_mg:entity_trail_explode") {
        if (!sourceEntity) return;

        const element = sourceEntity.getProperty("mf_mg:element")
        const rarity = sourceEntity.getProperty("mf_mg:rarity")
        const ability = sourceEntity.getDynamicProperty("mf_mg:projectile_ability")
        const damage = sourceEntity.getDynamicProperty("mf_mg:projectile_damage")
        const effects = JSON.parse(sourceEntity.getDynamicProperty("mf_mg:projectile_effects") ?? '["none"]')
        const duration = sourceEntity.getDynamicProperty("mf_mg:projectile_duration")
        const range = sourceEntity.getDynamicProperty("mf_mg:projectile_range")
        const ownerId = sourceEntity.getDynamicProperty("mf_mg:projectile_owner")


        if (ability == "lumus") {

            const lumusEntity = sourceEntity.dimension.spawnEntity("mf_mg:projectile_lumus", sourceEntity.location)
            lumusEntity.setProperty("mf_mg:element", element)
            lumusEntity.setProperty("mf_mg:rarity", rarity)
            lumusEntity.setDynamicProperty("mf_mg:projectile_duration", duration)
            lumusEntity.setDynamicProperty("mf_mg:projectile_range", range)
            lumusEntity.setDynamicProperty("mf_mg:projectile_owner", ownerId)

            if (element == "light") {

                lumusEntity.runCommand(`fill ~-7 ~-7 ~-7 ~7 ~7 ~7 light_block_15 keep`)

            }

            new Lumus(null, null, element, rarity, ability).lumusExplode(lumusEntity, range, duration)


        }

        try {
            sourceEntity.remove()
        } catch { }

    }

    if (id === "mf_mg:remove_lumus_light") {
        if (!sourceEntity) return;

        try {
            sourceEntity.runCommand(`fill ~-8 ~-8 ~-8 ~8 ~8 ~8 air replace light_block_15`)
            sourceEntity.remove()
        } catch { }

    }

    if (id === "mf_mg:wall_kb") {
        if (!sourceEntity) return;

        const element = sourceEntity.getProperty("mf_mg:element")
        const rarity = sourceEntity.getProperty("mf_mg:rarity")
        const effects = JSON.parse(sourceEntity.getDynamicProperty("mf_mg:wall_effects") ?? '["none"]')

        try {
            for (const hitEntity of sourceEntity.dimension.getEntities({ location: sourceEntity.location, maxDistance: 2.5 })) {
                if ((!hitEntity.typeId.startsWith("mf_mg") || hitEntity.typeId.includes('skeleton')) || hitEntity.typeId == "mf_mg:trader" && hitEntity.location.y >= sourceEntity.location.y) {

                    const directionVector = {
                        x: hitEntity.location.x - sourceEntity.location.x,
                        y: hitEntity.location.y - sourceEntity.location.y,
                        z: hitEntity.location.z - sourceEntity.location.z
                    };

                    const magnitude = Math.sqrt(directionVector.x ** 2 + directionVector.y ** 2 + directionVector.z ** 2);
                    const normalizedVector = {
                        x: directionVector.x / magnitude,
                        y: directionVector.y / magnitude,
                        z: directionVector.z / magnitude
                    };

                    const impulseStrength = 0.3 + (0.5 * rarity);

                    if (hitEntity.typeId === "minecraft:player") {

                        hitEntity.applyKnockback(
                            normalizedVector.x,
                            normalizedVector.z,
                            impulseStrength * 2,
                            (impulseStrength * 0.8)
                        );
                    } else {

                        hitEntity.applyImpulse({
                            x: normalizedVector.x * impulseStrength,
                            y: impulseStrength / 2,
                            z: normalizedVector.z * impulseStrength
                        });
                    }

                    damageAndEffect(hitEntity, 0, effects, 3, 1)

                }
            }
        } catch { }

        system.runTimeout(() => {
            sourceEntity.setProperty("mf_mg:remove", true)
            system.runTimeout(() => {
                sourceEntity.remove()
            }, 0.4 * 20)
        }, 40)

    }

});



function lifestealHandler(player, entity, value, doDamage) {
    const playerHealth = player.getComponent("minecraft:health")
    const entityHealth = player.getComponent("minecraft:health")

    if (playerHealth.currentValue >= playerHealth.effectiveMax) return

    if (entityHealth.currentValue < value) {
        value = entityHealth.currentValue
    }

    playerHealth.setCurrentValue((playerHealth.currentValue + value) >= playerHealth.effectiveMax ? playerHealth.effectiveMax : playerHealth.currentValue + value)

    if (doDamage) {
        entity.applyDamage(value)
    }

}



function getPositionsInCircle(radius, center, distToCenter) {
    const { x, y, z } = center;
    const positions = [];

    for (let dx = -radius; dx <= radius; dx++) {
        for (let dz = -radius; dz <= radius; dz++) {
            const distanceSquared = dx * dx + dz * dz;

            if (distanceSquared <= radius * radius) {
                const loc = { x: x + dx, y, z: z + dz }
                if (Vector.distanceBetween(loc, center) > distToCenter)
                    positions.push(loc);
            }
        }
    }
    return positions;
}




world.afterEvents.entityHitEntity.subscribe((e) => {
    const { damagingEntity, hitEntity } = e

    if (!hitEntity) return

    if ((damagingEntity.typeId.startsWith("mf_mg") && damagingEntity.typeId.includes("skeleton")) && !hitEntity.typeId.startsWith("mf_mg")) {

        const element = damagingEntity.getDynamicProperty("mf_mg:skeleton_element")
        const effects = JSON.parse(damagingEntity.getDynamicProperty("mf_mg:skeleton_effects"))
        const ownerName = damagingEntity.getDynamicProperty("mf_mg:skeleton_owner")

        if (element == "dark") {
            const owner = damagingEntity.dimension.getEntities({ name: ownerName, type: "minecraft:player" })[0]
            lifestealHandler(owner, hitEntity, 1, false)
            damageAndEffect(hitEntity, 0, effects, 2, 1, damagingEntity)
        } else {
            damageAndEffect(hitEntity, 0, effects, 2, 1, damagingEntity)

        }
    }

})

world.afterEvents.entityHurt.subscribe((e) => {
    const damagingEntity = e.damageSource.damagingEntity
    const hitEntity = e.hurtEntity
    const damage = e.damage

    if (hitEntity.typeId == "mf_mg:shield" && !damagingEntity.hasTag("mf_mg:shield_owner")) {
        const element = hitEntity.getProperty("mf_mg:element")
        if (element == "dark") {
            damagingEntity.addEffect("wither", 3)
        } else if (element == "fire") {
            damagingEntity.setOnFire(3, false)
        }

        damagingEntity.applyDamage(damage * 2)
    }

})





function isEntityInPlayerCursor(player, entity, hitboxSize) {

    const viewDir = player.getViewDirection();
    const playerLoc = player.getHeadLocation();
    const entityLoc = entity.location;

    const minBounds = {
        x: entityLoc.x - hitboxSize.l / 2,
        y: entityLoc.y,
        z: entityLoc.z - hitboxSize.l / 2,
    };
    const maxBounds = {
        x: entityLoc.x + hitboxSize.l / 2,
        y: entityLoc.y + hitboxSize.h,
        z: entityLoc.z + hitboxSize.l / 2,
    };

    const tMin = (minBounds.x - playerLoc.x) / viewDir.x;
    const tMax = (maxBounds.x - playerLoc.x) / viewDir.x;

    let t1 = Math.min(tMin, tMax);
    let t2 = Math.max(tMin, tMax);

    const tyMin = (minBounds.y - playerLoc.y) / viewDir.y;
    const tyMax = (maxBounds.y - playerLoc.y) / viewDir.y;

    t1 = Math.max(t1, Math.min(tyMin, tyMax));
    t2 = Math.min(t2, Math.max(tyMin, tyMax));

    const tzMin = (minBounds.z - playerLoc.z) / viewDir.z;
    const tzMax = (maxBounds.z - playerLoc.z) / viewDir.z;

    t1 = Math.max(t1, Math.min(tzMin, tzMax));
    t2 = Math.min(t2, Math.max(tzMin, tzMax));

    return t1 <= t2 && t1 > 0;
}


function getBlockCenter(block) {
    return { x: block.location.x + 0.5, y: block.location.y + 0.5, z: block.location.z + 0.5 }
}





const Abilities = [
    ""


]




world.afterEvents.playerSpawn.subscribe((e) => {
    const { initialSpawn, player } = e

    const abilities = [...new Set(ABILITY_INFO.map(info => info.ability))];
    const elements = [...new Set(ABILITY_INFO.map(info => info.element))];

    for (const ability of abilities) {
        for (const element of elements) {
            player.setDynamicProperty(`mf_mg:weapon_cooldown:${ability}:${element}`, 0);
        }
    }

    player.runCommand(`inputpermission set @s movement enabled`)


    if (!initialSpawn) return

    const projectiles = player.dimension.getEntities()

    const PROJECTILE_IDS = [
        "mf_mg:barrier",
        "mf_mg:blaster_entity",
        "mf_mg:entity_trail",
        "mf_mg:lifesteal_entity",
        "mf_mg:projectile_geyser",
        "mf_mg:projectile_impact",
        "mf_mg:projectile_lumus",
        "mf_mg:projectile_power_ring",
        "mf_mg:projectile_tornado",
        "mf_mg:projectile_trail",
        "mf_mg:ring_mob",
        "mf_mg:shield"
    ]

    for (const projectile of projectiles) {
        if (PROJECTILE_IDS.includes(projectile.typeId) || (projectile.typeId.includes("skeleton") && projectile.typeId.startsWith("mf_mg:"))) {
            projectile.remove()
        }
    }



})