diff --git a/src/main/java/cr0s/warpdrive/SirenSound.java b/src/main/java/cr0s/warpdrive/SirenSound.java new file mode 100644 index 00000000..4159489e --- /dev/null +++ b/src/main/java/cr0s/warpdrive/SirenSound.java @@ -0,0 +1,53 @@ +package cr0s.warpdrive; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.MovingSound; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +public class SirenSound extends MovingSound { + ResourceLocation resource; + float range; + float x, y, z; + + /**x, y and z are the position of the tile entity. the actual sound is broadcast from + xPosF, yPosF, zPosF, which is the location of the player. + The volume is adjusted according to the distance to x, y, z. + Why? Because Minecraft's sound system was not made for this kind of thing, and this + is the easiest way which: + 1. Produces a sound audible from a specifiable range. + 2. Produces a sound which decreases in volume the farther you get away from it. + 3. Doesn't completely spazz out the instant you try to actually use it.*/ + public SirenSound(ResourceLocation resource, float range, float x, float y, float z) { + super(resource); + + this.resource = resource; + this.range = range; + + this.x = x; + this.y = y; + this.z = z; + + this.xPosF = x; + this.yPosF = y; + this.zPosF = z; + } + + public void update() { + EntityPlayer player = Minecraft.getMinecraft().thePlayer; + + this.xPosF = (float) player.posX; + this.yPosF = (float) player.posY; + this.zPosF = (float) player.posZ; + + if (player.getDistance(x, y, z) > range) { + this.volume = 0.0F; + } else { + this.volume = 1.0F - scaleTo((float) player.getDistance(x, y, z), 0.0F, range, 0.0F, 1.0F); + } + } + + private float scaleTo(float num, float oldMin, float oldMax, float newMin, float newMax) { + return ((newMax - newMin)*(num - oldMin)) / (oldMax - oldMin) + newMin; + } +} diff --git a/src/main/java/cr0s/warpdrive/WarpDrive.java b/src/main/java/cr0s/warpdrive/WarpDrive.java index 90300e36..1010f823 100644 --- a/src/main/java/cr0s/warpdrive/WarpDrive.java +++ b/src/main/java/cr0s/warpdrive/WarpDrive.java @@ -6,17 +6,23 @@ import java.util.UUID; import com.mojang.authlib.GameProfile; import cr0s.warpdrive.block.*; +import cr0s.warpdrive.block.detection.*; import cr0s.warpdrive.block.forcefield.*; import cr0s.warpdrive.block.hull.BlockHullStairs; +import cr0s.warpdrive.compat.CompatMekanism; import cr0s.warpdrive.item.*; +import ic2.api.item.IC2Items; import net.minecraft.block.Block; import net.minecraft.block.BlockColored; import net.minecraft.client.Minecraft; import net.minecraft.command.ICommandSender; import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemArmor.ArmorMaterial; import net.minecraft.item.ItemDye; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; @@ -53,16 +59,6 @@ import cr0s.warpdrive.block.collection.BlockLaserTreeFarm; import cr0s.warpdrive.block.collection.BlockMiningLaser; import cr0s.warpdrive.block.collection.TileEntityLaserTreeFarm; import cr0s.warpdrive.block.collection.TileEntityMiningLaser; -import cr0s.warpdrive.block.detection.BlockCamera; -import cr0s.warpdrive.block.detection.BlockCloakingCoil; -import cr0s.warpdrive.block.detection.BlockCloakingCore; -import cr0s.warpdrive.block.detection.BlockMonitor; -import cr0s.warpdrive.block.detection.BlockRadar; -import cr0s.warpdrive.block.detection.BlockWarpIsolation; -import cr0s.warpdrive.block.detection.TileEntityCamera; -import cr0s.warpdrive.block.detection.TileEntityCloakingCore; -import cr0s.warpdrive.block.detection.TileEntityMonitor; -import cr0s.warpdrive.block.detection.TileEntityRadar; import cr0s.warpdrive.block.energy.BlockEnanReactorCore; import cr0s.warpdrive.block.energy.BlockEnanReactorLaser; import cr0s.warpdrive.block.energy.BlockEnergyBank; @@ -126,7 +122,12 @@ public class WarpDrive implements LoadingCallback { public static final String VERSION = "@version@"; public static final boolean isDev = VERSION.equals("@" + "version" + "@") || VERSION.contains("-dev"); public static GameProfile gameProfile = new GameProfile(UUID.nameUUIDFromBytes("[WarpDrive]".getBytes()), "[WarpDrive]"); - + + public static Block blockSirenIndustrial; + public static Block blockSirenRaidBasic; + public static Block blockSirenRaidAdvanced; + public static Block blockSirenRaidSuperior; + public static Block blockShipCore; public static Block blockShipController; public static Block blockRadar; @@ -236,7 +237,35 @@ public class WarpDrive implements LoadingCallback { // open access to Block.blockHardness fieldBlockHardness = WarpDrive.getField(Block.class, "blockHardness", "field_149782_v"); - + + // SIRENS + blockSirenIndustrial = new BlockSiren("siren_industrial", false, 32.0F); + blockSirenRaidBasic = new BlockSiren("siren_raid_basic", true, 32.0F); + blockSirenRaidAdvanced = new BlockSiren("siren_raid_advanced", true, 64.0F); + blockSirenRaidSuperior = new BlockSiren("siren_raid_superior", true, 128.0F); + + GameRegistry.registerBlock(blockSirenIndustrial, "siren_industrial"); + GameRegistry.registerBlock(blockSirenRaidBasic, "siren_raid_basic"); + GameRegistry.registerBlock(blockSirenRaidAdvanced, "siren_raid_advanced"); + GameRegistry.registerBlock(blockSirenRaidSuperior, "siren_raid_superior"); + + GameRegistry.addRecipe(new ItemStack(blockSirenIndustrial, 1), "ICI", "ICI", "NRN", + Character.valueOf('I'), new ItemStack(Blocks.planks, 1), + Character.valueOf('C'), new ItemStack(Items.iron_ingot, 1), + Character.valueOf('N'), new ItemStack(Blocks.noteblock, 1), + Character.valueOf('R'), new ItemStack(Items.redstone, 1)); + GameRegistry.addRecipe(new ItemStack(blockSirenRaidBasic, 1), " I ", "ISI", " I ", + Character.valueOf('I'), new ItemStack(Items.iron_ingot, 1), + Character.valueOf('S'), new ItemStack(blockSirenIndustrial, 1)); + GameRegistry.addRecipe(new ItemStack(blockSirenRaidAdvanced, 1), " I ", "ISI", " I ", + Character.valueOf('I'), new ItemStack(Items.gold_ingot, 1), + Character.valueOf('S'), new ItemStack(blockSirenRaidBasic, 1)); + GameRegistry.addRecipe(new ItemStack(blockSirenRaidSuperior, 1), " I ", "ISI", " I ", + Character.valueOf('I'), new ItemStack(Items.diamond, 1), + Character.valueOf('S'), new ItemStack(blockSirenRaidAdvanced, 1)); + + GameRegistry.registerTileEntity(TileEntitySiren.class, MODID + ":tileEntitySiren"); + // CORE CONTROLLER blockShipController = new BlockShipController(); diff --git a/src/main/java/cr0s/warpdrive/block/detection/BlockSiren.java b/src/main/java/cr0s/warpdrive/block/detection/BlockSiren.java new file mode 100644 index 00000000..883904f8 --- /dev/null +++ b/src/main/java/cr0s/warpdrive/block/detection/BlockSiren.java @@ -0,0 +1,49 @@ +package cr0s.warpdrive.block.detection; + +import cr0s.warpdrive.WarpDrive; +import net.minecraft.block.Block; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.block.material.Material; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +public class BlockSiren extends Block implements ITileEntityProvider { + private boolean isRaidSiren; + private float range; + + public BlockSiren(String name, boolean isRaidSiren, float range) { + super(Material.iron); + + this.setBlockName(name); + this.isRaidSiren = isRaidSiren; + this.range = range; + + this.setCreativeTab(WarpDrive.creativeTabWarpDrive); + this.setBlockTextureName("warpdrive:detection/" + name); + } + + @Override + public TileEntity createNewTileEntity(World world, int metadata) { + return new TileEntitySiren(this.unlocalizedName, isRaidSiren, range); + } + + //Silences the siren if the block is destroyed. + //If this fails, the siren will still be stopped when it's invalidated. + @Override + public void onBlockPreDestroy(World world, int x, int y, int z, int meta) { + if (!world.isRemote) { + super.onBlockPreDestroy(world, x, y, z, meta); + return; + } + + TileEntity te = world.getTileEntity(x, y, z); + + if(te != null && te instanceof TileEntitySiren) { + TileEntitySiren siren = (TileEntitySiren) te; + + if (siren.isPlaying()) { + siren.stopSound(); + } + } + } +} diff --git a/src/main/java/cr0s/warpdrive/block/detection/TileEntitySiren.java b/src/main/java/cr0s/warpdrive/block/detection/TileEntitySiren.java new file mode 100644 index 00000000..dec891ee --- /dev/null +++ b/src/main/java/cr0s/warpdrive/block/detection/TileEntitySiren.java @@ -0,0 +1,147 @@ +package cr0s.warpdrive.block.detection; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import cr0s.warpdrive.SirenSound; +import cr0s.warpdrive.WarpDrive; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.ISound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ResourceLocation; + +public class TileEntitySiren extends TileEntity { + private static final int COOLDOWN_LENGTH_TICKS = 5; + + public enum SirenState { + STOPPED, STARTING, STARTED, STOPPING, COOLDOWN; + } + + private SirenState state = SirenState.STOPPED; + private boolean isRaidSiren; + private String name; + private Object sound; + private float range; + private int cooldown = 0; + + /* Because the SirenSound exists only on the client, but the Tile Entity itself + * also exists on the server, we cannot declare a SirenSound object directly, thus + * the sound object is declared as type Object. */ + + public TileEntitySiren(String name, boolean isRaidSiren, float range) { + super(); + + this.name = name; + this.range = range; + this.isRaidSiren = isRaidSiren; + } + + @Override + public void updateEntity() { + super.updateEntity(); + + if (!this.hasWorldObj() || !worldObj.isRemote) return; + if (this.sound == null) this.setSound(); + + //Siren sound logic. + switch (this.state) { + case STOPPED: + if (this.isPowered() && !this.isPlaying()) { + this.startSound(); + } else if (!this.isPowered() && this.isPlaying()) { + //Better safe than sorry. + this.stopSound(); + } + + break; + case STARTING: + if (this.isPlaying()) { + this.state = SirenState.STARTED; + } + + break; + case STARTED: + if (this.isPowered()) { + if (!this.isPlaying()) this.startSound(); + } else { + this.state = SirenState.STOPPING; + } + + break; + case STOPPING: + this.stopSound(); + + this.cooldown = COOLDOWN_LENGTH_TICKS; + this.state = SirenState.COOLDOWN; + + break; + case COOLDOWN: + if (cooldown > 0) { + cooldown--; + } else { + this.cooldown = 0; + this.state = SirenState.STOPPED; + } + + break; + default: + this.stopSound(); + this.state = SirenState.STOPPED; + break; + } + } + + //Stops the siren when the chunk is unloaded. + @Override + public void onChunkUnload() { + if (worldObj.isRemote && this.isPlaying()) this.stopSound(); + super.onChunkUnload(); + } + + //Stops the siren when the TileEntity object is destroyed. + //The siren should already be stopped by this point, but better safe than sorry. + @Override + public void invalidate() { + if (worldObj.isRemote && this.isPlaying()) this.stopSound(); + super.invalidate(); + } + + //Create a new SirenSound object that the siren will use. + @SideOnly(Side.CLIENT) + private void setSound() { + String resource = WarpDrive.MODID + ":siren_" + (isRaidSiren ? "raid" : "industrial"); + this.sound = new SirenSound(new ResourceLocation(resource), this.range, this.xCoord, this.yCoord, this.zCoord); + } + + //Forces the siren to start playing its sound; + @SideOnly(Side.CLIENT) + public void startSound() { + try { + Minecraft.getMinecraft().getSoundHandler().playSound((ISound) sound); + this.state = SirenState.STARTING; + } catch (IllegalArgumentException e) { + /* If soundHandler.playSound() is called before the previous same ISound stops + * from a call to soundHandler.stopSound(), an IllegalArgumentException will + * be thrown. This happens when you spam the redstone trigger. + * We're trying to recover by forcing a stop. */ + + this.state = SirenState.STOPPING; + } + } + + //Forces the siren to stop playing its sound. + @SideOnly(Side.CLIENT) + public void stopSound() { + Minecraft.getMinecraft().getSoundHandler().stopSound((ISound) sound); + } + + //Checks if the siren is currently playing its sound. + @SideOnly(Side.CLIENT) + public boolean isPlaying() { + return Minecraft.getMinecraft().getSoundHandler().isSoundPlaying((ISound) sound); + } + + //Checks if the siren is being powered by redstone. + public boolean isPowered() { + return worldObj.isBlockIndirectlyGettingPowered(this.xCoord, this.yCoord, this.zCoord); + } +} diff --git a/src/main/resources/assets/warpdrive/lang/en_US.lang b/src/main/resources/assets/warpdrive/lang/en_US.lang index 79f81667..222031b6 100644 --- a/src/main/resources/assets/warpdrive/lang/en_US.lang +++ b/src/main/resources/assets/warpdrive/lang/en_US.lang @@ -160,6 +160,11 @@ tile.warpdrive.building.ShipScanner.name=Ship Scanner tile.warpdrive.collection.LaserTreeFarm.name=Laser Tree Farm tile.warpdrive.collection.MiningLaser.name=Mining Laser +tile.siren_industrial.name=Industrial Siren +tile.siren_raid_basic.name=Basic Air Raid Siren +tile.siren_raid_advanced.name=Advanced Air Raid Siren +tile.siren_raid_superior.name=Superior Air Raid Siren + tile.warpdrive.detection.Camera.name=Camera tile.warpdrive.detection.CloakingCoil.name=Cloaking Coil tile.warpdrive.detection.CloakingCore.name=Cloaking Core diff --git a/src/main/resources/assets/warpdrive/sounds.json b/src/main/resources/assets/warpdrive/sounds.json index ed0cb88f..2caf1da4 100644 --- a/src/main/resources/assets/warpdrive/sounds.json +++ b/src/main/resources/assets/warpdrive/sounds.json @@ -7,5 +7,7 @@ "lowlaser": {"category": "master","sounds": [{"name": "lowlaser","stream": false}]}, "cloak": {"category": "master","sounds": [{"name": "cloak","stream": false}]}, "decloak": {"category": "master","sounds": [{"name": "decloak","stream": false}]}, - "projecting": {"category": "master","sounds": [{"name": "projecting","stream": false}]} + "projecting": {"category": "master","sounds": [{"name": "projecting","stream": false}]}, + "siren_raid": {"category": "master","sounds": [{"name": "siren_raid","stream": true}]}, + "siren_industrial": {"category": "master","sounds": [{"name": "siren_industrial","stream": true}]} } diff --git a/src/main/resources/assets/warpdrive/sounds/siren_industrial.ogg b/src/main/resources/assets/warpdrive/sounds/siren_industrial.ogg new file mode 100644 index 00000000..ea845341 Binary files /dev/null and b/src/main/resources/assets/warpdrive/sounds/siren_industrial.ogg differ diff --git a/src/main/resources/assets/warpdrive/sounds/siren_raid.ogg b/src/main/resources/assets/warpdrive/sounds/siren_raid.ogg new file mode 100644 index 00000000..a5d9fcd1 Binary files /dev/null and b/src/main/resources/assets/warpdrive/sounds/siren_raid.ogg differ diff --git a/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_industrial.png b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_industrial.png new file mode 100644 index 00000000..c55aa989 Binary files /dev/null and b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_industrial.png differ diff --git a/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_advanced.png b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_advanced.png new file mode 100644 index 00000000..24f230d0 Binary files /dev/null and b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_advanced.png differ diff --git a/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_basic.png b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_basic.png new file mode 100644 index 00000000..fba6f830 Binary files /dev/null and b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_basic.png differ diff --git a/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_superior.png b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_superior.png new file mode 100644 index 00000000..e1f37cc5 Binary files /dev/null and b/src/main/resources/assets/warpdrive/textures/blocks/detection/siren_raid_superior.png differ