f4884d23ba
Updated README.md+screenshot Fixed the Marx generator not charging properly off an EU supply Fixed the IE version detection. Stupid Java optimized out the field access to the version String since it's static final
786 lines
26 KiB
Java
786 lines
26 KiB
Java
/*
|
|
* This file is part of Industrial Wires.
|
|
* Copyright (C) 2016-2017 malte0811
|
|
*
|
|
* Industrial Wires is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Industrial Wires is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Industrial Wires. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package malte0811.industrialWires.blocks.hv;
|
|
|
|
import blusunrize.immersiveengineering.api.ApiUtils;
|
|
import blusunrize.immersiveengineering.api.IEProperties;
|
|
import blusunrize.immersiveengineering.api.TargetingInfo;
|
|
import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable;
|
|
import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler;
|
|
import blusunrize.immersiveengineering.api.energy.wires.WireType;
|
|
import blusunrize.immersiveengineering.api.energy.wires.redstone.IRedstoneConnector;
|
|
import blusunrize.immersiveengineering.api.energy.wires.redstone.RedstoneWireNetwork;
|
|
import blusunrize.immersiveengineering.common.IEContent;
|
|
import blusunrize.immersiveengineering.common.blocks.BlockTypes_MetalsIE;
|
|
import blusunrize.immersiveengineering.common.blocks.metal.*;
|
|
import blusunrize.immersiveengineering.common.util.Utils;
|
|
import blusunrize.immersiveengineering.common.util.chickenbones.Matrix4;
|
|
import com.elytradev.mirage.event.GatherLightsEvent;
|
|
import com.elytradev.mirage.lighting.Light;
|
|
import malte0811.industrialWires.*;
|
|
import malte0811.industrialWires.blocks.IBlockBoundsIW;
|
|
import malte0811.industrialWires.blocks.ISyncReceiver;
|
|
import malte0811.industrialWires.blocks.IWProperties;
|
|
import malte0811.industrialWires.blocks.TileEntityIWMultiblock;
|
|
import malte0811.industrialWires.hv.MarxOreHandler;
|
|
import malte0811.industrialWires.network.MessageTileSyncIW;
|
|
import malte0811.industrialWires.util.DualEnergyStorage;
|
|
import malte0811.industrialWires.util.MiscUtils;
|
|
import malte0811.industrialWires.wires.IC2Wiretype;
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.block.state.IBlockState;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.entity.item.EntityItem;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.item.EnumDyeColor;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.nbt.NBTTagDouble;
|
|
import net.minecraft.nbt.NBTTagList;
|
|
import net.minecraft.potion.Potion;
|
|
import net.minecraft.potion.PotionEffect;
|
|
import net.minecraft.util.EnumFacing;
|
|
import net.minecraft.util.ITickable;
|
|
import net.minecraft.util.math.AxisAlignedBB;
|
|
import net.minecraft.util.math.BlockPos;
|
|
import net.minecraft.util.math.Vec3d;
|
|
import net.minecraft.util.math.Vec3i;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
import net.minecraftforge.fml.common.Optional;
|
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
|
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
import java.util.*;
|
|
|
|
import static malte0811.industrialWires.blocks.hv.TileEntityMarx.FiringState.FIRE;
|
|
import static malte0811.industrialWires.util.MiscUtils.getOffset;
|
|
import static malte0811.industrialWires.util.MiscUtils.offset;
|
|
import static net.minecraft.item.EnumDyeColor.*;
|
|
|
|
/**
|
|
* Channel: Purpose
|
|
* White: Coarse Vcharge
|
|
* Orange: Coarse bottom cap voltage
|
|
* Magenta: Coarse top voltage
|
|
* LBlue: Firing trigger
|
|
* Yellow: Fine Vcharge
|
|
* Lime: Fine bottom cap voltage
|
|
* Pink: Fine top cap voltage
|
|
*/
|
|
@Mod.EventBusSubscriber
|
|
public class TileEntityMarx extends TileEntityIWMultiblock implements ITickable, ISyncReceiver, IBlockBoundsIW, IImmersiveConnectable, IIC2Connector,
|
|
IRedstoneConnector {
|
|
//Only relevant client-side.
|
|
private static final Set<TileEntityMarx> FIRING_GENERATORS = Collections.newSetFromMap(new WeakHashMap<>());
|
|
|
|
private static final String TYPE = "type";
|
|
private static final String STAGES = "stages";
|
|
private static final String HAS_CONN = "hasConn";
|
|
private static final String CAP_VOLTAGES = "capVoltages";
|
|
private double rcTimeConst;
|
|
private double timeFactor;
|
|
private double timeFactorBottom;
|
|
private final static double CAPACITANCE = 1.6e-6;
|
|
private final static double MAX_VOLTAGE = 250e3;
|
|
|
|
public IWProperties.MarxType type = IWProperties.MarxType.NO_MODEL;
|
|
private int stageCount = 0;
|
|
public FiringState state = FiringState.CHARGING;
|
|
public Discharge dischargeData;
|
|
// Voltage=100*storedEU
|
|
private DualEnergyStorage storage = new DualEnergyStorage(50_000, 32_000);
|
|
private boolean hasConnection;
|
|
private double[] capVoltages;
|
|
private int voltageControl = 0;
|
|
private boolean loaded = false;
|
|
private double leftover;
|
|
private long lastUpdate = -1;
|
|
|
|
TileEntityMarx(EnumFacing facing, IWProperties.MarxType type, boolean mirrored) {
|
|
this.facing = facing;
|
|
this.type = type;
|
|
this.mirrored = mirrored;
|
|
}
|
|
public TileEntityMarx() {}
|
|
|
|
@Override
|
|
public void writeNBT(NBTTagCompound out, boolean updatePacket) {
|
|
super.writeNBT(out, updatePacket);
|
|
MiscUtils.writeConnsToNBT(out, this);
|
|
out.setInteger(TYPE, type.ordinal());
|
|
out.setInteger(STAGES, stageCount);
|
|
out.setBoolean(HAS_CONN, hasConnection);
|
|
storage.writeToNbt(out, ENERGY_TAG);
|
|
NBTTagList voltages = new NBTTagList();
|
|
if (capVoltages != null) {
|
|
for (int i = 0; i < stageCount; i++) {
|
|
voltages.appendTag(new NBTTagDouble(capVoltages[i]));
|
|
}
|
|
}
|
|
out.setTag(CAP_VOLTAGES, voltages);
|
|
}
|
|
|
|
@Override
|
|
public void readNBT(NBTTagCompound in, boolean updatePacket) {
|
|
super.readNBT(in, updatePacket);
|
|
MiscUtils.loadConnsFromNBT(in, this);
|
|
type = IWProperties.MarxType.values()[in.getInteger(TYPE)];
|
|
setStageCount(in.getInteger(STAGES));
|
|
NBTTagList voltages = in.getTagList(CAP_VOLTAGES, 6);//DOUBLE
|
|
capVoltages = new double[stageCount];
|
|
for (int i = 0;i<stageCount;i++) {
|
|
capVoltages[i] = voltages.getDoubleAt(i);
|
|
}
|
|
storage.readFromNBT(in.getCompoundTag(ENERGY_TAG));
|
|
hasConnection = in.getBoolean(HAS_CONN);
|
|
collisionAabb = null;
|
|
renderAabb = null;
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
protected BlockPos getOrigin() {
|
|
return getPos().subtract(offset).offset(facing.getOpposite(), 3);
|
|
}
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
public IBlockState getOriginalBlock() {
|
|
int forward = getForward();
|
|
int right = getRight();
|
|
int up = offset.getY();
|
|
if (forward==0) {
|
|
return IEContent.blockMetalDevice0.getDefaultState().withProperty(IEContent.blockMetalDevice0.property, BlockTypes_MetalDevice0.CAPACITOR_HV);
|
|
} else if (forward==-1) {
|
|
return IEContent.blockConnectors.getDefaultState().withProperty(IEContent.blockConnectors.property, BlockTypes_Connector.RELAY_HV)
|
|
.withProperty(IEProperties.FACING_ALL, facing);
|
|
} else if (forward==4&&up==0&&right==1) {
|
|
return IEContent.blockStorage.getDefaultState().withProperty(IEContent.blockStorage.property, BlockTypes_MetalsIE.STEEL);
|
|
} else if (forward>0) {
|
|
if ((right==0&&up==0)||(right==1&&up==stageCount-1)) {
|
|
return IEContent.blockMetalDecoration1.getDefaultState().withProperty(IEContent.blockMetalDecoration1.property, BlockTypes_MetalDecoration1.STEEL_FENCE);
|
|
} else {
|
|
return IEContent.blockMetalDecoration2.getDefaultState().withProperty(IEContent.blockMetalDecoration2.property, BlockTypes_MetalDecoration2.STEEL_WALLMOUNT)
|
|
.withProperty(IEProperties.INT_4, 1-right).withProperty(IEProperties.FACING_ALL, facing.getOpposite());
|
|
}
|
|
} else if (forward==-2) {
|
|
return IEContent.blockMetalDecoration0.getDefaultState().withProperty(IEContent.blockMetalDecoration0.property, BlockTypes_MetalDecoration0.HEAVY_ENGINEERING);
|
|
} else if (right==0) {
|
|
return IEContent.blockConnectors.getDefaultState().withProperty(IEContent.blockConnectors.property, BlockTypes_Connector.CONNECTOR_REDSTONE)
|
|
.withProperty(IEProperties.FACING_ALL, facing);
|
|
} else {
|
|
return IEContent.blockConnectors.getDefaultState().withProperty(IEContent.blockConnectors.property, BlockTypes_Connector.CONNECTOR_HV)
|
|
.withProperty(IEProperties.FACING_ALL, facing);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void update() {
|
|
ApiUtils.checkForNeedlessTicking(this);
|
|
FIRING_GENERATORS.remove(this);
|
|
switch (state) {
|
|
case NEXT_TICK:
|
|
if (world.isRemote) {
|
|
FIRING_GENERATORS.add(this);
|
|
IndustrialWires.proxy.playMarxBang(this, getMiddle(), (float) getNormedEnergy(dischargeData.energy));
|
|
} else {
|
|
fire();
|
|
}
|
|
state = FIRE;
|
|
break;
|
|
case FIRE:
|
|
state = FiringState.CHARGING;
|
|
break;
|
|
}
|
|
if (!world.isRemote&&type== IWProperties.MarxType.BOTTOM) {
|
|
if (capVoltages == null || capVoltages.length != stageCount) {
|
|
capVoltages = new double[stageCount];
|
|
}
|
|
final double oldTopVoltage = capVoltages[stageCount - 1];
|
|
final double oldBottomVoltage = capVoltages[0];
|
|
for (int i = stageCount - 1; i > 0; i--) {
|
|
double oldVoltage = capVoltages[i];
|
|
double u0 = capVoltages[i - 1];
|
|
capVoltages[i] = u0 - (u0 - oldVoltage) * timeFactor;
|
|
capVoltages[i - 1] -= capVoltages[i] - oldVoltage;
|
|
}
|
|
//charge bottom cap from storage
|
|
double setVoltage = MAX_VOLTAGE * voltageControl / 255F;
|
|
double u0 = Math.min(setVoltage, 100 * storage.getEnergyStoredEU());
|
|
if (u0 < 0) {
|
|
u0 = 0;
|
|
}
|
|
if (u0 < capVoltages[0] && setVoltage > capVoltages[0]) {
|
|
u0 = capVoltages[0];
|
|
}
|
|
double tmp = u0 - (u0 - oldBottomVoltage) * timeFactorBottom;
|
|
double energyUsed = .5 * (tmp * tmp - oldBottomVoltage * oldBottomVoltage) * CAPACITANCE;
|
|
if (energyUsed > 0 && storage.extractEU(energyUsed, false) == energyUsed) {// energyUsed can be negative when discharging the caps
|
|
storage.extractEU(energyUsed, true);
|
|
capVoltages[0] = tmp;
|
|
} else if (energyUsed <= 0) {
|
|
capVoltages[0] = tmp;
|
|
}
|
|
int delta = (int) (lastUpdate+15-world.getTotalWorldTime());
|
|
if (Math.abs(getRSSignalFromVoltage(oldBottomVoltage)-getRSSignalFromVoltage(capVoltages[0]))>delta) {
|
|
net.updateValues();
|
|
} else if (Math.abs(getRSSignalFromVoltage(oldTopVoltage)-getRSSignalFromVoltage(capVoltages[stageCount-1]))>delta) {
|
|
net.updateValues();
|
|
}
|
|
if (capVoltages[0] > MAX_VOLTAGE * 14.5 / 15) {
|
|
state = FiringState.NEXT_TICK;
|
|
}
|
|
}
|
|
leftover = storage.getMaxInputIF();
|
|
}
|
|
|
|
private void fire() {
|
|
if (!world.isRemote) {
|
|
//calculate energy
|
|
double energyStored = 0;
|
|
boolean failed = capVoltages[0]<MAX_VOLTAGE*.5;
|
|
double totalVoltage = 0;
|
|
for (int i = 0;i<stageCount;i++) {
|
|
energyStored += .5*capVoltages[i]*capVoltages[i]*CAPACITANCE;
|
|
totalVoltage += capVoltages[i];
|
|
capVoltages[i] = 0;
|
|
}
|
|
if (totalVoltage<.1*MAX_VOLTAGE*stageCount) {
|
|
return;
|
|
}
|
|
failed |= totalVoltage<MAX_VOLTAGE*.3*stageCount;
|
|
net.updateValues();
|
|
NBTTagCompound data = new NBTTagCompound();
|
|
if (failed) {
|
|
energyStored = -energyStored;
|
|
} else {
|
|
int seed = Utils.RAND.nextInt();
|
|
genDischarge((float) energyStored, seed);
|
|
data.setInteger("randSeed", seed);
|
|
handleEntities(energyStored);
|
|
handleOreProcessing(energyStored);//After entities to prevent killing the newly dropped items
|
|
}
|
|
data.setDouble("energy", energyStored);
|
|
IndustrialWires.packetHandler.sendToDimension(new MessageTileSyncIW(this, data), world.provider.getDimension());
|
|
}
|
|
}
|
|
|
|
private void handleOreProcessing(double energyStored) {
|
|
BlockPos bottom = getBottomElectrode();
|
|
Vec3d origin = new Vec3d(bottom).addVector(.5, 1, .5);
|
|
Set<BlockPos> toBreak = new HashSet<>(stageCount-2);
|
|
int ores = 0;
|
|
for (int i = 1;i<stageCount-1;i++) {
|
|
BlockPos blockHere = bottom.up(i);
|
|
if (!world.isAirBlock(blockHere) && canBreak(blockHere)) {
|
|
toBreak.add(blockHere);
|
|
ores++;
|
|
}
|
|
}
|
|
if (ores>0) {
|
|
double energyPerOre = energyStored / ores;
|
|
for (BlockPos here:toBreak) {
|
|
IBlockState state = world.getBlockState(here);
|
|
if (state.getBlockHardness(world, here) < 0) {
|
|
continue;
|
|
}
|
|
if (!world.isAirBlock(here)) {
|
|
ItemStack[] out = MarxOreHandler.getYield(world, here, energyPerOre);
|
|
for (ItemStack stack : out) {
|
|
EntityItem item = new EntityItem(world, here.getX() + .5, here.getY() + .5, here.getZ() + .5, stack);
|
|
final double maxMotion = .3;
|
|
item.motionX = 2 * maxMotion * (Utils.RAND.nextDouble() - .5);
|
|
item.motionY = 2 * maxMotion * (Utils.RAND.nextDouble() - .5);
|
|
item.motionZ = 2 * maxMotion * (Utils.RAND.nextDouble() - .5);
|
|
world.spawnEntity(item);
|
|
}
|
|
world.setBlockToAir(here);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void handleEntities(double energyStored) {
|
|
double damageDistSqu = Math.sqrt(energyStored/50e3);
|
|
double tinnitusDistSqu = Math.sqrt(energyStored)/50;
|
|
Vec3d v0 = getMiddle();
|
|
AxisAlignedBB aabb = new AxisAlignedBB(v0.x, v0.y, v0.z, v0.x, v0.y, v0.z);
|
|
aabb = aabb.grow(0, stageCount/2-1,0);
|
|
aabb = aabb.grow(tinnitusDistSqu);
|
|
List<Entity> fools = world.getEntitiesWithinAABB(Entity.class, aabb);
|
|
damageDistSqu *= damageDistSqu;
|
|
tinnitusDistSqu *= tinnitusDistSqu;
|
|
if (IWConfig.HVStuff.marxSoundDamage == 2) {
|
|
damageDistSqu = tinnitusDistSqu;
|
|
tinnitusDistSqu = -1;
|
|
}
|
|
for (Entity entity : fools) {
|
|
double y;
|
|
if (entity.posY<pos.getY()+1) {
|
|
y = pos.getY()+1;
|
|
} else if (entity.posY>pos.getY()+stageCount-2) {
|
|
y = pos.getY()+stageCount-2;
|
|
} else {
|
|
y = entity.posY;
|
|
}
|
|
double distSqu = entity.getDistanceSq(v0.x, y, v0.z);
|
|
if (distSqu<=damageDistSqu) {
|
|
float dmg = (float) (10*stageCount*(1-distSqu/damageDistSqu));
|
|
entity.attackEntityFrom(IWDamageSources.dmg_marx, dmg);
|
|
}
|
|
if (distSqu<=tinnitusDistSqu && entity instanceof EntityPlayer) {
|
|
ItemStack helmet = ((EntityPlayer) entity).inventory.armorInventory.get(3);
|
|
boolean earMuff = helmet.getItem()==IEContent.itemEarmuffs;
|
|
if (!earMuff&&helmet.hasTagCompound()) {
|
|
earMuff = helmet.getTagCompound().hasKey("IE:Earmuffs");
|
|
}
|
|
if (!earMuff) {
|
|
double multipl = Math.min(5, Math.sqrt(stageCount));
|
|
int duration = (int) (20*20*(1+multipl*(1-distSqu/tinnitusDistSqu)));
|
|
if (IWConfig.HVStuff.marxSoundDamage == 0) {
|
|
((EntityPlayer) entity).addPotionEffect(new PotionEffect(IWPotions.tinnitus, duration));
|
|
} else {
|
|
((EntityPlayer) entity).addPotionEffect(new PotionEffect(Potion.getPotionFromResourceLocation("nausea"), duration));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//checks whether the given pos can't be broken because it is part of the generator
|
|
private boolean canBreak(BlockPos pos) {
|
|
BlockPos dischargePos = offset(this.pos, facing, mirrored, 1, 3, 0);
|
|
Vec3i offset = getOffset(dischargePos, facing, mirrored, pos);
|
|
if (offset.getZ()<1||offset.getZ()>=stageCount-1) {
|
|
return false;
|
|
}
|
|
return Math.abs(offset.getX())>Math.abs(offset.getY());
|
|
}
|
|
|
|
private int getRSSignalFromVoltage(double voltage) {
|
|
return (int) (Math.round(255 * voltage / MAX_VOLTAGE)&0xff);
|
|
}
|
|
|
|
@Override
|
|
public Vec3i getSize() {
|
|
return new Vec3i(stageCount, 8, 2);
|
|
}
|
|
|
|
@Override
|
|
public void onSync(NBTTagCompound nbt) {
|
|
float energy = nbt.getFloat("energy");
|
|
if (energy>0) {
|
|
genDischarge(energy, nbt.getInteger("randSeed"));
|
|
} else {
|
|
if (dischargeData==null) {
|
|
dischargeData = new Discharge(stageCount);
|
|
}
|
|
dischargeData.energy = energy;
|
|
}
|
|
state = FiringState.NEXT_TICK;
|
|
}
|
|
|
|
private void genDischarge(float energy, int seed) {
|
|
if (dischargeData==null) {
|
|
dischargeData = new Discharge(stageCount);
|
|
}
|
|
dischargeData.energy = energy;
|
|
dischargeData.diameter = (float) getNormedEnergy(dischargeData.energy);
|
|
dischargeData.genMarxPoint(seed);
|
|
}
|
|
|
|
private double getNormedEnergy(double total) {
|
|
return total*2/(stageCount*MAX_VOLTAGE*MAX_VOLTAGE*CAPACITANCE);
|
|
}
|
|
|
|
private AxisAlignedBB renderAabb = null;
|
|
@Nonnull
|
|
@Override
|
|
public AxisAlignedBB getRenderBoundingBox() {
|
|
if (renderAabb ==null) {
|
|
if (type== IWProperties.MarxType.BOTTOM) {
|
|
renderAabb = new AxisAlignedBB(pos,
|
|
offset(pos, facing, mirrored, 2, 4, stageCount));
|
|
} else {
|
|
renderAabb = new AxisAlignedBB(pos, pos);
|
|
}
|
|
}
|
|
return renderAabb;
|
|
}
|
|
private AxisAlignedBB collisionAabb = null;
|
|
@Override
|
|
public AxisAlignedBB getBoundingBox() {
|
|
if (collisionAabb ==null) {
|
|
int forward = getForward();
|
|
int right = getRight();
|
|
int up = offset.getY();
|
|
AxisAlignedBB ret = Block.FULL_BLOCK_AABB;
|
|
switch (forward) {
|
|
case -3://IO
|
|
if (right == 1) {
|
|
ret = new AxisAlignedBB(5 / 16D, 5 / 16D, .25, 11 / 16D, 11 / 16D, 1);
|
|
} else {
|
|
ret = new AxisAlignedBB(5 / 16D, 5 / 16D, 7 / 16D, 11 / 16D, 11 / 16D, 1);
|
|
}
|
|
break;
|
|
case -1://charging resistors
|
|
if (up == 0) {
|
|
ret = new AxisAlignedBB(.375, 0, 0, .625, 1, 1);
|
|
} else if (up == stageCount - 1) {
|
|
ret = new AxisAlignedBB(.375, 0, 9 / 16D, .625, 5 / 16D, 1);
|
|
} else {
|
|
ret = new AxisAlignedBB(.375, 0, 9 / 16D, .625, 1, 1);
|
|
}
|
|
break;
|
|
case 1://spark gaps
|
|
if (right == 0) {
|
|
if (up!=0) {
|
|
ret = new AxisAlignedBB(0, 0, 0, 9 / 16D, up == stageCount - 1 ? .5 : 1, 7 / 16D);
|
|
} else {
|
|
ret = new AxisAlignedBB(7/16D, 0, 0, 9/16D, 5/16D, 1);
|
|
}
|
|
} else {
|
|
if (stageCount - 1 == up) {
|
|
ret = new AxisAlignedBB(7 / 16D, 3 / 16D, 0, 9 / 16D, 5 / 16D, 1);
|
|
} else {
|
|
ret = new AxisAlignedBB(7 / 16D, 0, 0, 1, 1, 7 / 16D);
|
|
}
|
|
}
|
|
break;
|
|
case -2://Controller
|
|
break;
|
|
case 0://Caps
|
|
if (up == stageCount - 1) {
|
|
ret = new AxisAlignedBB(0, 0, 0, 1, .5, 1);
|
|
}
|
|
break;
|
|
default:
|
|
if (right == 0) {
|
|
if (forward<4) {
|
|
ret = new AxisAlignedBB(7/16D, 0, 0, 9/16D, 5/16D, 1);
|
|
} else {
|
|
ret = new AxisAlignedBB(0, 0, 0, 9/16D, 5/16D, 9/16D);
|
|
}
|
|
} else {
|
|
if (up==0) {
|
|
ret = Block.FULL_BLOCK_AABB;
|
|
} else if (forward < 4) {
|
|
ret = new AxisAlignedBB(7 / 16D, 3 / 16D, 0, 9 / 16D, 5 / 16D, 1);
|
|
} else {
|
|
ret = new AxisAlignedBB(6 / 16D, 1 / 16D, 0, 10 / 16D, 5 / 16D, 10 / 16D);
|
|
}
|
|
}
|
|
}
|
|
collisionAabb = MiscUtils.apply(getBaseTransform(), ret);
|
|
}
|
|
return collisionAabb;
|
|
}
|
|
|
|
private Matrix4 getBaseTransform() {
|
|
Matrix4 transform = new Matrix4();
|
|
transform.translate(.5, 0, .5);
|
|
transform.rotate(-facing.getHorizontalAngle() * Math.PI / 180, 0, 1, 0);
|
|
if (mirrored) {
|
|
transform.scale(-1, 1, 1);
|
|
}
|
|
transform.translate(-.5, 0, -.5);
|
|
return transform;
|
|
}
|
|
//WIRE STUFF
|
|
@Override
|
|
public boolean canConnect() {
|
|
return getForward()==-3;
|
|
}
|
|
|
|
@Override
|
|
public boolean isEnergyOutput() {
|
|
return getForward()==-3&&getRight()==1;
|
|
}
|
|
|
|
@Override
|
|
public int outputEnergy(int amount, boolean simulate, int energyType) {
|
|
TileEntityMarx master = master(this);
|
|
if (master!=null && amount>0) {
|
|
double ret = master.storage.insertIF(amount, master.leftover, !simulate);
|
|
master.leftover -= ret;
|
|
return (int) ret;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public double insertEnergy(double eu, boolean simulate) {
|
|
TileEntityMarx master = master(this);
|
|
if (master!=null) {
|
|
double ret = master.storage.insertEU(eu, master.leftover, !simulate);
|
|
master.leftover -= ret;
|
|
return eu-ret;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public BlockPos getConnectionMaster(@Nullable WireType cableType, TargetingInfo target) {
|
|
return pos;
|
|
}
|
|
|
|
@Override
|
|
public boolean canConnectCable(WireType cableType, TargetingInfo target) {
|
|
if (hasConnection) {
|
|
return false;
|
|
}
|
|
if (getRight()==0) {
|
|
return cableType==WireType.REDSTONE;
|
|
} else {
|
|
return cableType==WireType.STEEL||cableType== IC2Wiretype.IC2_TYPES[3];
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void connectCable(WireType cableType, TargetingInfo target, IImmersiveConnectable other) {
|
|
hasConnection = true;
|
|
}
|
|
|
|
@Override
|
|
public WireType getCableLimiter(TargetingInfo target) {
|
|
return getRight()==0?WireType.REDSTONE:IC2Wiretype.IC2_TYPES[3];
|
|
}
|
|
|
|
@Override
|
|
public boolean allowEnergyToPass(ImmersiveNetHandler.Connection con) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void onEnergyPassthrough(int amount) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void removeCable(ImmersiveNetHandler.Connection connection) {
|
|
hasConnection = false;
|
|
if(world != null)
|
|
{
|
|
IBlockState state = world.getBlockState(pos);
|
|
world.notifyBlockUpdate(pos, state,state, 3);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Vec3d getRaytraceOffset(IImmersiveConnectable link) {
|
|
Matrix4 transf = getBaseTransform();
|
|
if (getRight()==0) {
|
|
return transf.apply(new Vec3d(.5, .5, 7/16D));
|
|
} else {
|
|
return transf.apply(new Vec3d(.5, .5, 4/16D));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Vec3d getConnectionOffset(ImmersiveNetHandler.Connection con) {
|
|
return getRaytraceOffset(null);
|
|
}
|
|
|
|
|
|
@Override
|
|
public void validate()
|
|
{
|
|
super.validate();
|
|
ImmersiveNetHandler.INSTANCE.resetCachedIndirectConnections();
|
|
}
|
|
|
|
@Override
|
|
public void invalidate()
|
|
{
|
|
super.invalidate();
|
|
if (world.isRemote)
|
|
ImmersiveNetHandler.INSTANCE.clearConnectionsOriginatingFrom(pos, world);
|
|
}
|
|
|
|
@Override
|
|
public boolean receiveClientEvent(int id, int type) {
|
|
return MiscUtils.handleUpdate(id, pos, world)||super.receiveClientEvent(id, type);
|
|
}
|
|
|
|
// Redstone wire stuff
|
|
private RedstoneWireNetwork net = new RedstoneWireNetwork();
|
|
@Override
|
|
public void setNetwork(RedstoneWireNetwork net) {
|
|
masterOr(this, this).net = net;
|
|
}
|
|
|
|
@Override
|
|
public RedstoneWireNetwork getNetwork() {
|
|
TileEntityMarx master = masterOr(this, this);
|
|
if (!loaded) {
|
|
master.net.add(this);
|
|
loaded = true;
|
|
}
|
|
return master.net;
|
|
}
|
|
|
|
@Override
|
|
public void onChange() {
|
|
TileEntityMarx master = masterOr(this, this);
|
|
master.voltageControl = (master.net.channelValues[WHITE.getMetadata()]<<4)|master.net.channelValues[YELLOW.getMetadata()];
|
|
if (master.net.channelValues[LIGHT_BLUE.getMetadata()]!=0) {
|
|
master.tryTriggeredDischarge();
|
|
}
|
|
master.lastUpdate = world.getTotalWorldTime();
|
|
}
|
|
private void tryTriggeredDischarge() {
|
|
state = FiringState.NEXT_TICK;
|
|
}
|
|
|
|
@Override
|
|
public World getConnectorWorld() {
|
|
return world;
|
|
}
|
|
|
|
@Override
|
|
public void updateInput(byte[] signals) {
|
|
TileEntityMarx master = masterOr(this, this);
|
|
if (master.capVoltages!=null&&master.capVoltages.length==stageCount) {
|
|
int signalTop = getRSSignalFromVoltage(master.capVoltages[stageCount-1]);
|
|
int signalBottom = getRSSignalFromVoltage(master.capVoltages[0]);
|
|
setSignal(ORANGE, (signalBottom>>4)&0xf, signals);
|
|
setSignal(MAGENTA, (signalTop>>4)&0xf, signals);
|
|
setSignal(LIME, signalBottom&0xf, signals);
|
|
setSignal(PINK, signalTop&0xf, signals);
|
|
}
|
|
}
|
|
|
|
private void setSignal(EnumDyeColor channel, int value, byte[] signals) {
|
|
signals[channel.getMetadata()] = (byte) Math.max(value, signals[channel.getMetadata()]);
|
|
}
|
|
|
|
public void setStageCount(int stageCount) {
|
|
this.stageCount = stageCount;
|
|
rcTimeConst = 5D/stageCount;
|
|
timeFactor = Math.exp(-1/(20*rcTimeConst));
|
|
timeFactorBottom = Math.exp(-1 / (20 * rcTimeConst * 2 / 3));
|
|
collisionAabb = null;
|
|
renderAabb = null;
|
|
}
|
|
|
|
public int getStageCount() {
|
|
return stageCount;
|
|
}
|
|
|
|
private Vec3d getMiddle() {
|
|
double middleY = pos.getY()+(stageCount)/2D;
|
|
Vec3i electrodXZ = getBottomElectrode();
|
|
return new Vec3d(electrodXZ.getX()+.5, middleY, electrodXZ.getZ()+.5);
|
|
}
|
|
|
|
private BlockPos getBottomElectrode() {
|
|
return offset(pos, facing, mirrored, 1, 4, 0);
|
|
}
|
|
|
|
@Optional.Method(modid="mirage")
|
|
@SubscribeEvent
|
|
public static void gatherLights(GatherLightsEvent event) {
|
|
for (TileEntityMarx te:FIRING_GENERATORS) {
|
|
Vec3d origin = te.getMiddle().subtract(0, .5*te.stageCount-1,0);
|
|
Light.Builder builder = Light.builder()
|
|
.color(1, 1, 1)
|
|
.radius(5);
|
|
List<Light> toAdd = new ArrayList<>(te.stageCount*2-3);
|
|
if (te.dischargeData!=null&&te.dischargeData.energy>0) {
|
|
toAdd.add(builder.pos(origin.addVector(0, 0, 0)).build());
|
|
toAdd.add(builder.pos(origin.addVector(0, te.stageCount/2, 0)).build());
|
|
toAdd.add(builder.pos(origin.addVector(0, te.stageCount-2, 0)).build());
|
|
}
|
|
origin = new Vec3d(offset(te.pos, te.facing, te.mirrored, 1, 0, 0))
|
|
.addVector(0, .75, 0)
|
|
.add(new Vec3d(te.facing.getDirectionVec()).scale(.25));
|
|
builder.radius(.5F);
|
|
for (int i = 0;i<te.stageCount-1;i+=te.stageCount/5) {
|
|
toAdd.add(builder.pos(origin.addVector(0, i, 0)).build());
|
|
}
|
|
event.getLightList().addAll(toAdd);
|
|
}
|
|
}
|
|
|
|
public enum FiringState {
|
|
CHARGING,
|
|
NEXT_TICK,
|
|
FIRE
|
|
}
|
|
|
|
public static final class Discharge {
|
|
public float energy;
|
|
public Vec3d[] vertices;
|
|
public float diameter = .25F;
|
|
final int stageCount;
|
|
Discharge(int stages) {
|
|
stageCount = stages;
|
|
int count = stages/5+1;
|
|
vertices = new Vec3d[2*count];
|
|
vertices[0] = new Vec3d(0, -.5F, 0);
|
|
for (int i = 1;i<vertices.length;i++) {
|
|
vertices[i] = new Vec3d(0, 0, 0);
|
|
}
|
|
vertices[vertices.length-1] = new Vec3d(0, stageCount-1.9375F, 0);
|
|
|
|
}
|
|
|
|
// Meant to be const
|
|
private final Vec3d side = new Vec3d(0, 0, 1);
|
|
private Matrix4 transform = new Matrix4();
|
|
|
|
void genMarxPoint(int randSeed) {
|
|
genMarxPoint(0, vertices.length-1, new Random(randSeed));
|
|
}
|
|
/**
|
|
* @param min The first point of the discharge section to be generated. has to be pre-populated
|
|
* @param max The last point of the discharge section to be generated. has to be pre-populated
|
|
*/
|
|
void genMarxPoint(int min, int max, Random rand) {
|
|
int toGenerate = (min+max)/2;
|
|
Vec3d diff = vertices[max].subtract(vertices[min]);
|
|
Vec3d v0 = diff.crossProduct(side);
|
|
transform.setIdentity();
|
|
double diffLength = diff.lengthVector();
|
|
double noise = Math.sqrt(diffLength)*rand.nextDouble()*1/(1+Math.abs(stageCount/2.0-toGenerate))*.75;
|
|
if ((max-min)%2==1) {
|
|
noise *= (toGenerate-min)/(double)(max-min);
|
|
}
|
|
v0 = v0.scale((float) (noise/v0.lengthVector()));
|
|
diff = diff.scale(1/diffLength);
|
|
transform.rotate(Math.PI*2*rand.nextDouble(), diff.x, diff.y, diff.z);
|
|
Vec3d center = vertices[max].add(vertices[min]).scale(.5);
|
|
vertices[toGenerate] = transform.apply(v0);
|
|
vertices[toGenerate] = center.add(vertices[toGenerate]);
|
|
|
|
if (toGenerate-min>1) {
|
|
genMarxPoint(min, toGenerate, rand);
|
|
}
|
|
if (max-toGenerate>1) {
|
|
genMarxPoint(toGenerate, max, rand);
|
|
}
|
|
}
|
|
}
|
|
}
|