/*
hello dear add-on maker. want to use my code? sure. but it would probably help if you understand how it works instead of
just copy pasting it and changing ids...

the code shouldnt be too hard to understand. should be easy to configure for your own things

but if you're mostly just using the same code, would be nice if you credit

otherwise, have fun removing hit immunity :)

- JuliusScizzor
*/
import {
    world,
    Player,
    system,
    Vector,
    EntityDamageCause,
    EntityEquipmentInventoryComponent,
    EquipmentSlot,
    ItemStack,
    ItemEnchantsComponent,
    EnchantmentTypes,
    ItemDurabilityComponent,
    EntityHealthComponent,
    Entity,
  } from "@minecraft/server";
  



// return totalD + (100 - (totalD * 4)) * totalEpf * 0.04 * 0.0075;
const defPoints = {
    turtle_helmet: 2,
    leather_helmet: 1,
    golden_helmet: 2,
    chainmail_helmet: 2,
    iron_helmet: 2,
    diamond_helmet: 3,
    netherite_helmet: 3,
    "js:ntf_helmet": 2,
    "js:ntf_helmet_lv2": 3,
    "js:ntf_helmet_lv3": 3,
  
    leather_chestplate: 3,
    golden_chestplate: 5,
    chainmail_chestplate: 5,
    iron_chestplate: 6,
    diamond_chestplate: 8,
    netherite_chestplate: 8,
    "js:ntf_chestplate": 6,
    "js:ntf_chestplate_lv2": 8,
    "js:ntf_chestplate_lv3": 8,
  
    leather_leggings: 2,
    golden_leggings: 3,
    chainmail_leggings: 4,
    iron_leggings: 5,
    diamond_leggings: 6,
    netherite_leggings: 6,
    "js:ntf_leggings": 5,
    "js:ntf_leggings_lv2": 6,
    "js:ntf_leggings_lv3": 6,
  
    leather_boots: 1,
    golden_boots: 1,
    chainmail_boots: 1,
    iron_boots: 2,
    diamond_boots: 3,
    netherite_boots: 3,
    "js:ntf_boots": 2,
    "js:ntf_boots_lv2": 3,
    "js:ntf_boots_lv3": 3,
  };
  
export const projectileDef = {
    "js:ngl7_bullet": {
      damage: {
        entity: {
          falloff: [0, 0],
          damage: [0, 0],
        },
        armor: {
          falloff: [0, 0],
          damage: [0, 0],
        },
      },
      deathMessage: {
        messages: ["blown up", "kabloomied", "exploded", "obliterated"],
        weapon: "NG-L7",
      },
    },
    "js:mrx15_bullet": {
      damage: {
        entity: {
          falloff: [4, 15],
          damage: [7, 9],
        },
        armor: {
          falloff: [4, 15],
          damage: [0, 0],
        },
      },
      deathMessage: {
        messages: ["shot"],
        weapon: "MRX-15",
      },
    },
    "js:praedo_bullet": {
      damage: {
        entity: {
          falloff: [16, 32],
          damage: [12, 12],
        },
        armor: {
          falloff: [16, 32],
          damage: [0, 0],
        },
      },
      deathMessage: {
        messages: ["shot", "incinerated"],
        weapon: "Praedo",
      },
    },
    "js:praetereo_bullet": {
      damage: {
        entity: {
          falloff: [16, 32],
          damage: [40, 40],
        },
        armor: {
          falloff: [16, 32],
          damage: [0, 0],
        },
      },
      deathMessage: {
        messages: ["schrindonguloded", "obliterated", "shot", "shot"],
        weapon: "Praetereo",
      },
    },
    "js:project80_bullet": {
      damage: {
        entity: {
          falloff: [10, 32],
          damage: [12, 15],
        },
        armor: {
          falloff: [10, 32],
          damage: [0, 0],
        },
      },
      deathMessage: {
        messages: ["shot"],
        weapon: "Project-80",
      },
    },
    "js:xra5_bullet": {
      damage: {
        entity: {
          falloff: [24, 40],
          damage: [11, 12],
        },
        armor: {
          falloff: [24, 40],
          damage: [0, 0],
        },
      },
      deathMessage: {
        messages: ["shot"],
        weapon: "XR-A5",
      },
    },
  };
  
