From 10987bee2c2530d9400eaac1525c804237a2764a Mon Sep 17 00:00:00 2001 From: Bob Date: Mon, 21 Oct 2024 21:11:45 +0200 Subject: [PATCH] lockon funsies --- .../java/com/hbm/blocks/bomb/Landmine.java | 22 ++- .../projectile/EntityBulletBaseMK4.java | 25 ++++ .../hbm/items/weapon/sedna/ItemGunBaseNT.java | 7 + .../com/hbm/items/weapon/sedna/Receiver.java | 6 + .../hbm/items/weapon/sedna/factory/Lego.java | 15 +- .../weapon/sedna/factory/Orchestras.java | 19 +++ .../weapon/sedna/factory/XFactoryRocket.java | 2 +- .../weapon/sedna/impl/ItemGunStinger.java | 88 ++++++++---- .../hbm/particle/helper/CasingCreator.java | 3 + .../helper/ExplosionSmallCreator.java | 3 + .../com/hbm/particle/helper/FlameCreator.java | 3 + src/main/java/com/hbm/util/Vec3NT.java | 128 ++++++++++++++++++ src/main/resources/assets/hbm/sounds.json | 1 + .../assets/hbm/sounds/weapon/fire/lockon.ogg | Bin 0 -> 18011 bytes .../weapon/{ => fire}/loudestNoiseOnEarth.ogg | Bin 15 files changed, 292 insertions(+), 30 deletions(-) create mode 100644 src/main/java/com/hbm/util/Vec3NT.java create mode 100644 src/main/resources/assets/hbm/sounds/weapon/fire/lockon.ogg rename src/main/resources/assets/hbm/sounds/weapon/{ => fire}/loudestNoiseOnEarth.ogg (100%) diff --git a/src/main/java/com/hbm/blocks/bomb/Landmine.java b/src/main/java/com/hbm/blocks/bomb/Landmine.java index 5f6b76062..35b7bdc9a 100644 --- a/src/main/java/com/hbm/blocks/bomb/Landmine.java +++ b/src/main/java/com/hbm/blocks/bomb/Landmine.java @@ -5,6 +5,13 @@ import java.util.Random; import com.hbm.blocks.ModBlocks; import com.hbm.explosion.ExplosionLarge; import com.hbm.explosion.ExplosionNukeSmall; +import com.hbm.explosion.vanillant.ExplosionVNT; +import com.hbm.explosion.vanillant.standard.BlockAllocatorStandard; +import com.hbm.explosion.vanillant.standard.BlockMutatorFire; +import com.hbm.explosion.vanillant.standard.BlockProcessorStandard; +import com.hbm.explosion.vanillant.standard.EntityProcessorCrossSmooth; +import com.hbm.explosion.vanillant.standard.ExplosionEffectWeapon; +import com.hbm.explosion.vanillant.standard.PlayerProcessorStandard; import com.hbm.interfaces.IBomb; import com.hbm.items.ModItems; import com.hbm.tileentity.bomb.TileEntityLandmine; @@ -147,10 +154,19 @@ public class Landmine extends BlockContainer implements IBomb { Landmine.safeMode = false; if(this == ModBlocks.mine_ap) { - world.newExplosion(null, x + 0.5, y + 0.5, z + 0.5, 2.5F, false, false); + ExplosionVNT vnt = new ExplosionVNT(world, x + 0.5, y + 0.5, z + 0.5, 3F); + vnt.setEntityProcessor(new EntityProcessorCrossSmooth(0.5, 10F)); + vnt.setPlayerProcessor(new PlayerProcessorStandard()); + vnt.setSFX(new ExplosionEffectWeapon(5, 1F, 0.5F)); + vnt.explode(); } else if(this == ModBlocks.mine_he) { - ExplosionLarge.explode(world, x + 0.5, y + 0.5, z + 0.5, 3F, true, false, false); - world.newExplosion(null, x + 0.5, y + 2, z + 0.5, 15F, false, false); + ExplosionVNT vnt = new ExplosionVNT(world, x + 0.5, y + 0.5, z + 0.5, 4F); + vnt.setBlockAllocator(new BlockAllocatorStandard()); + vnt.setBlockProcessor(new BlockProcessorStandard()); + vnt.setEntityProcessor(new EntityProcessorCrossSmooth(1, 35)); + vnt.setPlayerProcessor(new PlayerProcessorStandard()); + vnt.setSFX(new ExplosionEffectWeapon(15, 3.5F, 1.25F)); + vnt.explode(); } else if(this == ModBlocks.mine_shrap) { ExplosionLarge.explode(world, x + 0.5, y + 0.5, z + 0.5, 1, true, false, false); ExplosionLarge.spawnShrapnelShower(world, x + 0.5, y + 0.5, z + 0.5, 0, 1D, 0, 45, 0.2D); diff --git a/src/main/java/com/hbm/entity/projectile/EntityBulletBaseMK4.java b/src/main/java/com/hbm/entity/projectile/EntityBulletBaseMK4.java index 58227ab80..f4b37d283 100644 --- a/src/main/java/com/hbm/entity/projectile/EntityBulletBaseMK4.java +++ b/src/main/java/com/hbm/entity/projectile/EntityBulletBaseMK4.java @@ -1,9 +1,13 @@ package com.hbm.entity.projectile; import com.hbm.items.weapon.sedna.BulletConfig; +import com.hbm.util.BobMathUtil; import com.hbm.util.TrackerUtil; +import com.hbm.util.Vec3NT; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.EntityTrackerEntry; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; @@ -19,6 +23,7 @@ public class EntityBulletBaseMK4 extends EntityThrowableInterp { public double accel; public float damage; public int ricochets = 0; + public Entity lockonTarget = null; public EntityBulletBaseMK4(World world) { super(world); @@ -49,6 +54,10 @@ public class EntityBulletBaseMK4 extends EntityThrowableInterp { this.motionX = -MathHelper.sin(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI); this.motionZ = MathHelper.cos(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI); this.motionY = (-MathHelper.sin(this.rotationPitch / 180.0F * (float) Math.PI)); + + motionX += entity.motionX; + motionY += entity.motionY; + motionZ += entity.motionZ; this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, 1.0F, this.config.spread + gunSpread); } @@ -90,6 +99,22 @@ public class EntityBulletBaseMK4 extends EntityThrowableInterp { double dY = this.posY - this.prevPosY; double dZ = this.posZ - this.prevPosZ; + if(this.lockonTarget != null && !this.lockonTarget.isDead) { + Vec3NT motion = new Vec3NT(motionX, motionY, motionZ); + double vel = motion.lengthVector(); + Vec3NT delta = new Vec3NT(lockonTarget.posX - posX, lockonTarget.posY + lockonTarget.height / 2D - posY, lockonTarget.posZ - posZ); + float turn = Math.min(0.005F * this.ticksExisted, 1F); + Vec3NT newVec = new Vec3NT( + BobMathUtil.interp(motion.xCoord, delta.xCoord, turn), + BobMathUtil.interp(motion.yCoord, delta.yCoord, turn), + BobMathUtil.interp(motion.zCoord, delta.zCoord, turn)).normalizeSelf().multiply(vel); + this.motionX = newVec.xCoord; + this.motionY = newVec.yCoord; + this.motionZ = newVec.zCoord; + EntityTrackerEntry entry = TrackerUtil.getTrackerEntry((WorldServer) worldObj, this.getEntityId()); + entry.lastYaw = MathHelper.floor_float(this.rotationYaw * 256.0F / 360.0F) + 10; //force-trigger rotation update + } + this.prevVelocity = this.velocity; this.velocity = Math.sqrt(dX * dX + dY * dY + dZ * dZ); diff --git a/src/main/java/com/hbm/items/weapon/sedna/ItemGunBaseNT.java b/src/main/java/com/hbm/items/weapon/sedna/ItemGunBaseNT.java index 78ea096aa..0028748f3 100644 --- a/src/main/java/com/hbm/items/weapon/sedna/ItemGunBaseNT.java +++ b/src/main/java/com/hbm/items/weapon/sedna/ItemGunBaseNT.java @@ -59,6 +59,8 @@ public class ItemGunBaseNT extends Item implements IKeybindReceiver, IEquipRecei public static final String KEY_RELOAD = "reload_"; public static final String KEY_LASTANIM = "lastanim_"; public static final String KEY_ANIMTIMER = "animtimer_"; + public static final String KEY_LOCKONTARGET = "lockontarget"; + public static final String KEY_LOCKEDON = "lockedon"; public static ConcurrentHashMap loopedSounds = new ConcurrentHashMap(); @@ -231,6 +233,11 @@ public class ItemGunBaseNT extends Item implements IKeybindReceiver, IEquipRecei // GUN AIMING // public static float getWear(ItemStack stack, int index) { return getValueFloat(stack, KEY_WEAR + index); } public static void setWear(ItemStack stack, int index, float value) { setValueFloat(stack, KEY_WEAR + index, value); } + // LOCKON // + public static int getLockonTarget(ItemStack stack) { return getValueInt(stack, KEY_LOCKONTARGET); } + public static void setLockonTarget(ItemStack stack, int value) { setValueInt(stack, KEY_LOCKONTARGET, value); } + public static boolean getIsLockedOn(ItemStack stack) { return getValueBool(stack, KEY_LOCKEDON); } + public static void setIsLockedOn(ItemStack stack, boolean value) { setValueBool(stack, KEY_LOCKEDON, value); } // ANIM TRACKING // public static AnimType getLastAnim(ItemStack stack, int index) { return EnumUtil.grabEnumSafely(AnimType.class, getValueInt(stack, KEY_LASTANIM + index)); } public static void setLastAnim(ItemStack stack, int index, AnimType value) { setValueInt(stack, KEY_LASTANIM + index, value.ordinal()); } diff --git a/src/main/java/com/hbm/items/weapon/sedna/Receiver.java b/src/main/java/com/hbm/items/weapon/sedna/Receiver.java index 6c6f6f13a..73bc7faf8 100644 --- a/src/main/java/com/hbm/items/weapon/sedna/Receiver.java +++ b/src/main/java/com/hbm/items/weapon/sedna/Receiver.java @@ -134,4 +134,10 @@ public class Receiver { .canFire(Lego.LAMBDA_STANDARD_CAN_FIRE) .fire(Lego.LAMBDA_STANDARD_FIRE); } + + public Receiver setupLockonFire() { + return this + .canFire(Lego.LAMBDA_LOCKON_CAN_FIRE) + .fire(Lego.LAMBDA_STANDARD_FIRE); + } } diff --git a/src/main/java/com/hbm/items/weapon/sedna/factory/Lego.java b/src/main/java/com/hbm/items/weapon/sedna/factory/Lego.java index 139f32a12..1c84b95bc 100644 --- a/src/main/java/com/hbm/items/weapon/sedna/factory/Lego.java +++ b/src/main/java/com/hbm/items/weapon/sedna/factory/Lego.java @@ -154,20 +154,28 @@ public class Lego { /** Toggles isAiming. Used by keybinds. */ public static BiConsumer LAMBDA_TOGGLE_AIM = (stack, ctx) -> { ItemGunBaseNT.setIsAiming(stack, !ItemGunBaseNT.getIsAiming(stack)); }; - - /** Returns true if the mag has ammo in it. Used by keybind functions on whether to fire, and deciders on whether to trigger a refire, */ + + /** Returns true if the mag has ammo in it. Used by keybind functions on whether to fire, and deciders on whether to trigger a refire. */ public static BiFunction LAMBDA_STANDARD_CAN_FIRE = (stack, ctx) -> { return ctx.config.getReceivers(stack)[0].getMagazine(stack).getAmount(stack) > 0; }; + + /** Returns true if the mag has ammo in it, and the gun is in the locked on state */ + public static BiFunction LAMBDA_LOCKON_CAN_FIRE = (stack, ctx) -> { return ctx.config.getReceivers(stack)[0].getMagazine(stack).getAmount(stack) > 0 && ItemGunBaseNT.getIsLockedOn(stack); }; /** JUMPER - bypasses mag testing and just allows constant fire */ public static BiFunction LAMBDA_DEBUG_CAN_FIRE = (stack, ctx) -> { return true; }; - + /** Spawns an EntityBulletBaseMK4 with the loaded bulletcfg */ public static BiConsumer LAMBDA_STANDARD_FIRE = (stack, ctx) -> { doStandardFire(stack, ctx, AnimType.CYCLE); }; + /** Spawns an EntityBulletBaseMK4 with the loaded bulletcfg, then resets lockon progress */ + public static BiConsumer LAMBDA_LOCKON_FIRE = (stack, ctx) -> { + doStandardFire(stack, ctx, AnimType.CYCLE); + ItemGunBaseNT.setIsLockedOn(stack, false); + }; public static void doStandardFire(ItemStack stack, LambdaContext ctx, AnimType anim) { EntityPlayer player = ctx.player; @@ -195,6 +203,7 @@ public class Lego { float damage = primary.getBaseDamage(stack) * getStandardWearDamage(stack, ctx.config, index); float spread = primary.getGunSpread(stack) * aim + getStandardWearSpread(stack, ctx.config, index) * 0.125F; EntityBulletBaseMK4 mk4 = new EntityBulletBaseMK4(player, config, damage, spread, sideOffset, heightOffset, forwardOffset); + if(ItemGunBaseNT.getIsLockedOn(stack)) mk4.lockonTarget = player.worldObj.getEntityByID(ItemGunBaseNT.getLockonTarget(stack)); player.worldObj.spawnEntityInWorld(mk4); } diff --git a/src/main/java/com/hbm/items/weapon/sedna/factory/Orchestras.java b/src/main/java/com/hbm/items/weapon/sedna/factory/Orchestras.java index 52ce656b4..aba68cf83 100644 --- a/src/main/java/com/hbm/items/weapon/sedna/factory/Orchestras.java +++ b/src/main/java/com/hbm/items/weapon/sedna/factory/Orchestras.java @@ -5,6 +5,7 @@ import java.util.function.BiConsumer; import com.hbm.config.ClientConfig; import com.hbm.items.weapon.sedna.ItemGunBaseNT; import com.hbm.items.weapon.sedna.Receiver; +import com.hbm.items.weapon.sedna.impl.ItemGunStinger; import com.hbm.items.weapon.sedna.ItemGunBaseNT.LambdaContext; import com.hbm.items.weapon.sedna.mags.IMagazine; import com.hbm.main.MainRegistry; @@ -664,6 +665,24 @@ public class Orchestras { if(player.worldObj.isRemote) return; AnimType type = ItemGunBaseNT.getLastAnim(stack, ctx.configIndex); int timer = ItemGunBaseNT.getAnimTimer(stack, ctx.configIndex); + + AudioWrapper runningAudio = ItemGunBaseNT.loopedSounds.get(player); + if(ItemGunStinger.getLockonProgress(stack) > 0 && !ItemGunStinger.getIsLockedOn(stack)) { + //start sound + if(runningAudio == null || !runningAudio.isPlaying()) { + AudioWrapper audio = MainRegistry.proxy.getLoopedSound("hbm:weapon.fire.lockon", (float) player.posX, (float) player.posY, (float) player.posZ, 1F, 15F, 1F, 10); + ItemGunBaseNT.loopedSounds.put(player, audio); + audio.startSound(); + } + //keepalive + if(runningAudio != null && runningAudio.isPlaying()) { + runningAudio.keepAlive(); + runningAudio.updatePosition((float) player.posX, (float) player.posY, (float) player.posZ); + } + } else { + //stop sound due to timeout + if(runningAudio != null && runningAudio.isPlaying()) runningAudio.stopSound(); + } if(type == AnimType.RELOAD) { if(timer == 30) player.worldObj.playSoundAtEntity(player, "hbm:weapon.reload.insertCanister", 1F, 1F); diff --git a/src/main/java/com/hbm/items/weapon/sedna/factory/XFactoryRocket.java b/src/main/java/com/hbm/items/weapon/sedna/factory/XFactoryRocket.java index 8eed61386..b43904073 100644 --- a/src/main/java/com/hbm/items/weapon/sedna/factory/XFactoryRocket.java +++ b/src/main/java/com/hbm/items/weapon/sedna/factory/XFactoryRocket.java @@ -65,7 +65,7 @@ public class XFactoryRocket { .dmg(25F).delay(5).reload(50).jam(40).sound("hbm:weapon.rpgShoot", 1.0F, 1.0F) .mag(new MagazineSingleReload(0, 1).addConfigs(rocket_rpzb_he, rocket_rpzb_heat)) .offset(1, -0.0625 * 1.5, -0.1875D) - .setupStandardFire().recoil(Lego.LAMBDA_STANDARD_RECOIL)) + .setupLockonFire().recoil(Lego.LAMBDA_STANDARD_RECOIL)) .setupStandardConfiguration().ps(LAMBDA_STINGER_SECONDARY_PRESS).rs(LAMBDA_STINGER_SECONDARY_RELEASE) .anim(LAMBDA_PANZERSCHRECK_ANIMS).orchestra(Orchestras.ORCHESTRA_STINGER) ).setUnlocalizedName("gun_stinger").setTextureName(RefStrings.MODID + ":gun_darter"); diff --git a/src/main/java/com/hbm/items/weapon/sedna/impl/ItemGunStinger.java b/src/main/java/com/hbm/items/weapon/sedna/impl/ItemGunStinger.java index 6fcd39e19..5dccffc0e 100644 --- a/src/main/java/com/hbm/items/weapon/sedna/impl/ItemGunStinger.java +++ b/src/main/java/com/hbm/items/weapon/sedna/impl/ItemGunStinger.java @@ -1,9 +1,12 @@ package com.hbm.items.weapon.sedna.impl; +import java.util.List; + import com.hbm.items.weapon.sedna.GunConfig; import com.hbm.items.weapon.sedna.ItemGunBaseNT; import com.hbm.items.weapon.sedna.hud.IHUDComponent; import com.hbm.render.util.RenderScreenOverlay; +import com.hbm.util.Vec3NT; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -11,6 +14,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; import net.minecraftforge.client.event.RenderGameOverlayEvent.Pre; @@ -18,8 +22,6 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent.Pre; public class ItemGunStinger extends ItemGunBaseNT { public static final String KEY_LOCKINGON = "lockingon"; - public static final String KEY_LOCKEDON = "lockedon"; - public static final String KEY_LOCKONTARGET = "lockontarget"; public static final String KEY_LOCKONPROGRESS = "lockonprogress"; public static float prevLockon; @@ -40,37 +42,81 @@ public class ItemGunStinger extends ItemGunBaseNT { } this.prevLockon = this.lockon; - int prevTarget = this.getLockonTarget(stack); - if(isHeld && this.getIsLockingOn(stack) && this.getIsAiming(stack)) { - int newLockonTarget = this.getLockonTarget(player); - - if(newLockonTarget == -1) { - resetLockon(world, stack); - } else { - if(newLockonTarget != prevTarget) { - resetLockon(world, stack); - this.setLockonTarget(stack, newLockonTarget); + + if(!world.isRemote) { + int prevTarget = this.getLockonTarget(stack); + if(isHeld && this.getIsLockingOn(stack) && this.getIsAiming(stack) && this.getConfig(stack, 0).getReceivers(stack)[0].getMagazine(stack).getAmount(stack) > 0) { + int newLockonTarget = this.getLockonTarget(player); + + if(newLockonTarget == -1) { + if(!this.getIsLockedOn(stack)) resetLockon(world, stack); + } else { + if(!this.getIsLockedOn(stack) && newLockonTarget != prevTarget) { + resetLockon(world, stack); + this.setLockonTarget(stack, newLockonTarget); + } + progressLockon(world, stack); + + if(this.getLockonProgress(stack) >= 60 && !this.getIsLockedOn(stack)) { + player.worldObj.playSoundAtEntity(player, "hbm:item.techBleep", 1F, 1F); + this.setIsLockedOn(stack, true); + } } - progressLockon(world, stack); + } else { + resetLockon(world, stack); } } else { - resetLockon(world, stack); + if(this.getLockonProgress(stack) > 1) { + this.lockon += (1F / 60F); + } else { + this.lockon = 0; + } } } } public void resetLockon(World world, ItemStack stack) { - if(world.isRemote) this.lockon = 0F; - if(!world.isRemote) this.setLockonProgress(stack, 0); + this.setLockonProgress(stack, 0); + this.setIsLockedOn(stack, false); } public void progressLockon(World world, ItemStack stack) { - if(world.isRemote) this.lockon += (1F / 100F); - if(!world.isRemote) this.setLockonProgress(stack, this.getLockonProgress(stack) + 1); + this.setLockonProgress(stack, this.getLockonProgress(stack) + 1); } public static int getLockonTarget(EntityPlayer player) { - return -1; + + double x = player.posX; + double y = player.posY + player.getEyeHeight(); + double z = player.posZ; + + Vec3NT delta = new Vec3NT(player.getLook(1F)).multiply(150); + Vec3NT look = new Vec3NT(delta).add(x, y, z); + Vec3NT pos = new Vec3NT(x, y, z); + + AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(Vec3NT.getMinX(look, pos), Vec3NT.getMinY(look, pos), Vec3NT.getMinZ(look, pos), + Vec3NT.getMaxX(look, pos), Vec3NT.getMaxY(look, pos), Vec3NT.getMaxZ(look, pos)); + List entities = player.worldObj.getEntitiesWithinAABBExcludingEntity(player, aabb); + Entity closestEntity = null; + double closestAngle = 360D; + + Vec3NT toEntity = new Vec3NT(0, 0, 0); + + for(Entity entity : entities) { + if(entity.height < 0.5F) continue; + toEntity.setComponents(entity.posX - x, entity.posY + entity.height / 2D - y, entity.posZ - z); + + double vecProd = toEntity.xCoord * delta.xCoord + toEntity.yCoord * delta.yCoord + toEntity.zCoord * delta.zCoord; + double bot = toEntity.lengthVector() * delta.lengthVector(); + double angle = Math.abs(Math.acos(vecProd / bot) * 180 / Math.PI); + + if(angle < closestAngle && angle < 10) { + closestAngle = angle; + closestEntity = entity; + } + } + + return closestEntity == null ? - 1 : closestEntity.getEntityId(); } @Override @@ -101,10 +147,6 @@ public class ItemGunStinger extends ItemGunBaseNT { public static boolean getIsLockingOn(ItemStack stack) { return getValueBool(stack, KEY_LOCKINGON); } public static void setIsLockingOn(ItemStack stack, boolean value) { setValueBool(stack, KEY_LOCKINGON, value); } - public static boolean getIsLockedOn(ItemStack stack) { return getValueBool(stack, KEY_LOCKEDON); } - public static void setIsLockedOn(ItemStack stack, boolean value) { setValueBool(stack, KEY_LOCKEDON, value); } - public static int getLockonTarget(ItemStack stack) { return getValueInt(stack, KEY_LOCKONTARGET); } - public static void setLockonTarget(ItemStack stack, int value) { setValueInt(stack, KEY_LOCKONTARGET, value); } public static int getLockonProgress(ItemStack stack) { return getValueInt(stack, KEY_LOCKONPROGRESS); } public static void setLockonProgress(ItemStack stack, int value) { setValueInt(stack, KEY_LOCKONPROGRESS, value); } } diff --git a/src/main/java/com/hbm/particle/helper/CasingCreator.java b/src/main/java/com/hbm/particle/helper/CasingCreator.java index 2c8e3f57b..522ab322e 100644 --- a/src/main/java/com/hbm/particle/helper/CasingCreator.java +++ b/src/main/java/com/hbm/particle/helper/CasingCreator.java @@ -5,6 +5,8 @@ import java.util.Random; import com.hbm.particle.ParticleSpentCasing; import com.hbm.particle.SpentCasing; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.entity.player.EntityPlayer; @@ -56,6 +58,7 @@ public class CasingCreator implements IParticleCreator { } @Override + @SideOnly(Side.CLIENT) public void makeParticle(World world, EntityPlayer player, TextureManager texman, Random rand, double x, double y, double z, NBTTagCompound data) { String name = data.getString("name"); diff --git a/src/main/java/com/hbm/particle/helper/ExplosionSmallCreator.java b/src/main/java/com/hbm/particle/helper/ExplosionSmallCreator.java index 7c7745f8a..8c8862738 100644 --- a/src/main/java/com/hbm/particle/helper/ExplosionSmallCreator.java +++ b/src/main/java/com/hbm/particle/helper/ExplosionSmallCreator.java @@ -5,6 +5,8 @@ import java.util.Random; import com.hbm.particle.ParticleExplosionSmall; import cpw.mods.fml.relauncher.ReflectionHelper; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; @@ -34,6 +36,7 @@ public class ExplosionSmallCreator implements IParticleCreator { } @Override + @SideOnly(Side.CLIENT) public void makeParticle(World world, EntityPlayer player, TextureManager texman, Random rand, double x, double y, double z, NBTTagCompound data) { int cloudCount = data.getInteger("cloudCount"); diff --git a/src/main/java/com/hbm/particle/helper/FlameCreator.java b/src/main/java/com/hbm/particle/helper/FlameCreator.java index da70bfacd..6e4f791e0 100644 --- a/src/main/java/com/hbm/particle/helper/FlameCreator.java +++ b/src/main/java/com/hbm/particle/helper/FlameCreator.java @@ -4,6 +4,8 @@ import java.util.Random; import com.hbm.particle.ParticleFlamethrower; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.entity.player.EntityPlayer; @@ -19,6 +21,7 @@ public class FlameCreator implements IParticleCreator { } @Override + @SideOnly(Side.CLIENT) public void makeParticle(World world, EntityPlayer player, TextureManager texman, Random rand, double x, double y, double z, NBTTagCompound data) { ParticleFlamethrower particle = new ParticleFlamethrower(world, x, y, z); Minecraft.getMinecraft().effectRenderer.addEffect(particle); diff --git a/src/main/java/com/hbm/util/Vec3NT.java b/src/main/java/com/hbm/util/Vec3NT.java new file mode 100644 index 000000000..8bedb3869 --- /dev/null +++ b/src/main/java/com/hbm/util/Vec3NT.java @@ -0,0 +1,128 @@ +package com.hbm.util; + +import net.minecraft.util.MathHelper; +import net.minecraft.util.Vec3; + +public class Vec3NT extends Vec3 { + + public Vec3NT(double x, double y, double z) { + super(x, y, z); + } + + public Vec3NT(Vec3 vec) { + super(vec.xCoord, vec.yCoord, vec.zCoord); + } + + public Vec3NT normalizeSelf() { + double len = MathHelper.sqrt_double(this.xCoord * this.xCoord + this.yCoord * this.yCoord + this.zCoord * this.zCoord); + if(len < 1.0E-4D) { + return multiply(0D); + } else { + return multiply(1D / len); + } + } + + public Vec3NT add(double x, double y, double z) { + this.xCoord += x; + this.yCoord += y; + this.zCoord += z; + return this; + } + + public Vec3NT multiply(double m) { + this.xCoord *= m; + this.yCoord *= m; + this.zCoord *= m; + return this; + } + + public Vec3NT multiply(double x, double y, double z) { + this.xCoord *= x; + this.yCoord *= y; + this.zCoord *= z; + return this; + } + + @Override + public Vec3NT setComponents(double x, double y, double z) { + this.xCoord = x; + this.yCoord = y; + this.zCoord = z; + return this; + } + + public Vec3NT rotateAroundXRad(double alpha) { + double cos = Math.cos(alpha); + double sin = Math.sin(alpha); + double x = this.xCoord; + double y = this.yCoord * cos + this.zCoord * sin; + double z = this.zCoord * cos - this.yCoord * sin; + return this.setComponents(x, y, z); + } + + public Vec3NT rotateAroundYRad(double alpha) { + double cos = Math.cos(alpha); + double sin = Math.sin(alpha); + double x = this.xCoord * cos + this.zCoord * sin; + double y = this.yCoord; + double z = this.zCoord * cos - this.xCoord * sin; + return this.setComponents(x, y, z); + } + + public Vec3NT rotateAroundZRad(double alpha) { + double cos = Math.cos(alpha); + double sin = Math.sin(alpha); + double x = this.xCoord * cos + this.yCoord * sin; + double y = this.yCoord * cos - this.xCoord * sin; + double z = this.zCoord; + return this.setComponents(x, y, z); + } + + public Vec3NT rotateAroundXDeg(double alpha) { + return this.rotateAroundXRad(alpha * 180D / Math.PI); + } + + public Vec3NT rotateAroundYDeg(double alpha) { + return this.rotateAroundYRad(alpha * 180D / Math.PI); + } + + public Vec3NT rotateAroundZDeg(double alpha) { + return this.rotateAroundZRad(alpha * 180D / Math.PI); + } + + public static double getMinX(Vec3NT... vecs) { + double min = Double.POSITIVE_INFINITY; + for(Vec3NT vec : vecs) if(vec.xCoord < min) min = vec.xCoord; + return min; + } + + public static double getMinY(Vec3NT... vecs) { + double min = Double.POSITIVE_INFINITY; + for(Vec3NT vec : vecs) if(vec.yCoord < min) min = vec.yCoord; + return min; + } + + public static double getMinZ(Vec3NT... vecs) { + double min = Double.POSITIVE_INFINITY; + for(Vec3NT vec : vecs) if(vec.zCoord < min) min = vec.zCoord; + return min; + } + + public static double getMaxX(Vec3NT... vecs) { + double max = Double.NEGATIVE_INFINITY; + for(Vec3NT vec : vecs) if(vec.xCoord > max) max = vec.xCoord; + return max; + } + + public static double getMaxY(Vec3NT... vecs) { + double max = Double.NEGATIVE_INFINITY; + for(Vec3NT vec : vecs) if(vec.yCoord > max) max = vec.yCoord; + return max; + } + + public static double getMaxZ(Vec3NT... vecs) { + double max = Double.NEGATIVE_INFINITY; + for(Vec3NT vec : vecs) if(vec.zCoord > max) max = vec.zCoord; + return max; + } +} diff --git a/src/main/resources/assets/hbm/sounds.json b/src/main/resources/assets/hbm/sounds.json index d418e7d5b..226c377c5 100644 --- a/src/main/resources/assets/hbm/sounds.json +++ b/src/main/resources/assets/hbm/sounds.json @@ -226,6 +226,7 @@ "weapon.fire.blackPowder": {"category": "player", "sounds": [{"name": "weapon/fire/blackPowder", "stream": false}]}, "weapon.fire.flameLoop": {"category": "player", "sounds": [{"name": "weapon/fire/flameLoop", "stream": false}]}, + "weapon.fire.lockon": {"category": "player", "sounds": [{"name": "weapon/fire/lockon", "stream": false}]}, "weapon.reload.boltClose": {"category": "player", "sounds": ["weapon/reload/boltClose"]}, "weapon.reload.boltOpen": {"category": "player", "sounds": ["weapon/reload/boltOpen"]}, diff --git a/src/main/resources/assets/hbm/sounds/weapon/fire/lockon.ogg b/src/main/resources/assets/hbm/sounds/weapon/fire/lockon.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6fb9782d48d9050fe00575ce1f0051604b616616 GIT binary patch literal 18011 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 z