Search in sources :

Example 1 with ParryEvent

use of jackiecrazy.wardance.event.ParryEvent in project WarDance by Jackiecrazy.

the class Afterimage method parry.

@Override
protected void parry(LivingEntity caster, ParryEvent procPoint, SkillData stats, LivingEntity target, STATE state) {
    if (!caster.isShiftKeyDown() || state == STATE.COOLING)
        return;
    final float cost = ((ParryEvent) procPoint).getPostureConsumption();
    SkillUtils.createCloud(caster.level, caster, caster.getX(), caster.getY(), caster.getZ(), cost, ParticleTypes.LARGE_SMOKE);
    for (LivingEntity e : caster.level.getLoadedEntitiesOfClass(LivingEntity.class, caster.getBoundingBox().inflate(cost))) {
        if (e.distanceToSqr(caster) > cost * cost) {
            e.addEffect(new EffectInstance(Effects.BLINDNESS, 100));
        }
    }
    caster.addEffect(new EffectInstance(Effects.INVISIBILITY, 20));
    markUsed(caster);
    ((ParryEvent) procPoint).setPostureConsumption(0);
}
Also used : LivingEntity(net.minecraft.entity.LivingEntity) ParryEvent(jackiecrazy.wardance.event.ParryEvent) EffectInstance(net.minecraft.potion.EffectInstance)

Example 2 with ParryEvent

use of jackiecrazy.wardance.event.ParryEvent in project WarDance by Jackiecrazy.

the class CombatHandler method parry.