const ARMOUR_POINTS = {
  "js:scp682_growth6": 15,
  "js:scp682_growth5": 20,
  "js:scp682_growth2": 8,
}

const IGNORED_ENTITIES = {
  /*
  "js:mine": {
    "event": "event entity @s js:explode"
  },
  "js:hand_grenade": {
    "event": "event entity @s js:explode"
  },
  */
  "js:mrx15_ammo": {},
  "js:ngl7_ammo": {},
  "js:praetereo_ammo": {},
  "js:project80_ammo": {},
  "js:xra5_ammo": {},
  "js:crusher_smash": {},
  "js:scp682_growth5_attack": {},
  "js:scp682_growth5_smashing": {},
  "js:scp682_growth1_display": {},
  "js:scp682_growth2_display": {},
  "js:scp682_growth3_display": {},
  "js:scp682_growth4_display": {},
  "js:scp682_growth5_display": {},
  "js:scp682_growth6_display": {},
  "js:scp682_growth7_display": {},
  "js:crusher_display": {},
  "js:mrx15_display": {},
  "js:ngl7_display": {},
  "js:praedo_display": {},
  "js:praetereo_display": {},
  "js:xra5_display": {},
  "js:praedo_unlock": {},
  "js:praetereo_unlock": {}
}

export const MRX15_WEAK = {
  "js:scp682_growth3": {},
  "js:scp682_growth4": {},
}
export const XRA5_WEAK = {
  "js:scp682_growth1": {},
}
export const PROJECT80_WEAK = {
  "js:scp682_growth2": {},
}
export const PRAETEREO_WEAK = {
  "js:scp682_growth5": {},
  "js:scp682_growth6": {},
}
export const PRAEDO_WEAK = {
  "js:scp682_growth5": {},
  "js:scp682_growth6": {},
}


export const AFFECTED_MOBS = {
  "js:scp682_growth1": {},
  "js:scp682_growth2": {},
  "js:scp682_growth3": {},
  "js:scp682_growth4": {},
  "js:scp682_growth5": {},
  "js:scp682_growth6": {},
  "js:scp682_growth7": {},
  "js:scp682_tentacle": {},
  "js:scp682_tentacle": {},
  "js:scp682_tentacle_growth_nest": {},
  "js:scp682_tentacle_growth_nest_acid": {},
  "js:scp682": {},
}

