package cn.academy.ability.api; import cn.academy.ability.SkillDamageSource; import cn.academy.ability.api.cooldown.CooldownData; import cn.academy.ability.api.data.AbilityData; import cn.academy.ability.api.data.CPData; import cn.academy.ability.api.event.CalcEvent; import cn.academy.ability.api.event.ReflectEvent; import cn.academy.core.config.ACConfig; import cn.lambdalib.util.generic.MathUtils; import cn.lambdalib.util.mc.WorldUtils; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItemFrame; import net.minecraft.entity.item.EntityPainting; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import java.util.List; import java.util.function.Predicate; /** * A context for skill usage to abstract over common operations. */ public class AbilityContext { public static AbilityContext of(EntityPlayer player, Skill skill) { return new AbilityContext(player, skill); } public final EntityPlayer player; public final Skill skill; public final AbilityData aData; public final CPData cpData; public final CooldownData cdData; private AbilityContext(EntityPlayer p, Skill s) { player = p; skill = s; aData = AbilityData.get(player); cpData = CPData.get(player); cdData = CooldownData.of(player); } /** * Make the player fire the attack with the given skill. The damage will be re-calculated according to the damage * scale specified globally and skill-locally, and influenced by passive skills. * @param target The entity to attack * @param damage The amount of damage applied (raw) */ public void attack(Entity target, float damage) { damage = CalcEvent.calc(new CalcEvent.SkillAttack(player, skill, target, damage)); if (damage > 0 && (AbilityPipeline.canAttackPlayer() || (!(target instanceof EntityPlayer))) && canAttack(target)) { target.attackEntityFrom(new SkillDamageSource(player, skill), getFinalDamage(damage)); } } public void attackIgnoreArmor(Entity target, float damage) { damage = CalcEvent.calc(new CalcEvent.SkillAttack(player, skill, target, damage)); if (damage > 0 && (AbilityPipeline.canAttackPlayer() || (!(target instanceof EntityPlayer))) && canAttack(target)) { target.attackEntityFrom(new SkillDamageSource(player, skill).setDamageBypassesArmor(), getFinalDamage(damage)); } } public void attackReflect(Entity target, float damage, Runnable reflectCallback) { if (MinecraftForge.EVENT_BUS.post(new ReflectEvent(player, skill, target))) { reflectCallback.run(); } else { attack(target, damage); } } public boolean canAttack(Entity entity) { return canBreakBlock(entity.worldObj) || (!(entity instanceof EntityPainting) && !(entity instanceof EntityItemFrame)); } public void attackRange(double x, double y, double z, double range, float damage, Predicate<Entity> entitySelector) { List<Entity> list = WorldUtils.getEntities(player.worldObj, x, y, z, range, entitySelector); for(Entity ent : list) { double dist = MathUtils.distance(x, y, z, ent.posX, ent.posY, ent.posZ); float factor = 1 - MathUtils.clampf(0, 1, (float) (dist / range)); float appliedDamage = MathUtils.lerpf(0, damage, factor); attack(ent, appliedDamage); } } public boolean canConsumeCP(float cp) { return cpData.canPerform(cp); } public boolean consume(float overload, float cp) { return cpData.perform( getFinalConsO(overload), getFinalConsCP(cp)); } public void consumeWithForce(float overload, float cp) { cpData.performWithForce(overload, cp); } public float getSkillExp() { return aData.getSkillExp(skill); } public void addSkillExp(float amt) { aData.addSkillExp(skill, getFinalExpIncr(amt)); } public void setCooldown(int ticks) { cdData.set(skill, ticks); } public void setCooldownSub(int subID, int ticks) { cdData.setSub(skill, subID, ticks); } private float g_getDamageScale() { return (float) ACConfig.instance().getDouble( "ac.ability.calc_global.damage_scale" ); } public boolean canBreakBlock(World world, int x, int y, int z) { return skill.shouldDestroyBlocks() && AbilityPipeline.canBreakBlock(world, player, x, y, z); } public boolean canBreakBlock(World world) { return skill.shouldDestroyBlocks() && AbilityPipeline.canBreakBlock(world); } private float getFinalDamage(float damage) { return g_getDamageScale() * skill.getDamageScale() * damage; } private float getFinalExpIncr(float expincr) { return skill.getExpIncrSpeed() * expincr; } private float getFinalConsCP(float cp) { return skill.getCPConsumeSpeed() * cp; } private float getFinalConsO(float overload) { return skill.getOverloadConsumeSpeed() * overload; } }