package com.hbm.items.weapon.sedna; import java.util.ArrayList; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; import com.hbm.entity.projectile.EntityBulletBaseMK4; import com.hbm.entity.projectile.EntityBulletBeamBase; import com.hbm.interfaces.NotableComments; import com.hbm.inventory.RecipesCommon.ComparableStack; import com.hbm.items.ModItems; import com.hbm.items.weapon.sedna.factory.ConfettiUtil; import com.hbm.items.weapon.sedna.factory.GunFactory.EnumAmmo; import com.hbm.items.weapon.sedna.factory.GunFactory.EnumAmmoSecret; import com.hbm.particle.SpentCasing; import com.hbm.util.BobMathUtil; import com.hbm.util.EntityDamageUtil; import com.hbm.util.TrackerUtil; import com.hbm.util.DamageResistanceHandler.DamageClass; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.DamageSource; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.ForgeDirection; @NotableComments public class BulletConfig implements Cloneable { public static List configs = new ArrayList(); public int id; public ComparableStack ammo; /** How much ammo is added to a standard mag when loading one item */ public int ammoReloadCount = 1; public float velocity = 10F; public float spread = 0F; public float wear = 1F; public int projectilesMin = 1; public int projectilesMax = 1; public ProjectileType pType = ProjectileType.BULLET; public float damageMult = 1.0F; public float armorThresholdNegation = 0.0F; public float armorPiercingPercent = 0.0F; public float knockbackMult = 0.1F; public float headshotMult = 1.0F; public DamageClass dmgClass = DamageClass.PHYSICAL; public float ricochetAngle = 5F; public int maxRicochetCount = 2; /** Whether damage dealt to an entity is subtracted from the projectile's damage on penetration */ public boolean damageFalloffByPen = true; public Consumer onUpdate; public BiConsumer onImpact; public BiConsumer onImpactBeam; //fuck fuck fuck fuck i should have used a better base class here god dammit public BiConsumer onRicochet = LAMBDA_STANDARD_RICOCHET; public BiConsumer onEntityHit = LAMBDA_STANDARD_ENTITY_HIT; public double gravity = 0; public int expires = 30; public boolean impactsEntities = true; public boolean doesPenetrate = false; /** Whether projectiles ignore blocks entirely */ public boolean isSpectral = false; public int selfDamageDelay = 2; public boolean blackPowder = false; public boolean renderRotations = true; public SpentCasing casing; public BiConsumer renderer; public BiConsumer rendererBeam; public BulletConfig() { this.id = configs.size(); configs.add(this); } /** Required for the clone() operation to reset the ID, otherwise the ID and config entry will be the same as the original */ public BulletConfig forceReRegister() { this.id = configs.size(); configs.add(this); return this; } public BulletConfig setBeam() { this.pType = ProjectileType.BEAM; return this; } public BulletConfig setChunkloading() { this.pType = ProjectileType.BULLET_CHUNKLOADING; return this; } public BulletConfig setItem(Item ammo) { this.ammo = new ComparableStack(ammo); return this; } public BulletConfig setItem(ItemStack ammo) { this.ammo = new ComparableStack(ammo); return this; } public BulletConfig setItem(EnumAmmo ammo) { this.ammo = new ComparableStack(ModItems.ammo_standard, 1, ammo.ordinal()); return this; } public BulletConfig setItem(EnumAmmoSecret ammo) { this.ammo = new ComparableStack(ModItems.ammo_secret, 1, ammo.ordinal()); return this; } public BulletConfig setReloadCount(int ammoReloadCount) { this.ammoReloadCount = ammoReloadCount; return this; } public BulletConfig setVel(float velocity) { this.velocity = velocity; return this; } public BulletConfig setSpread(float spread) { this.spread = spread; return this; } public BulletConfig setWear(float wear) { this.wear = wear; return this; } public BulletConfig setProjectiles(int amount) { this.projectilesMin = this.projectilesMax = amount; return this; } public BulletConfig setProjectiles(int min, int max) { this.projectilesMin = min; this.projectilesMax = max; return this; } public BulletConfig setDamage(float damageMult) { this.damageMult = damageMult; return this; } public BulletConfig setThresholdNegation(float armorThresholdNegation) { this.armorThresholdNegation = armorThresholdNegation; return this; } public BulletConfig setArmorPiercing(float armorPiercingPercent) { this.armorPiercingPercent = armorPiercingPercent; return this; } public BulletConfig setKnockback(float knockbackMult) { this.knockbackMult = knockbackMult; return this; } public BulletConfig setHeadshot(float headshotMult) { this.headshotMult = headshotMult; return this; } public BulletConfig setupDamageClass(DamageClass clazz) { this.dmgClass = clazz; return this; } public BulletConfig setRicochetAngle(float angle) { this.ricochetAngle = angle; return this; } public BulletConfig setRicochetCount(int count) { this.maxRicochetCount = count; return this; } public BulletConfig setDamageFalloutByPen(boolean falloff) { this.damageFalloffByPen = falloff; return this; } public BulletConfig setGrav(double gravity) { this.gravity = gravity; return this; } public BulletConfig setLife(int expires) { this.expires = expires; return this; } public BulletConfig setImpactsEntities(boolean impact) { this.impactsEntities = impact; return this; } public BulletConfig setDoesPenetrate(boolean pen) { this.doesPenetrate = pen; return this; } public BulletConfig setSpectral(boolean spectral) { this.isSpectral = spectral; return this; } public BulletConfig setSelfDamageDelay(int delay) { this.selfDamageDelay = delay; return this; } public BulletConfig setBlackPowder(boolean bp) { this.blackPowder = bp; return this; } public BulletConfig setRenderRotations(boolean rot) { this.renderRotations = rot; return this; } public BulletConfig setCasing(SpentCasing casing) { this.casing = casing; return this; } public BulletConfig setRenderer(BiConsumer renderer) { this.renderer = renderer; return this; } public BulletConfig setRendererBeam(BiConsumer renderer) { this.rendererBeam = renderer; return this; } public BulletConfig setOnUpdate(Consumer lambda) { this.onUpdate = lambda; return this; } public BulletConfig setOnRicochet(BiConsumer lambda) { this.onRicochet = lambda; return this; } public BulletConfig setOnImpact(BiConsumer lambda) { this.onImpact = lambda; return this; } public BulletConfig setOnBeamImpact(BiConsumer lambda) { this.onImpactBeam = lambda; return this; } public BulletConfig setOnEntityHit(BiConsumer lambda) { this.onEntityHit = lambda; return this; } public static enum ProjectileType { BULLET, BULLET_CHUNKLOADING, BEAM } @SuppressWarnings("incomplete-switch") //shut up public static DamageSource getDamage(Entity projectile, EntityLivingBase shooter, DamageClass dmgClass) { DamageSource dmg; if(shooter != null) dmg = new DamageSourceSednaWithAttacker(dmgClass.name(), projectile, shooter); else dmg = new DamageSourceSednaNoAttacker(dmgClass.name()); switch(dmgClass) { case PHYSICAL: dmg.setProjectile(); break; case FIRE: dmg.setFireDamage(); break; case EXPLOSIVE: dmg.setExplosion(); break; case ELECTRIC: break; case LASER: break; case SUBATOMIC: break; } return dmg; } public static BiConsumer LAMBDA_STANDARD_RICOCHET = (bullet, mop) -> { if(mop.typeOfHit == mop.typeOfHit.BLOCK) { Block b = bullet.worldObj.getBlock(mop.blockX, mop.blockY, mop.blockZ); if(b.getMaterial() == Material.glass) { bullet.worldObj.func_147480_a(mop.blockX, mop.blockY, mop.blockZ, false); bullet.setPosition(mop.hitVec.xCoord, mop.hitVec.yCoord, mop.hitVec.zCoord); return; } ForgeDirection dir = ForgeDirection.getOrientation(mop.sideHit); Vec3 face = Vec3.createVectorHelper(dir.offsetX, dir.offsetY, dir.offsetZ); Vec3 vel = Vec3.createVectorHelper(bullet.motionX, bullet.motionY, bullet.motionZ).normalize(); double angle = Math.abs(BobMathUtil.getCrossAngle(vel, face) - 90); if(angle <= bullet.config.ricochetAngle) { bullet.ricochets++; if(bullet.ricochets > bullet.config.maxRicochetCount) { bullet.setPosition(mop.hitVec.xCoord, mop.hitVec.yCoord, mop.hitVec.zCoord); bullet.setDead(); } switch(mop.sideHit) { case 0: case 1: bullet.motionY *= -1; break; case 2: case 3: bullet.motionZ *= -1; break; case 4: case 5: bullet.motionX *= -1; break; } bullet.worldObj.playSoundAtEntity(bullet, "hbm:weapon.ricochet", 0.25F, 1.0F); bullet.setPosition(mop.hitVec.xCoord, mop.hitVec.yCoord, mop.hitVec.zCoord); //send a teleport so the ricochet is more accurate instead of the interp smoothing fucking everything up if(bullet.worldObj instanceof WorldServer) TrackerUtil.sendTeleport((WorldServer) bullet.worldObj, bullet); return; } else { bullet.setPosition(mop.hitVec.xCoord, mop.hitVec.yCoord, mop.hitVec.zCoord); bullet.setDead(); } } }; public static BiConsumer LAMBDA_STANDARD_ENTITY_HIT = (bullet, mop) -> { if(mop.typeOfHit == mop.typeOfHit.ENTITY) { Entity entity = mop.entityHit; if(entity == bullet.getThrower() && bullet.ticksExisted < bullet.selfDamageDelay()) return; if(entity instanceof EntityLivingBase && ((EntityLivingBase) entity).getHealth() <= 0) return; DamageSource source = bullet.config.getDamage(bullet, bullet.getThrower(), bullet.config.dmgClass); if(!(entity instanceof EntityLivingBase)) { EntityDamageUtil.attackEntityFromIgnoreIFrame(entity, source, bullet.damage); return; } EntityLivingBase living = (EntityLivingBase) entity; float prevHealth = living.getHealth(); EntityDamageUtil.attackEntityFromNT(living, source, bullet.damage, true, true, bullet.config.knockbackMult, bullet.config.armorThresholdNegation, bullet.config.armorPiercingPercent); float newHealth = living.getHealth(); if(bullet.config.damageFalloffByPen) bullet.damage -= Math.max(prevHealth - newHealth, 0) * 0.5; if(!bullet.doesPenetrate() || bullet.damage < 0) { bullet.setPosition(mop.hitVec.xCoord, mop.hitVec.yCoord, mop.hitVec.zCoord); bullet.setDead(); } if(!living.isEntityAlive()) ConfettiUtil.decideConfetti(living, source); } }; public static BiConsumer LAMBDA_STANDARD_BEAM_HIT = (bullet, mop) -> { if(mop.typeOfHit == mop.typeOfHit.ENTITY) { Entity entity = mop.entityHit; if(entity instanceof EntityLivingBase && ((EntityLivingBase) entity).getHealth() <= 0) return; DamageSource source = bullet.config.getDamage(bullet, bullet.getThrower(), bullet.config.dmgClass); if(!(entity instanceof EntityLivingBase)) { EntityDamageUtil.attackEntityFromIgnoreIFrame(entity, source, bullet.damage); return; } EntityLivingBase living = (EntityLivingBase) entity; EntityDamageUtil.attackEntityFromNT(living, source, bullet.damage, true, true, bullet.config.knockbackMult, bullet.config.armorThresholdNegation, bullet.config.armorPiercingPercent); if(!living.isEntityAlive()) ConfettiUtil.decideConfetti(living, source); } }; public static BiConsumer LAMBDA_BEAM_HIT = (beam, mop) -> { if(mop.typeOfHit == mop.typeOfHit.ENTITY) { Entity entity = mop.entityHit; if(entity instanceof EntityLivingBase && ((EntityLivingBase) entity).getHealth() <= 0) return; DamageSource source = beam.config.getDamage(beam, beam.thrower, beam.config.dmgClass); if(!(entity instanceof EntityLivingBase)) { EntityDamageUtil.attackEntityFromIgnoreIFrame(entity, source, beam.damage); return; } EntityLivingBase living = (EntityLivingBase) entity; EntityDamageUtil.attackEntityFromNT(living, source, beam.damage, true, false, beam.config.knockbackMult, beam.config.armorThresholdNegation, beam.config.armorPiercingPercent); } }; @Override public BulletConfig clone() { try { BulletConfig clone = (BulletConfig) super.clone(); clone.forceReRegister(); return clone; } catch(CloneNotSupportedException e) { } return null; } }