const PROJECTILE_STATS = {
  "js:ngl7_bullet": {
    "defenceNegation": 0,
    "damageAmplifier": 0,
    "command": "",
    "effect": ""
  },
  "js:mrx15_bullet": {
    "defenceNegation": 0.5,
    "damageAmplifier": 1,
    "command": "function damage/hit_mrx15",
    "effect": {
      "id": "slowness", 
      "duration": 5 * 20,
      "amplifier": 0,
      "showParticles": true
    }
  },
  "js:xra5_bullet": {
    "defenceNegation": 0,
    "damageAmplifier": 1,
    "command": "function damage/hit_xra5",
    "effect": ""
  },
  "js:project80_bullet": {
    "defenceNegation": 0.25,
    "damageAmplifier": 1,
    "command": "function damage/hit_project80",
    "effect": ""
  },
  "js:praedo_bullet": {
    "defenceNegation": 1,
    "damageAmplifier": 1,
    "command": "function damage/hit_praedo",
    "effect": ""
  },
  "js:praetereo_bullet": {
    "defenceNegation": 0,
    "damageAmplifier": 1,
    "command": "function damage/hit_praetereo",
    "effect": ""
  },
}

  const unknownDefenseData = { dps: 0, epf: 0 };
  
  function calcEPF(lvl, modifier) {
    return Math.floor((6 + Math.pow(lvl, 2) * modifier) / 3);
  }
  
  function calcItem(item) {
    if (!item) return unknownDefenseData;
  
    const dps = defPoints[item.typeId.replace("minecraft:", "")];
    if (!dps) return unknownDefenseData;
  
    const enchs = item.getComponent("enchantments").enchantments;
    const prot = enchs.getEnchantment(EnchantmentTypes.get("protection"));
    const projProt = enchs.getEnchantment(EnchantmentTypes.get("projectile_protection"));
  
    let epf = 0;
    if (prot) {
      epf = calcEPF(prot.level, 0.75);
    } else if (projProt) {
      epf = calcEPF(projProt.level, 1.25);
    }
  
    return { dps, epf };
  }
  
  function getArmor(entity) {
    const equipment = entity.getComponent("equipment_inventory");
    if (!equipment) return [];
    return [
      equipment.getEquipmentSlot(EquipmentSlot.head),
      equipment.getEquipmentSlot(EquipmentSlot.chest),
      equipment.getEquipmentSlot(EquipmentSlot.legs),
      equipment.getEquipmentSlot(EquipmentSlot.feet),
    ].filter((v) => v.typeId);
  }
  

  function calcArmor(entity) {
    const armorPieces = getArmor(entity);
  
    let totalD = 0;
    let totalEpf = 0;
    for (const piece of armorPieces) {
      const itemCalc = calcItem(piece.getItem());
      totalD += itemCalc.dps;
      totalEpf += itemCalc.epf;
    }
    const armorTypeId = entity.typeId;
    if (armorTypeId in ARMOUR_POINTS) {
      totalD = ARMOUR_POINTS[armorTypeId];
    }

    totalD *= 0.04;
    if (totalD <= 0) return 0;
    return totalD + (100 - totalD * 4) * totalEpf * 0.04 * 0.0075;
  }
  
  function damageArmor(entity, dmg) {
    if (entity.hasTag("projectileArmourDamageFalse")) return;
    const armorPieces = getArmor(entity);
    for (const piece of armorPieces) {
      const item = piece.getItem();
      const dur = item.getComponent("durability");
      if (!dur) continue;
  
      const enchs = item.getComponent("enchantments")?.enchantments;
      const unbreak = enchs?.getEnchantment(EnchantmentTypes.get("unbreaking"))?.level ?? 0;
      if (Math.round(Math.random() * 100) + 1 > dur.getDamageChance(unbreak)) return;
  
      dur.damage = Math.min(Math.round(dur.damage + dmg), dur.maxDurability);
  
      if (dur.maxDurability == dur.damage) {
        piece.setItem();
        entity.runCommand("playsound random.break @a ~~~");
        world.playSound("random.break", entity.location, soundOptions)
      } else piece.setItem(item);
    }
  }
  
  function calcDamage(dmgDef, dist) {
    const [fmin, fmax] = dmgDef.falloff;
    const [dmin, dmax] = dmgDef.damage;
    let dmg = dmax;
    if (dist > fmin) {
      dmg = Math.round(Math.max(dmin, dmin + (dmax - dmin) * (1 - (dist - fmin) / (fmax - fmin))));
    }
    return dmg;
  }

/*
  world.afterEvents.entitySpawn.subscribe(({ entity }) => {
    const type = entity.typeId
    let projectileId
    if (type in projectileDef) {
      projectileId = type
*/


const LastHitThreshold = 300;
let lastProcessedTick = -1;

