import * as mc from "@minecraft/server";
import * as utils from "../../utils/index";
export class AttackComponent extends utils.ActorComponent {
    constructor() {
        super(...arguments);
        this.eventBinding = this.onEventRecieved.bind(this);
        this.projectileBinding = this.onVanillaProjectileDamage.bind(this);
        this.nextHitTime = 0;
    }
    onComponentAdded() {
        mc.system.afterEvents.scriptEventReceive.subscribe(this.eventBinding);
        if (this.options.type === "range") {
            mc.world.afterEvents.projectileHitEntity.subscribe(this.projectileBinding);
        }
    }
    onComponentRemoved() {
        mc.system.afterEvents.scriptEventReceive.unsubscribe(this.eventBinding);
        mc.world.afterEvents.projectileHitEntity.unsubscribe(this.projectileBinding);
    }
    onEventRecieved(event) {
        var _a, _b;
        if (mc.system.currentTick < this.nextHitTime)
            return;
        if (event.id === utils.addName("attack") && event.message === "attacker" && ((_a = event.sourceEntity) === null || _a === void 0 ? void 0 : _a.id) === ((_b = this.actor) === null || _b === void 0 ? void 0 : _b.id)) {
            const subScript = (subEvent) => {
                if (subEvent.id === utils.addName("attack") && subEvent.message === "victim" && subEvent.sourceEntity) {
                    mc.system.afterEvents.scriptEventReceive.unsubscribe(subScript);
                    this.performAttack(new utils.SafetyCheckedEntity(subEvent.sourceEntity));
                }
            };
            mc.system.afterEvents.scriptEventReceive.subscribe(subScript);
            utils.waitForEvent(1, () => mc.system.afterEvents.scriptEventReceive.unsubscribe(subScript));
        }
    }
    performAttack(victim) {
        if (this.actor.isValid() && victim.isValid()) {
            this.nextHitTime = mc.system.currentTick + ((this.options.duration.endTime * 20) + (this.options.cooldown * 20));
            this.actor.playAnimation(`animation.${utils.GlobalNamespace.NamespaceUnformatted}.generic.attack`);
            this.actor.raiseEvent("start_attack");
            utils.waitForEvent((this.options.duration.dmgTime * 20), () => {
                if (this.actor.isValid() && victim.isValid()) {
                    if (this.options.type === "melee") {
                        this.meleeAttack(victim, this.options);
                    }
                    else {
                        this.rangeAttack(victim, this.options);
                    }
                }
            });
            utils.waitForEvent((this.options.duration.endTime * 20), () => {
                // End animation by setting `v.attack_time = 0` in the RP
                this.actor.raiseEvent("finished_attack");
            });
        }
    }
    meleeAttack(victim, options) {
        var _a;
        const damage = typeof options.damage === "number" ? options.damage : utils.getRandomInteger(options.damage[0], options.damage[1]);
        (_a = options.onHit) === null || _a === void 0 ? void 0 : _a.call(options, victim);
        victim.applyDamage(damage, { cause: mc.EntityDamageCause.entityAttack, damagingEntity: this.actor.UnsafeEntity });
        if (victim.typeId === "minecraft:ender_dragon") {
            victim.applyDamage(damage, { cause: mc.EntityDamageCause.entityAttack, damagingEntity: utils.getAllPlayersSafe()[0].UnsafeEntity });
        }
    }
    rangeAttack(victim, options) {
        var _a, _b;
        const direction = victim.getHeadLocation().subtract(this.actor.getHeadLocation()).normalize();
        if (options.projectileTypeId.startsWith("minecraft:")) {
            const projectile = utils.spawnEntitySafe(this.actor.dimension, options.projectileTypeId, this.actor.getHeadLocation(), { facing: victim });
            const comp = projectile.getComponent("projectile");
            if (comp) {
                comp.owner = this.actor.UnsafeEntity;
                comp.shoot(direction.multiply((_b = (_a = options.projectileOptions) === null || _a === void 0 ? void 0 : _a.power) !== null && _b !== void 0 ? _b : 1));
            }
        }
        else {
            new utils.Projectile(this.actor.getHeadLocation(), this.actor.dimension, options.projectileTypeId, direction, this.actor, options.projectileOptions);
        }
    }
    onVanillaProjectileDamage(event) {
        var _a, _b, _c, _d, _e, _f;
        if (((_a = event.source) === null || _a === void 0 ? void 0 : _a.id) === this.actor.id) {
            const options = this.options;
            const entity = event.getEntityHit().entity;
            const filters = (_c = (_b = options.projectileOptions) === null || _b === void 0 ? void 0 : _b.on_hit) === null || _c === void 0 ? void 0 : _c.filters;
            if (!(entity === null || entity === void 0 ? void 0 : entity.isValid()) || entity.matches({ gameMode: mc.GameMode.creative }) || (filters && !entity.matches(filters)))
                return;
            let damage = (_f = (_e = (_d = options.projectileOptions) === null || _d === void 0 ? void 0 : _d.on_hit) === null || _e === void 0 ? void 0 : _e.damage) !== null && _f !== void 0 ? _f : 1;
            damage = typeof damage === "number" ? damage : utils.getRandomInteger(damage[0], damage[1]);
            const victim = new utils.SafetyCheckedEntity(entity);
            const health = victim.Health;
            if (health) {
                if (victim.matches({ type: "minecraft:wither" }) && health.currentValue <= health.effectiveMax / 2)
                    return; // Don't do projectile damage in phase 2
                if (victim.matches({ type: "minecraft:ender_dragon" }))
                    damage = damage * 0.25; // nerfed dragon damage
                health.setCurrentValue(health.currentValue - damage);
            }
            if (victim.isValid() && victim.matches({ type: "minecraft:ender_dragon" }) && event.projectile.isValid()) {
                victim.applyDamage(0.1, { cause: mc.EntityDamageCause.blockExplosion }); // Triggers red damage flash
                event.projectile.remove(); // Prevents damage every frame arrow is in contact
            }
        }
    }
}