// because compat with BHT...
@SubscribeEvent(priority = EventPriority.LOWEST)
public static void parry(final LivingAttackEvent e) {
    if (!e.getEntityLiving().level.isClientSide && e.getSource() != null && CombatUtils.isPhysicalAttack(e.getSource())) {
        LivingEntity uke = e.getEntityLiving();
        if (MovementUtils.hasInvFrames(uke)) {
            e.setCanceled(true);
        }
        ICombatCapability ukeCap = CombatData.getCap(uke);
        ItemStack attack = CombatUtils.getAttackingItemStack(e.getSource());
        if (CombatUtils.isMeleeAttack(e.getSource()) && e.getSource().getEntity() instanceof LivingEntity && attack != null && e.getAmount() > 0) {
            LivingEntity seme = (LivingEntity) e.getSource().getEntity();
            if (StealthConfig.inv)
                seme.removeEffect(Effects.INVISIBILITY);
            ICombatCapability semeCap = CombatData.getCap(seme);
            Hand attackingHand = semeCap.isOffhandAttack() ? Hand.OFF_HAND : Hand.MAIN_HAND;
            // hand bound or staggered, no attack
            if (semeCap.getStaggerTime() > 0 || semeCap.getHandBind(attackingHand) > 0) {
                e.setCanceled(true);
                return;
            }
            boolean sweeping = false;
            // capability handler
            seme.getMainHandItem().getCapability(CombatManipulator.CAP).ifPresent((i) -> i.attackStart(e.getSource(), seme, uke, seme.getMainHandItem(), e.getAmount()));
            // add stats if it's the first attack this tick and cooldown is sufficient
            if (semeCap.getSweepTick() != seme.tickCount) {
                // first hit of a potential sweep attack
                semeCap.addRank(0.1f);
                float might = CombatUtils.getAttackMight(seme, uke);
                semeCap.addMight(might);
                semeCap.setSweepTick(seme.tickCount);
            } else {
                // hitting twice in a sweep attack, disqualified from parry refund
                sweeping = true;
            }
            // blocking, reset posture cooldown without resetting combo cooldown, bypass parry
            if (uke.isBlocking()) {
                ukeCap.consumePosture(0);
                return;
            }
            // manual parry toggle
            // why does everyone want this feature...
            boolean failManualParry = CombatConfig.sneakParry > 0 && (ukeCap.getParryingTick() > uke.tickCount || ukeCap.getParryingTick() < uke.tickCount - CombatConfig.sneakParry);
            failManualParry |= CombatConfig.sneakParry < 0 && ukeCap.getParryingTick() == -1;
            failManualParry &= uke instanceof PlayerEntity;
            boolean canParry = GeneralUtils.isFacingEntity(uke, seme, 120);
            boolean useDeflect = (uke instanceof PlayerEntity || WarDance.rand.nextFloat() < CombatConfig.mobDeflectChance) && GeneralUtils.isFacingEntity(uke, seme, 120 + 2 * (int) GeneralUtils.getAttributeValueSafe(uke, WarAttributes.DEFLECTION.get())) && !GeneralUtils.isFacingEntity(uke, seme, 120) && !canParry;
            // staggered, no parry
            if (ukeCap.getStaggerTime() > 0) {
                downingHit = false;
                return;
            }
            // parry code start, grab attack multiplier
            float atkMult = CombatUtils.getPostureAtk(seme, seme, attackingHand, e.getAmount(), attack);
            // store atkMult at this stage for event
            float original = atkMult;
            downingHit = true;
            // stabby bonus
            StealthUtils.Awareness awareness = StealthUtils.getAwareness(seme, uke);
            atkMult *= CombatUtils.getDamageMultiplier(awareness, attack);
            // crit bonus
            if (e.getSource() instanceof CombatDamageSource && ((CombatDamageSource) e.getSource()).isCrit())
                atkMult *= ((CombatDamageSource) e.getSource()).getCritDamage();
            // grab defending stack
            ItemStack defend = null;
            Hand parryHand = null;
            if (canParry) {
                float posMod = 1337;
                boolean isShield = false;
                if (CombatUtils.canParry(uke, seme, uke.getOffhandItem(), atkMult)) {
                    defend = uke.getOffhandItem();
                    posMod = CombatUtils.getPostureDef(seme, uke, uke.getOffhandItem(), e.getAmount());
                    isShield = CombatUtils.isShield(uke, uke.getOffhandItem());
                    parryHand = Hand.OFF_HAND;
                }
                if (!isShield && CombatUtils.canParry(uke, seme, uke.getMainHandItem(), atkMult) && CombatUtils.getPostureDef(seme, uke, uke.getMainHandItem(), e.getAmount()) < posMod) {
                    defend = uke.getMainHandItem();
                    parryHand = Hand.MAIN_HAND;
                }
            }
            float defMult = CombatUtils.getPostureDef(seme, uke, defend, e.getAmount());
            // special mob parry overrides
            if (atkMult >= 0 && awareness != StealthUtils.Awareness.UNAWARE && CombatUtils.parryMap.containsKey(GeneralUtils.getResourceLocationFromEntity(uke))) {
                CombatUtils.MobInfo stats = CombatUtils.parryMap.get(GeneralUtils.getResourceLocationFromEntity(uke));
                if (WarDance.rand.nextFloat() < stats.chance) {
                    if (stats.mult < 0) {
                        // cannot parry
                        defend = null;
                        canParry = false;
                        defMult = (float) -stats.mult;
                    } else if (stats.omnidirectional || canParry) {
                        if (defMult > stats.mult) {
                            if (!canParry) {
                                parryHand = CombatUtils.getCooledAttackStrength(uke, Hand.MAIN_HAND, 0.5f) > CombatUtils.getCooledAttackStrength(uke, Hand.OFF_HAND, 0.5f) ? Hand.MAIN_HAND : Hand.OFF_HAND;
                            }
                            defend = ItemStack.EMPTY;
                            defMult = (float) Math.min(stats.mult, defMult);
                            canParry = true;
                        }
                    }
                }
            }
            // accounting for negative posture damage, used to mark an item as ignoring parries
            float finalPostureConsumption = Math.abs(atkMult * defMult);
            // updating this quickly, it's basically the above without crit and stab multipliers, which were necessary for calculating canParry so they couldn't be eliminated cleanly...
            float originalPostureConsumption = Math.abs(original * defMult);
            ParryEvent pe = new ParryEvent(uke, seme, ((canParry && defend != null) || useDeflect), attackingHand, attack, parryHand, defend, finalPostureConsumption, originalPostureConsumption, e.getAmount());
            if (failManualParry)
                pe.setResult(Event.Result.DENY);
            MinecraftForge.EVENT_BUS.post(pe);
            if (pe.isCanceled()) {
                e.setCanceled(true);
                return;
            }
            if (ukeCap.getStaggerTime() == 0) {
                // overflow posture
                float consumption = pe.getPostureConsumption();
                /*
                    barrier reduction if appropriate
                    this is so ugly oh my god
                     */
                if (pe.canParry() && !useDeflect && CombatUtils.isShield(uke, defend)) {
                    consumption -= ukeCap.consumeBarrier(consumption);
                }
                /*
                    end ugliness
                     */
                float knockback = ukeCap.consumePosture(seme, consumption);
                // no parries if stabby
                if (StealthConfig.ignore && awareness == StealthUtils.Awareness.UNAWARE)
                    return;
                if (pe.canParry()) {
                    e.setCanceled(true);
                    downingHit = false;
                    ukeCap.addRank(0);
                    if (useDeflect) {
                        // deflect
                        uke.level.playSound(null, uke.getX(), uke.getY(), uke.getZ(), SoundEvents.IRON_TRAPDOOR_OPEN, SoundCategory.PLAYERS, 0.75f + WarDance.rand.nextFloat() * 0.5f, (1 - (ukeCap.getPosture() / ukeCap.getMaxPosture())) + WarDance.rand.nextFloat() * 0.5f);
                        return;
                    }
                    // shield disabling
                    boolean disshield = false;
                    parryHand = uke.getOffhandItem() == defend ? Hand.OFF_HAND : Hand.MAIN_HAND;
                    // barrier has already been handled. Subsequent binding and cooldown are handled by the capability.
                    if (CombatUtils.isShield(uke, defend)) {
                        Tuple<Integer, Float> stat = CombatUtils.getShieldStats(defend);
                        if (attack.canDisableShield(defend, uke, seme)) {
                            // shield is disabled
                            if (uke instanceof PlayerEntity) {
                                ((PlayerEntity) uke).getCooldowns().addCooldown(defend.getItem(), stat.getA());
                            } else
                                ukeCap.setHandBind(parryHand, stat.getA());
                            disshield = true;
                        }
                    }
                    // knockback based on posture consumed
                    // this will return negative if the defmult is greater, and positive if the atkmult is greater. Larger abs val=larger difference
                    double kb = Math.sqrt(atkMult) - 0.18 - (1 / Math.max(defMult, 0.1));
                    // sigmoid curve again!
                    // this is the knockback to be applied to the defender
                    kb = 1d / (1d + Math.exp(-kb));
                    CombatUtils.knockBack(uke, seme, Math.min(uke instanceof PlayerEntity ? 1.6f : 1.3f, 0.2f + (pe.getPostureConsumption() + knockback) * (float) kb * (uke instanceof PlayerEntity ? 6 : 4) / ukeCap.getMaxPosture()), true, false);
                    kb = 1 - kb;
                    CombatUtils.knockBack(seme, uke, Math.min(uke instanceof PlayerEntity ? 1.6f : 1.3f, 0.1f + pe.getPostureConsumption() * (float) kb * (seme instanceof PlayerEntity ? 3 : 2) / semeCap.getMaxPosture()), true, false);
                    uke.level.playSound(null, uke.getX(), uke.getY(), uke.getZ(), disshield ? SoundEvents.SHIELD_BLOCK : SoundEvents.ANVIL_PLACE, SoundCategory.PLAYERS, 0.25f + WarDance.rand.nextFloat() * 0.5f, (1 - (ukeCap.getPosture() / ukeCap.getMaxPosture())) + WarDance.rand.nextFloat() * 0.5f);
                    // reset cooldown
                    if (defMult != 0) {
                        // shield time
                        // (posture consumption+1)*5 ticks of cooldown
                        int ticks = (int) ((consumption + 1) * 5);
                        // attack cooldown ticks
                        float cd = CombatUtils.getCooldownPeriod(uke, parryHand);
                        if (// if attack speed is lower, refund partial cooldown
                        cd > ticks)
                            CombatUtils.setHandCooldownDirect(uke, parryHand, ticks, true);
                        else
                            // otherwise bind hand
                            ukeCap.setHandBind(parryHand, (ticks - (int) cd));
                    }
                    if (sweeping) {
                        CombatUtils.setHandCooldown(seme, attackingHand, 0, true);
                    } else
                        CombatUtils.setHandCooldown(seme, attackingHand, (float) (1 - kb), true);
                    // sword on sword is 1.4, sword on shield is 1.12
                    if (defend != null) {
                        ItemStack finalDefend = defend;
                        defend.getCapability(CombatManipulator.CAP).ifPresent((i) -> i.onParry(seme, uke, finalDefend, e.getAmount()));
                        Hand other = uke.getMainHandItem() == defend ? Hand.OFF_HAND : Hand.MAIN_HAND;
                        ItemStack finalDefend1 = uke.getItemInHand(other);
                        finalDefend1.getCapability(CombatManipulator.CAP).ifPresent((i) -> i.onOtherHandParry(seme, uke, finalDefend1, e.getAmount()));
                    }
                }
            }
            if (!(seme instanceof PlayerEntity)) {
                semeCap.setHandBind(attackingHand, CombatUtils.getCooldownPeriod(seme, attackingHand) + 7);
            }
        }
        // shatter, at the rock bottom of the attack event, saving your protected butt.
        if (!uke.isBlocking() && !e.isCanceled()) {
            if (CombatUtils.isPhysicalAttack(e.getSource()) && StealthUtils.getAwareness(e.getSource().getDirectEntity() instanceof LivingEntity ? (LivingEntity) e.getSource().getDirectEntity() : null, uke) != StealthUtils.Awareness.UNAWARE) {
                if (CombatData.getCap(uke).consumeShatter(e.getAmount())) {
                    e.setCanceled(true);
                    uke.level.playSound(null, uke.getX(), uke.getY(), uke.getZ(), SoundEvents.GLASS_BREAK, SoundCategory.PLAYERS, 0.25f + WarDance.rand.nextFloat() * 0.5f, 0.75f + WarDance.rand.nextFloat() * 0.5f);
                }
            // otherwise the rest of the damage goes through and is handled later down the line anyway
            }
        }
    }
}
Also used : StealthUtils(jackiecrazy.wardance.utils.StealthUtils) CombatUtils(jackiecrazy.wardance.utils.CombatUtils) PlayerEntity(net.minecraft.entity.player.PlayerEntity) LivingEntity(net.minecraft.entity.LivingEntity) ICombatCapability(jackiecrazy.wardance.capability.resources.ICombatCapability) CombatDamageSource(jackiecrazy.wardance.api.CombatDamageSource) ParryEvent(jackiecrazy.wardance.event.ParryEvent) ProjectileParryEvent(jackiecrazy.wardance.event.ProjectileParryEvent) ItemStack(net.minecraft.item.ItemStack) SubscribeEvent(net.minecraftforge.eventbus.api.SubscribeEvent)

Aggregations

ParryEvent (jackiecrazy.wardance.event.ParryEvent)2 LivingEntity (net.minecraft.entity.LivingEntity)2 CombatDamageSource (jackiecrazy.wardance.api.CombatDamageSource)1 ICombatCapability (jackiecrazy.wardance.capability.resources.ICombatCapability)1 ProjectileParryEvent (jackiecrazy.wardance.event.ProjectileParryEvent)1 CombatUtils (jackiecrazy.wardance.utils.CombatUtils)1 StealthUtils (jackiecrazy.wardance.utils.StealthUtils)1 PlayerEntity (net.minecraft.entity.player.PlayerEntity)1 ItemStack (net.minecraft.item.ItemStack)1 EffectInstance (net.minecraft.potion.EffectInstance)1 SubscribeEvent (net.minecraftforge.eventbus.api.SubscribeEvent)1