const callback = world.afterEvents.projectileHit.subscribe((ev) => {
        
        //world.afterEvents.projectileHit.unsubscribe(callback)
        const eHitI = ev.getEntityHit();
        const proj = ev.projectile;
        
      //  if (!eHitI || !(proj === entity)) return
      if (!eHitI || !proj) return

        const source = ev.source;
        const hitEntity = eHitI.entity;
       // const projId = projectileId
        const projId = proj.typeId ?? proj
        const projDef = projectileDef[projId];
      
        
        if (eHitI instanceof Player) {
          const gamemode = getGamemode(hitEntity);
          if (gamemode == "creative" || gamemode == "spectator") return;
        }
        if (!eHitI || hitEntity.hasTag("projectileDamageFalse") || hitEntity.hasTag("projectileShield")) return;
      
        if (!projDef) return;
      
        if (projId == "js:fire_extinguisher_bullet" && !hitEntity.hasTag("scp457")) {
          hitEntity.extinguishFire(true)
          return
        }
        if (projId == "js:flamethrower_bullet" && hitEntity.hasTag("scp457")) {
          hitEntity.extinguishFire(true)
          return
        }
      
        const entityTypeId = hitEntity.typeId;
        if (entityTypeId in IGNORED_ENTITIES) {
          const eventCommand = IGNORED_ENTITIES[entityTypeId]?.event;
          if (eventCommand) {
            hitEntity.runCommandAsync(eventCommand);
          }
          return;
        }
      
        if (hitEntity.hasTag("scp") && source.hasTag("scp")) return;
      
        const dist = Vector.subtract(source.location, ev.location).length();
      
        const dmgDefs = projDef.damage;
        const entDmg = calcDamage(dmgDefs.entity, dist);
        const armorDmg = calcDamage(dmgDefs.armor, dist);
      
        let finalDefencePoints = calcArmor(hitEntity);
      
        damageArmor(hitEntity, armorDmg);
      
        const sourceEquipment = (source.getComponent("equipment_inventory"));
        const sourceEquipmentSlot = sourceEquipment.getEquipmentSlot(EquipmentSlot.mainhand);
        const sourceItem = sourceEquipmentSlot.typeId ? sourceEquipmentSlot.getItem() : undefined;
        const sourceEnchs = (sourceItem?.getComponent("enchantments"))?.enchantments;
        const fireAspect = sourceEnchs?.getEnchantment(EnchantmentTypes.get("fire_aspect"))?.level ?? 0;
        const sharpness = sourceEnchs?.getEnchantment(EnchantmentTypes.get("sharpness"))?.level ?? 0;
      

        let damageAmplifier = 1
        let defenceNegation = 0;

        const projTypeId = proj?.typeId;
        if (projTypeId in PROJECTILE_STATS) {
          const projectileStats = PROJECTILE_STATS[projTypeId];
          defenceNegation = projectileStats.defenceNegation;
          damageAmplifier = projectileStats.damageAmplifier;
        }
        if (projTypeId == "js:mrx15_bullet" && entityTypeId in MRX15_WEAK) {
          defenceNegation = 1;
          damageAmplifier = 1.5;
          hitEntity.runCommand(`particle minecraft:critical_hit_emitter ~~~`)
        }
        if (projTypeId == "js:xra5_bullet" && entityTypeId in XRA5_WEAK) {
          defenceNegation = 1;
          damageAmplifier = 1.5;
          hitEntity.runCommand(`particle minecraft:critical_hit_emitter ~~~`)
        }
        if (projTypeId == "js:project80_bullet" && entityTypeId in PROJECT80_WEAK) {
          defenceNegation = 1;
          damageAmplifier = 2;
          hitEntity.runCommand(`particle minecraft:critical_hit_emitter ~~~`)
        }
        if (projTypeId == "js:praedo_bullet" && entityTypeId in PRAEDO_WEAK) {
          defenceNegation = 1;
          damageAmplifier = 1.5;
          hitEntity.runCommand(`particle minecraft:critical_hit_emitter ~~~`)
        }
        if (projTypeId == "js:praetereo_bullet" && entityTypeId in PRAETEREO_WEAK) {
          defenceNegation = 1;
          damageAmplifier = 1.5;
          hitEntity.runCommand(`particle minecraft:critical_hit_emitter ~~~`)
        }

        if (projTypeId != "js:xra5_bullet" && source.hasTag("has_luck_lv2")) {
          hitEntity.triggerEvent("js:luck_drop_ammo")
          //hitEntity.runCommand(`say luck2`)
        }
        if (projTypeId == "js:xra5_bullet" && !source.hasTag("has_luck")) {
          hitEntity.triggerEvent("js:drop_ammo")
          //hitEntity.runCommand(`say xra5 ammo`)
        }
        if (projTypeId == "js:xra5_bullet" && source.hasTag("has_luck")) {
          hitEntity.triggerEvent("js:drop_ammo2")
          //hitEntity.runCommand(`say xra5 ammo 2`)
        }
        if (hitEntity.typeId == "js:scp682_growth7") {
          hitEntity.triggerEvent("js:teleport_away")
        }

        if (!(hitEntity.getEffect("fire_resistance"))) {
          hitEntity.setOnFire(3 * fireAspect, true);
        }

       
        let resistanceDefence = 0
        if (hitEntity.getEffect("resistance")) {
          resistanceDefence = (0.1 * (hitEntity.getEffect("resistance").amplifier + 1));
        }

        if (projTypeId == "js:praedo_bullet" && hitEntity instanceof Player) {
          defenceNegation = 0.25;
          damageAmplifier = 1;
        }



        if (hitEntity instanceof Player) {
          defenceNegation = 0;
          damageAmplifier = 0.5;
        }

        if (projTypeId == "js:mrx15_bullet" && hitEntity instanceof Player) {
          defenceNegation = 0.5;
          damageAmplifier = 0.5;
        }
        

        if (hitEntity.hasTag("ultranightmare")) {
          damageAmplifier = 0.5;
        }
        const defenceNegationMultipler = 1 + sharpness * 0.05
        defenceNegation *= defenceNegationMultipler
        const finalDmg = (Math.max(entDmg * (1 - (finalDefencePoints - defenceNegation + resistanceDefence)), 1)) * damageAmplifier
      
        hitEntity.applyDamage(finalDmg, {
          cause: EntityDamageCause.suicide,
          damagingProjectile: proj,
        });
        
        if (source.hasTag("has_lifesteal")) {
          const sourceHp = source.getComponent("health")
          const lifesteal = finalDmg / 50
          sourceHp.setCurrentValue(sourceHp.currentValue + lifesteal)
          //source.runCommandAsync(`say ${lifesteal}`)
        }
        
      
        const hitCommand = PROJECTILE_STATS[projTypeId]?.command;
        const hitEffect = PROJECTILE_STATS[projTypeId]?.effect;
        if (hitCommand) {
          hitEntity.runCommandAsync(hitCommand);
        }
        if (hitEffect) {
          hitEntity.addEffect(hitEffect.id, hitEffect.duration, {amplifier: hitEffect.amplifier, showParticles: hitEffect.showParticles});
        }

        
        if (system.currentTick === lastProcessedTick) {
          return;
        }
        const hp = (hitEntity.getComponent("health"));
        if (hp.currentValue <= 0 && hitEntity instanceof Player && source instanceof Player) {
          source.runCommand(`scoreboard players add @s kills 1`);
          source.runCommand(`function game_system/ngl7_drop`);
          const killerHp = (source.getComponent("health"));
          killerHp.setCurrentValue(killerHp.currentValue + 10)
          lastProcessedTick = system.currentTick; 
        }
      
        

       // world.sendMessage(`EntityDMG: ${entDmg} ArmorDMG: ${armorDmg} FinalEntityDMG: ${finalDmg} FinalArmourDefencePoints: ${defenceNegation}  ${sharpness}`);
      

      });
      /*
   }
})
*/



