Skip to content

Instantly share code, notes, and snippets.

@coldblade2000
Last active August 4, 2019 12:26
Show Gist options
  • Save coldblade2000/59ba2acf7420bb201df7862fea303bf0 to your computer and use it in GitHub Desktop.
Save coldblade2000/59ba2acf7420bb201df7862fea303bf0 to your computer and use it in GitHub Desktop.
Focus on shootEach for EntityDrivable.
package com.flansmod.common.driveables;
import java.util.ArrayList;
import java.util.List;
import com.flansmod.common.network.PacketShotData;
import io.netty.buffer.ByteBuf;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.GameType;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.fml.common.network.ByteBufUtils;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import com.flansmod.api.IControllable;
import com.flansmod.api.IExplodeable;
import com.flansmod.client.EntityCamera;
import com.flansmod.client.FlansModClient;
import com.flansmod.client.debug.EntityDebugDot;
import com.flansmod.client.debug.EntityDebugVector;
import com.flansmod.common.FlansMod;
import com.flansmod.common.RotatedAxes;
import com.flansmod.common.driveables.DriveableType.ParticleEmitter;
import com.flansmod.common.guns.BulletType;
import com.flansmod.common.guns.EntityShootable;
import com.flansmod.common.guns.EnumFireMode;
import com.flansmod.common.guns.GunType;
import com.flansmod.common.guns.InventoryHelper;
import com.flansmod.common.guns.ItemBullet;
import com.flansmod.common.guns.ItemGun;
import com.flansmod.common.guns.ItemShootable;
import com.flansmod.common.guns.ShootableType;
import com.flansmod.common.guns.ShotData;
import com.flansmod.common.guns.ShotData.InstantShotData;
import com.flansmod.common.guns.ShotData.SpawnEntityShotData;
import com.flansmod.common.guns.raytracing.FlansModRaytracer;
import com.flansmod.common.guns.raytracing.FlansModRaytracer.BulletHit;
import com.flansmod.common.guns.raytracing.FlansModRaytracer.DriveableHit;
import com.flansmod.common.network.PacketDriveableDamage;
import com.flansmod.common.network.PacketDriveableKeyHeld;
import com.flansmod.common.network.PacketPlaySound;
import com.flansmod.common.parts.EnumPartCategory;
import com.flansmod.common.parts.ItemPart;
import com.flansmod.common.parts.PartType;
import com.flansmod.common.teams.TeamsManager;
import com.flansmod.common.vector.Vector3f;
import static com.flansmod.common.util.BlockUtil.destroyBlock;
//import cofh.api.energy.IEnergyContainerItem;
public abstract class EntityDriveable extends Entity implements IControllable, IExplodeable, IEntityAdditionalSpawnData
{
public boolean syncFromServer = true;
/**
* Ticks since last server update. Use to smoothly transition to new position
*/
public int serverPositionTransitionTicker;
/**
* Server side position, as synced by PacketVehicleControl packets
*/
public double serverPosX, serverPosY, serverPosZ;
/**
* Server side rotation, as synced by PacketVehicleControl packets
*/
public double serverYaw, serverPitch, serverRoll;
/**
* The driveable data which contains the inventory, the engine and the fuel
*/
public DriveableData driveableData;
/**
* The shortName of the driveable type, used to obtain said type
*/
public String driveableType;
/**
* The throttle, in the range -1, 1 is multiplied by the maxThrottle (or maxNegativeThrottle) from the plane type to obtain the thrust
*/
public float throttle;
/**
* The wheels on this plane
*/
public EntityWheel[] wheels;
public boolean fuelling;
/**
* Extra prevRoation field for smoothness in all 3 rotational axes
*/
public float prevRotationRoll;
/**
* Angular velocity
*/
public Vector3f angularVelocity = new Vector3f(0F, 0F, 0F);
/**
* Whether each mouse button is held
*/
public boolean leftMouseHeld = false, rightMouseHeld = false;
/**
* Shoot delay variables
*/
public float shootDelayPrimary, shootDelaySecondary;
/**
* Minigun speed variables
*/
public float minigunSpeedPrimary, minigunSpeedSecondary;
/**
* Current gun variables for alternating weapons.
*/
public int currentGunPrimary, currentGunSecondary;
/**
* Angle of harvester aesthetic piece
*/
public float harvesterAngle;
public RotatedAxes prevAxes;
public RotatedAxes axes;
private EntitySeat[] seats;
/**
* Until this is true, just look for seat and wheel connections
*/
protected boolean readyForUpdates = false;
private float yOffset;
@SideOnly(Side.CLIENT)
public EntityLivingBase camera;
private int[] emitterTimers;
public int animCount = 0;
public int animFrame = 0;
public EntityDriveable(World world)
{
super(world);
axes = new RotatedAxes();
prevAxes = new RotatedAxes();
preventEntitySpawning = true;
setSize(1F, 1F);
yOffset = 6F / 16F;
ignoreFrustumCheck = true;
}
public EntityDriveable(World world, DriveableType t, DriveableData d)
{
this(world);
driveableType = t.shortName;
driveableData = d;
}
protected void initType(DriveableType type, boolean firstSpawn, boolean clientSide)
{
seats = new EntitySeat[type.numPassengers + 1];
wheels = new EntityWheel[type.wheelPositions.length];
if(!clientSide && firstSpawn)
{
for(int i = 0; i < type.numPassengers + 1; i++)
{
seats[i] = new EntitySeat(world, this, i);
world.spawnEntity(seats[i]);
seats[i].startRiding(this);
}
for(int i = 0; i < wheels.length; i++)
{
wheels[i] = new EntityWheel(world, this, i);
world.spawnEntity(wheels[i]);
wheels[i].startRiding(this);
}
}
stepHeight = type.wheelStepHeight;
yOffset = type.yOffset;
emitterTimers = new int[type.emitters.size()];
for(int i = 0; i < type.emitters.size(); i++)
{
emitterTimers[i] = rand.nextInt(type.emitters.get(i).emitRate);
}
//Register Plane to Radar on Spawning
//if(type.onRadar == true)
// RadarRegistry.register(this);
}
@SideOnly(Side.CLIENT)
@Override
public boolean isInRangeToRender3d(double x, double y, double z)
{
double dX = this.posX - x;
double dY = this.posY - y;
double dZ = this.posZ - z;
double distSq = dX * dX + dY * dY + dZ * dZ;
double maxDist = 200.0D * getRenderDistanceWeight();
return distSq < maxDist * maxDist;
}
@Override
protected void writeEntityToNBT(NBTTagCompound tag)
{
driveableData.writeToNBT(tag);
tag.setString("Type", driveableType);
tag.setFloat("RotationYaw", axes.getYaw());
tag.setFloat("RotationPitch", axes.getPitch());
tag.setFloat("RotationRoll", axes.getRoll());
}
@Override
protected void readEntityFromNBT(NBTTagCompound tag)
{
driveableType = tag.getString("Type");
driveableData = new DriveableData(tag);
initType(DriveableType.getDriveable(driveableType), false, false);
prevRotationYaw = tag.getFloat("RotationYaw");
prevRotationPitch = tag.getFloat("RotationPitch");
prevRotationRoll = tag.getFloat("RotationRoll");
axes = new RotatedAxes(prevRotationYaw, prevRotationPitch, prevRotationRoll);
}
@Override
public void writeSpawnData(ByteBuf data)
{
ByteBufUtils.writeUTF8String(data, driveableType);
NBTTagCompound tag = new NBTTagCompound();
driveableData.writeToNBT(tag);
ByteBufUtils.writeTag(data, tag);
data.writeFloat(axes.getYaw());
data.writeFloat(axes.getPitch());
data.writeFloat(axes.getRoll());
//Write damage
for(EnumDriveablePart ep : EnumDriveablePart.values())
{
DriveablePart part = getDriveableData().parts.get(ep);
data.writeShort((short)part.health);
data.writeBoolean(part.onFire);
}
}
@Override
public void readSpawnData(ByteBuf data)
{
try
{
driveableType = ByteBufUtils.readUTF8String(data);
driveableData = new DriveableData(ByteBufUtils.readTag(data));
initType(getDriveableType(), false, true);
axes.setAngles(data.readFloat(), data.readFloat(), data.readFloat());
prevRotationYaw = axes.getYaw();
prevRotationPitch = axes.getPitch();
prevRotationRoll = axes.getRoll();
//Read damage
for(EnumDriveablePart ep : EnumDriveablePart.values())
{
DriveablePart part = getDriveableData().parts.get(ep);
part.health = data.readShort();
part.onFire = data.readBoolean();
}
}
catch(Exception e)
{
FlansMod.log.error("Failed to retreive plane type from server.");
super.setDead();
FlansMod.log.throwing(e);
}
camera = new EntityCamera(world, this);
world.spawnEntity(camera);
}
/**
* Called with the movement of the mouse. Used in controlling vehicles if need be.
*
* @param deltaY
* @param deltaX
* @return if mouse movement was handled.
*/
@Override
public abstract void onMouseMoved(int deltaX, int deltaY);
@Override
@SideOnly(Side.CLIENT)
public EntityLivingBase getCamera()
{
return camera;
}
protected boolean canSit(int seat)
{
return getDriveableType().numPassengers >= seat && seats[seat].getControllingPassenger() == null;
}
@Override
protected boolean canTriggerWalking()
{
return false;
}
@Override
protected void entityInit()
{
}
@Override
public AxisAlignedBB getCollisionBox(Entity entity)
{
return null;//entity.boundingBox;
}
@Override
public boolean canBePushed()
{
return false;
}
@Override
public double getMountedYOffset()
{
return -0.3D;
}
@Override
public double getYOffset()
{
return yOffset;
}
@Override
/** Pass generic damage to the core */
public boolean attackEntityFrom(DamageSource damagesource, float i)
{
return world.isRemote || isDead || attackPart(EnumDriveablePart.core, damagesource, i);
}
@Override
public void setDead()
{
super.setDead();
//Unregister to Radar
//RadarRegistry.unregister(this);
if(world.isRemote)
camera.setDead();
for(EntitySeat seat : seats)
{
if(seat != null)
seat.reallySetDead();
}
for(EntityWheel wheel : wheels)
{
if(wheel != null)
wheel.reallySetDead();
}
}
@Override
public void onCollideWithPlayer(EntityPlayer par1EntityPlayer)
{
//Do nothing. Like a boss.
// TODO: perhaps send the player flying??
//Sounds good. ^
}
@SideOnly(Side.CLIENT)
private void ReportVehicleError()
{
FlansMod.log.warn("Vehicle error in " + this);
FlansModClient.numVehicleExceptions++;
}
@Override
public boolean canBeCollidedWith()
{
return !isDead;
}
@Override
public void applyEntityCollision(Entity entity)
{
if(!isPartOfThis(entity))
super.applyEntityCollision(entity);
}
@Override
public void setPositionAndRotationDirect(double d, double d1, double d2, float f, float f1, int i, boolean b)
{
if(ticksExisted > 1)
return;
if(!(getControllingPassenger() instanceof EntityPlayer) || !FlansMod.proxy.isThePlayer((EntityPlayer)getControllingPassenger()))
{
if(syncFromServer)
{
serverPositionTransitionTicker = i + 5;
}
else
{
double var10 = d - posX;
double var12 = d1 - posY;
double var14 = d2 - posZ;
double var16 = var10 * var10 + var12 * var12 + var14 * var14;
if(var16 <= 1.0D)
{
return;
}
serverPositionTransitionTicker = 3;
}
serverPosX = d;
serverPosY = d1;
serverPosZ = d2;
serverYaw = f;
serverPitch = f1;
}
}
public void setPositionRotationAndMotion(double x, double y, double z, float yaw, float pitch, float roll, double motX, double motY, double motZ, float velYaw, float velPitch, float velRoll, float throt, float steeringYaw)
{
if(world.isRemote)
{
serverPosX = x;
serverPosY = y;
serverPosZ = z;
serverYaw = yaw;
serverPitch = pitch;
serverRoll = roll;
serverPositionTransitionTicker = 5;
}
else
{
setPosition(x, y, z);
prevRotationYaw = yaw;
prevRotationPitch = pitch;
prevRotationRoll = roll;
setRotation(yaw, pitch, roll);
}
//Set the motions regardless of side.
motionX = motX;
motionY = motY;
motionZ = motZ;
angularVelocity = new Vector3f(velYaw, velPitch, velRoll);
throttle = throt;
}
@Override
public void setVelocity(double d, double d1, double d2)
{
motionX = d;
motionY = d1;
motionZ = d2;
}
@Override
public boolean pressKey(int key, EntityPlayer player)
{
if(!world.isRemote && key == 9 && getDriveableType().modePrimary == EnumFireMode.SEMIAUTO) //Primary
{
shoot(false);
return true;
}
else if(!world.isRemote && key == 8 && getDriveableType().modeSecondary == EnumFireMode.SEMIAUTO) //Secondary
{
shoot(true);
return true;
}
return false;
}
@Override
public void updateKeyHeldState(int key, boolean held)
{
if(world.isRemote)
{
FlansMod.getPacketHandler().sendToServer(new PacketDriveableKeyHeld(key, held));
}
switch(key)
{
case 9: leftMouseHeld = held;
break;
case 8: rightMouseHeld = held;
break;
}
}
/**
* Shoot method called by pressing / holding shoot buttons
*/
public void shoot(boolean secondary)
{
DriveableType type = getDriveableType();
if(seats[0] == null || !(seats[0].getControllingPassenger() instanceof EntityLivingBase))
return;
//Check shoot delay
while(getShootDelay(secondary) <= 0.0f)
{
//We can shoot, so grab the available shoot points and the weaponType
ArrayList<DriveablePosition> shootPoints = type.shootPoints(secondary);
EnumWeaponType weaponType = type.weaponType(secondary);
//If there are no shoot points, return
if(shootPoints.isEmpty())
return;
//For alternating guns, move on to the next one
int currentGun = getCurrentGun(secondary);
if(type.alternate(secondary))
{
currentGun = (currentGun + 1) % shootPoints.size();
setCurrentGun(currentGun, secondary);
shootEach(type, shootPoints.get(currentGun), currentGun, secondary, weaponType);
}
else for(int i = 0; i < shootPoints.size(); i++)
shootEach(type, shootPoints.get(i), i, secondary, weaponType);
}
}
private boolean driverIsCreative()
{
return seats != null && seats[0] != null && seats[0].getControllingPassenger() instanceof EntityPlayer && ((EntityPlayer)seats[0].getControllingPassenger()).capabilities.isCreativeMode;
}
private EntityPlayer getDriver()
{
if(seats != null && seats[0] != null && seats[0].getControllingPassenger() instanceof EntityPlayer)
return ((EntityPlayer)seats[0].getControllingPassenger());
else return null;
}
private void shootEach(DriveableType type, DriveablePosition shootPoint, int currentGun, boolean secondary, EnumWeaponType weaponType)
{
//Rotate the gun vector to global axes
Vector3f gunVec = getOrigin(shootPoint);
Vector3f globalGunVec = Vector3f.add(new Vector3f(posX, posY, posZ), gunVec, null); //Added a separate global gunV ec, and replaced all mentions of gunVec to globalGunVec
Vector3f lookVector = getLookVector(shootPoint);
//If its a pilot gun, then it is using a gun type, so do the following
if(shootPoint instanceof PilotGun)
{
PilotGun pilotGun = (PilotGun)shootPoint;
//Get the gun from the plane type and the ammo from the data
GunType gunType = pilotGun.type;
ItemStack shootableStack = driveableData.ammo[getDriveableType().numPassengerGunners + currentGun];
EntityPlayer driver = getDriver();
if(shootableStack == null || !(shootableStack.getItem() instanceof ItemShootable))
{
shootDelayPrimary = shootDelaySecondary = 1;
return;
}
// For each
ItemShootable shootableItem = (ItemShootable)shootableStack.getItem();
ShootableType shootableType = shootableItem.type;
float spread = 0.005f * gunType.bulletSpread * shootableType.bulletSpread;
lookVector.x += (float)world.rand.nextGaussian() * spread;
lookVector.y += (float)world.rand.nextGaussian() * spread;
lookVector.z += (float)world.rand.nextGaussian() * spread;
lookVector.scale(500.0f);
// Instant bullets. Do a raytrace
if(gunType.bulletSpeed == 0.0f)
{
for(int i = 0; i < gunType.numBullets * shootableType.numBullets; i++)
{
List<BulletHit> hits = FlansModRaytracer.Raytrace(world, driver, false, null, globalGunVec, lookVector, 0);
Entity victim = null;
//Sound to make sure this runs
// PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, type.shootSound(secondary), false);
Vector3f hitPos = Vector3f.add(globalGunVec, lookVector, null);
BulletHit firstHit = null;
if(!hits.isEmpty())
{
firstHit = hits.get(0);
hitPos = Vector3f.add(globalGunVec, (Vector3f)lookVector.scale(firstHit.intersectTime), null);
//debug chat message to get the location of impact
Minecraft.getMinecraft().player.sendMessage(new TextComponentString(" X:"+hitPos.x+" Y:"+hitPos.y+" Z:"+hitPos.z));
victim = firstHit.GetEntity();
if(victim != null){
//Name any entity that is hit. Though I get expected results where the entity I aim at is mentioned, they are not affected in game (no damage, no knockback, etc)
Minecraft.getMinecraft().player.sendMessage(new TextComponentString(victim.getName()));
}
}else{
Minecraft.getMinecraft().player.sendMessage(new TextComponentString("No hits"));
}
if(FlansMod.DEBUG) //Removed &&world.isRemote to ensure the line is run, but I could never get it to work
{
world.spawnEntity(new EntityDebugDot(world, globalGunVec, 100, 1.0f, 1.0f, 1.0f));
}
if(driver != null)
{
ShotData shotData = new InstantShotData(-1, EnumHand.MAIN_HAND, type, shootableType, driver, globalGunVec, firstHit, hitPos, gunType.damage, i < gunType.numBullets * shootableType.numBullets - 1, false);
FlansMod.getPacketHandler().sendToServer(new PacketShotData(shotData)); //I tried using this to get shots to register, I haven't noticed if it made any difference
//There was no gunstack passed in this method before, which would cause a NullPointerException. I guessed with sending shootablestack, I'm not sure if it was right
((ItemGun)gunType.item).ServerHandleDrivableShotData(shootableStack, -1, world, this, this.getDriver(), false, shotData);
}
}
}
// Else, spawn an entity
else
{
if(driver != null)
{
ShotData shotData = new SpawnEntityShotData(-1, EnumHand.MAIN_HAND, type, shootableType, driver, lookVector);
((ItemGun)gunType.item).ServerHandleShotData(null, -1, world, this, false, shotData);
}
}
//Reset the shoot delay
setShootDelay(type.shootDelay(secondary), secondary);
}
else //One of the other modes
{
switch(weaponType)
{
case BOMB:
{
if(TeamsManager.bombsEnabled)
{
int slot = -1;
for(int i = driveableData.getBombInventoryStart(); i < driveableData.getBombInventoryStart() + type.numBombSlots; i++)
{
ItemStack bomb = driveableData.getStackInSlot(i);
if(bomb != null && bomb.getItem() instanceof ItemBullet && type.isValidAmmo(((ItemBullet)bomb.getItem()).type, weaponType))
{
slot = i;
}
}
if(slot != -1)
{
int spread = 0;
int damageMultiplier = secondary ? type.damageModifierSecondary : type.damageModifierPrimary;
float shellSpeed = 0F;
ItemStack bulletStack = driveableData.getStackInSlot(slot);
ItemBullet bulletItem = (ItemBullet)bulletStack.getItem();
EntityShootable bulletEntity = bulletItem.getEntity(world,
new Vec3d(posX + gunVec.x, posY + gunVec.y, posZ + gunVec.z),
axes.getYaw(),
axes.getPitch(),
motionX,
motionY,
motionZ,
(EntityLivingBase)seats[0].getControllingPassenger(),
damageMultiplier,
type);
world.spawnEntity(bulletEntity);
if(type.shootSound(secondary) != null)
PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, type.shootSound(secondary), false);
if(!driverIsCreative())
{
bulletStack.setItemDamage(bulletStack.getItemDamage() + 1);
if(bulletStack.getItemDamage() == bulletStack.getMaxDamage())
{
bulletStack.setItemDamage(0);
bulletStack.setCount(bulletStack.getCount() - 1);
if(bulletStack.getCount() == 0)
bulletStack = ItemStack.EMPTY.copy();
}
driveableData.setInventorySlotContents(slot, bulletStack);
}
setShootDelay(type.shootDelay(secondary), secondary);
}
else
{
shootDelayPrimary = shootDelaySecondary = 1;
}
}
break;
}
case MISSILE: //These two are actually almost identical
case SHELL:
{
if(TeamsManager.shellsEnabled)
{
int slot = -1;
for(int i = driveableData.getMissileInventoryStart(); i < driveableData.getMissileInventoryStart() + type.numMissileSlots; i++)
{
ItemStack shell = driveableData.getStackInSlot(i);
if(shell != null && shell.getItem() instanceof ItemBullet && type.isValidAmmo(((ItemBullet)shell.getItem()).type, weaponType))
{
slot = i;
}
}
if(slot != -1)
{
int spread = 0;
int damageMultiplier = secondary ? type.damageModifierSecondary : type.damageModifierPrimary;
float shellSpeed = 3F;
ItemStack bulletStack = driveableData.getStackInSlot(slot);
ItemBullet bulletItem = (ItemBullet)bulletStack.getItem();
EntityShootable bulletEntity = bulletItem.getEntity(world,
Vector3f.add(new Vector3f(posX, posY, posZ), gunVec, null),
lookVector,
(EntityLivingBase)seats[0].getControllingPassenger(),
spread,
damageMultiplier,
shellSpeed,
type);
world.spawnEntity(bulletEntity);
if(type.shootSound(secondary) != null)
PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, type.shootSound(secondary), false);
if(!driverIsCreative())
{
bulletStack.setItemDamage(bulletStack.getItemDamage() + 1);
if(bulletStack.getItemDamage() == bulletStack.getMaxDamage())
{
bulletStack.setItemDamage(0);
bulletStack.setCount(bulletStack.getCount() - 1);
if(bulletStack.getCount() == 0)
bulletStack = ItemStack.EMPTY.copy();
}
driveableData.setInventorySlotContents(slot, bulletStack);
}
setShootDelay(type.shootDelay(secondary), secondary);
}
else
{
shootDelayPrimary = shootDelaySecondary = 1;
}
}
break;
}
case GUN: //Handled above
break;
case MINE:
break;
case NONE:
break;
}
}
}
public Vector3f getOrigin(DriveablePosition dp)
{
//Rotate the gun vector to global axes
Vector3f localGunVec = new Vector3f(dp.position);
if(dp.part == EnumDriveablePart.turret)
{
//Untranslate by the turret origin, to get the rotation about the right point
Vector3f.sub(localGunVec, getDriveableType().turretOrigin, localGunVec);
//Rotate by the turret angles
localGunVec = seats[0].looking.findLocalVectorGlobally(localGunVec);
//Translate by the turret origin
Vector3f.add(localGunVec, getDriveableType().turretOrigin, localGunVec);
}
return rotate(localGunVec);
}
public Vector3f getLookVector(DriveablePosition dp)
{
return axes.getXAxis();
}
@Override
public void onUpdate()
{
super.onUpdate();
DriveableType type = getDriveableType();
// Do a full check of our passengers for wheels or seats
for(Entity passenger : getPassengers())
{
if(passenger instanceof EntitySeat)
{
EntitySeat seat = (EntitySeat)passenger;
if(seat.getExpectedSeatID() >= 0 && seats[seat.getExpectedSeatID()] == null)
{
seats[seat.getExpectedSeatID()] = seat;
}
}
else if(passenger instanceof EntityWheel)
{
EntityWheel wheel = (EntityWheel)passenger;
if(wheel.getExpectedWheelID() >= 0 && wheels[wheel.getExpectedWheelID()] == null)
{
wheels[wheel.getExpectedWheelID()] = wheel;
}
}
else
{
FlansMod.log.warn("Entity " + passenger + " is riding a driveable core entity.");
}
}
readyForUpdates = true;
for(int i = 0; i < type.numPassengers; i++)
{
if(seats[i] == null)
{
readyForUpdates = false;
}
}
for(int i = 0; i < type.wheelPositions.length; i++)
{
if(wheels[i] == null)
{
readyForUpdates = false;
}
}
if(!readyForUpdates)
{
if(!world.isRemote)
{
// Well heck, if it's bork, let's make new ones
initType(type, true, false);
}
// If we end up stuck like this on a client, handle updates from server
if(world.isRemote)
{
//The driveable is currently moving towards its server position. Continue doing so.
if(serverPositionTransitionTicker > 0)
{
double x = posX + (serverPosX - posX) / serverPositionTransitionTicker;
double y = posY + (serverPosY - posY) / serverPositionTransitionTicker;
double z = posZ + (serverPosZ - posZ) / serverPositionTransitionTicker;
double dYaw = MathHelper.wrapDegrees(serverYaw - axes.getYaw());
double dPitch = MathHelper.wrapDegrees(serverPitch - axes.getPitch());
double dRoll = MathHelper.wrapDegrees(serverRoll - axes.getRoll());
rotationYaw = (float)(axes.getYaw() + dYaw / serverPositionTransitionTicker);
rotationPitch = (float)(axes.getPitch() + dPitch / serverPositionTransitionTicker);
float rotationRoll = (float)(axes.getRoll() + dRoll / serverPositionTransitionTicker);
--serverPositionTransitionTicker;
setPosition(x, y, z);
setRotation(rotationYaw, rotationPitch, rotationRoll);
//return;
}
//If the driveable is at its server position and does not have the next update, it should just simulate itself as a server side driveable would, so continue
}
return;
}
//Harvest stuff
//Aesthetics
if(hasEnoughFuel())
{
harvesterAngle += throttle / 5F;
}
//Actual harvesting
if(type.harvestBlocks && type.health.get(EnumDriveablePart.harvester) != null)
{
CollisionBox box = type.health.get(EnumDriveablePart.harvester);
for(float x = box.x; x <= box.x + box.w; x++)
{
for(float y = box.y; y <= box.y + box.h; y++)
{
for(float z = box.z; z <= box.z + box.d; z++)
{
Vector3f v = axes.findLocalVectorGlobally(new Vector3f(x, y, z));
int blockX = (int)Math.round(posX + v.x);
int blockY = (int)Math.round(posY + v.y);
int blockZ = (int)Math.round(posZ + v.z);
IBlockState block = world.getBlockState(new BlockPos(blockX, blockY, blockZ));
boolean cancelled = false;
if(seats[0] != null && seats[0].getControllingPassenger() instanceof EntityPlayerMP)
{
int eventOutcome = ForgeHooks.onBlockBreakEvent(world,
((EntityPlayerMP)seats[0].getControllingPassenger()).capabilities.isCreativeMode ? GameType.CREATIVE : ((EntityPlayerMP)seats[0].getControllingPassenger()).capabilities.allowEdit ? GameType.SURVIVAL : GameType.ADVENTURE,
(EntityPlayerMP)seats[0].getControllingPassenger(), new BlockPos(blockX, blockY, blockZ));
cancelled = eventOutcome == -1;
}
if(!cancelled)
{
if(type.materialsHarvested.contains(block.getMaterial()) && block.getBlockHardness(world, new BlockPos(blockX, blockY, blockZ)) >= 0F)
{
//Add the itemstack to mecha inventory
NonNullList<ItemStack> stacks = NonNullList.create();
block.getBlock().getDrops(stacks, world, new BlockPos(blockX, blockY, blockZ), world.getBlockState(new BlockPos(blockX, blockY, blockZ)), 0);
for(ItemStack stack : stacks)
{
if(!InventoryHelper.addItemStackToInventory(driveableData, stack, driverIsCreative()) && !world.isRemote && world.getGameRules().getBoolean("doTileDrops"))
{
world.spawnEntity(new EntityItem(world, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, stack));
}
}
//Destroy block
if(!world.isRemote)
{
WorldServer worldServer = (WorldServer)world;
BlockPos pos = new BlockPos(blockX, blockY, blockZ);
destroyBlock(worldServer, pos, getDriver(), false);
}
}
}
}
}
}
}
for(DriveablePart part : getDriveableData().parts.values())
{
if(part.box != null)
{
part.update(this);
//Client side particles
if(world.isRemote)
{
if(part.onFire)
{
//Pick a random position within the bounding box and spawn a flame there
Vector3f pos = axes.findLocalVectorGlobally(new Vector3f(part.box.x + rand.nextFloat() * part.box.w, part.box.y + rand.nextFloat() * part.box.h, part.box.z + rand.nextFloat() * part.box.d));
world.spawnParticle(EnumParticleTypes.FLAME, posX + pos.x, posY + pos.y, posZ + pos.z, 0, 0, 0);
}
if(part.health > 0 && part.health < part.maxHealth / 2)
{
//Pick a random position within the bounding box and spawn a flame there
Vector3f pos = axes.findLocalVectorGlobally(new Vector3f(part.box.x + rand.nextFloat() * part.box.w, part.box.y + rand.nextFloat() * part.box.h, part.box.z + rand.nextFloat() * part.box.d));
world.spawnParticle(part.health < part.maxHealth / 4 ? EnumParticleTypes.SMOKE_LARGE : EnumParticleTypes.SMOKE_NORMAL, posX + pos.x, posY + pos.y, posZ + pos.z, 0, 0, 0);
}
}
//Server side fire handling
if(part.onFire)
{
//Rain can put out fire
if(world.isRaining() && rand.nextInt(40) == 0)
part.onFire = false;
//Also water blocks
//Get the centre point of the part
Vector3f pos = axes.findLocalVectorGlobally(new Vector3f(part.box.x + part.box.w / 2F, part.box.y + part.box.h / 2F, part.box.z + part.box.d / 2F));
if(world.getBlockState(new BlockPos(MathHelper.floor(posX + pos.x), MathHelper.floor(posY + pos.y), MathHelper.floor(posZ + pos.z))).getMaterial() == Material.WATER)
{
part.onFire = false;
}
}
else
{
Vector3f pos = axes.findLocalVectorGlobally(new Vector3f(part.box.x / 16F + part.box.w / 32F, part.box.y / 16F + part.box.h / 32F, part.box.z / 16F + part.box.d / 32F));
if(world.getBlockState(new BlockPos(MathHelper.floor(posX + pos.x), MathHelper.floor(posY + pos.y), MathHelper.floor(posZ + pos.z))).getMaterial() == Material.LAVA)
{
part.onFire = true;
}
}
}
}
for(int i = 0; i < type.emitters.size(); i++)
{
ParticleEmitter emitter = type.emitters.get(i);
emitterTimers[i]--;
boolean canEmit = false;
DriveablePart part = getDriveableData().parts.get(EnumDriveablePart.getPart(emitter.part));
float healthPercentage = (float)part.health / (float)part.maxHealth;
if(isPartIntact(EnumDriveablePart.getPart(emitter.part)) && healthPercentage >= emitter.minHealth && healthPercentage <= emitter.maxHealth)
{
canEmit = true;
}
if(emitterTimers[i] <= 0)
{
if(throttle >= emitter.minThrottle && throttle <= emitter.maxThrottle && canEmit)
{
//Emit!
Vector3f velocity = new Vector3f(0, 0, 0);
Vector3f pos = new Vector3f(0, 0, 0);
if(seats != null && seats[0] != null)
{
if(EnumDriveablePart.getPart(emitter.part) != EnumDriveablePart.turret && EnumDriveablePart.getPart(emitter.part) != EnumDriveablePart.head)
{
Vector3f localPosition = new Vector3f(emitter.origin.x + rand.nextFloat() * emitter.extents.x - emitter.extents.x * 0.5f,
emitter.origin.y + rand.nextFloat() * emitter.extents.y - emitter.extents.y * 0.5f,
emitter.origin.z + rand.nextFloat() * emitter.extents.z - emitter.extents.z * 0.5f);
pos = axes.findLocalVectorGlobally(localPosition);
velocity = axes.findLocalVectorGlobally(emitter.velocity);
}
else if(EnumDriveablePart.getPart(emitter.part) == EnumDriveablePart.turret || EnumDriveablePart.getPart(emitter.part) != EnumDriveablePart.head)
{
Vector3f localPosition2 = new Vector3f(emitter.origin.x + rand.nextFloat() * emitter.extents.x - emitter.extents.x * 0.5f,
emitter.origin.y + rand.nextFloat() * emitter.extents.y - emitter.extents.y * 0.5f,
emitter.origin.z + rand.nextFloat() * emitter.extents.z - emitter.extents.z * 0.5f);
RotatedAxes yawOnlyLooking = new RotatedAxes(seats[0].looking.getYaw() + axes.getYaw(), axes.getPitch(), axes.getRoll());
pos = yawOnlyLooking.findLocalVectorGlobally(localPosition2);
velocity = yawOnlyLooking.findLocalVectorGlobally(emitter.velocity);
}
world.spawnParticle(emitter.effectType,
posX + pos.x, posY + pos.y, posZ + pos.z, velocity.x, velocity.y, velocity.z);
}
}
emitterTimers[i] = emitter.emitRate;
}
}
checkParts();
prevRotationYaw = axes.getYaw();
prevRotationPitch = axes.getPitch();
prevRotationRoll = axes.getRoll();
prevAxes = axes.clone();
boolean canThrust = driverIsCreative() || driveableData.fuelInTank > 0;
//If there's no player in the driveable or it cannot thrust, slow the plane and turn off mouse held actions
if((seats[0] != null && seats[0].getControllingPassenger() == null) || !canThrust && getDriveableType().maxThrottle != 0 && getDriveableType().maxNegativeThrottle != 0)
{
throttle *= 0.98F;
rightMouseHeld = leftMouseHeld = false;
}
else if(seats[0] != null && seats[0].getControllingPassenger() != null && seats[0].getControllingPassenger() == getControllingPassenger())
{
ReportVehicleError();
}
//Check if shooting
if(shootDelayPrimary > 0)
shootDelayPrimary--;
if(shootDelaySecondary > 0)
shootDelaySecondary--;
if(!world.isRemote)
{
if(leftMouseHeld && getDriveableType().modePrimary == EnumFireMode.FULLAUTO)
shoot(false);
if(rightMouseHeld && getDriveableType().modeSecondary == EnumFireMode.FULLAUTO)
shoot(true);
minigunSpeedPrimary *= 0.9F;
minigunSpeedSecondary *= 0.9F;
if(leftMouseHeld && getDriveableType().modePrimary == EnumFireMode.MINIGUN)
{
minigunSpeedPrimary += 0.1F;
if(minigunSpeedPrimary > 1F)
shoot(false);
}
if(rightMouseHeld && getDriveableType().modeSecondary == EnumFireMode.MINIGUN)
{
minigunSpeedSecondary += 0.1F;
if(minigunSpeedSecondary > 1F)
shoot(true);
}
}
//Handle fuel
int fuelMultiplier = 2;
//The tank is currently full, so do nothing
if(getDriveableData().fuelInTank >= type.fuelTankSize)
return;
//Look through the entire inventory for fuel cans, buildcraft fuel buckets and RedstoneFlux power sources
for(int i = 0; i < getDriveableData().getSizeInventory(); i++)
{
ItemStack stack = getDriveableData().getStackInSlot(i);
if(stack == null || stack.isEmpty())
continue;
Item item = stack.getItem();
//If we have an electric engine, look for RedstoneFlux power source items, such as power cubes
/*
if(getDriveableData().engine.useRFPower)
{
if(item instanceof IEnergyContainerItem)
{
IEnergyContainerItem energy = (IEnergyContainerItem)item;
getDriveableData().fuelInTank += (fuelMultiplier * energy.extractEnergy(stack, getDriveableData().engine.RFDrawRate, false)) / getDriveableData().engine.RFDrawRate;
}
}
else
*/
{
//Check for Flan's Mod fuel items
if(item instanceof ItemPart)
{
PartType part = ((ItemPart)item).type;
//Check it is a fuel item
if(part.category == EnumPartCategory.FUEL)
{
//Put 2 points of fuel
getDriveableData().fuelInTank += fuelMultiplier;
//Damage the fuel item to indicate being used up
int damage = stack.getItemDamage();
stack.setItemDamage(damage + 1);
//If we have finished this fuel item
if(damage >= stack.getMaxDamage())
{
//Reset the damage to 0
stack.setItemDamage(0);
//Consume one item
stack.setCount(stack.getCount() - 1);
//If we consumed the last one, destroy the stack
if(stack.getCount() <= 0)
getDriveableData().setInventorySlotContents(i, ItemStack.EMPTY.copy());
}
//We found a fuel item and consumed some, so we are done
break;
}
}
//Check for Buildcraft oil and fuel buckets
else if(FlansMod.hooks.BuildCraftLoaded && stack.isItemEqual(FlansMod.hooks.BuildCraftOilBucket) && getDriveableData().fuelInTank + 1000 * fuelMultiplier <= type.fuelTankSize)
{
getDriveableData().fuelInTank += 1000 * fuelMultiplier;
getDriveableData().setInventorySlotContents(i, new ItemStack(Items.BUCKET));
}
else if(FlansMod.hooks.BuildCraftLoaded && stack.isItemEqual(FlansMod.hooks.BuildCraftFuelBucket) && getDriveableData().fuelInTank + 2000 * fuelMultiplier <= type.fuelTankSize)
{
getDriveableData().fuelInTank += 2000 * fuelMultiplier;
getDriveableData().setInventorySlotContents(i, new ItemStack(Items.BUCKET));
}
}
}
}
public void checkForCollisions()
{
boolean crashInWater = false;
double speed = getSpeedXYZ();
for(DriveablePosition p : getDriveableType().collisionPoints)
{
if(driveableData.parts.get(p.part).dead)
continue;
Vector3f lastRelPos = prevAxes.findLocalVectorGlobally(p.position);
Vec3d lastPos = new Vec3d(prevPosX + lastRelPos.x, prevPosY + lastRelPos.y, prevPosZ + lastRelPos.z);
Vector3f currentRelPos = axes.findLocalVectorGlobally(p.position);
Vec3d currentPos = new Vec3d(posX + currentRelPos.x, posY + currentRelPos.y, posZ + currentRelPos.z);
if(FlansMod.DEBUG && world.isRemote)
{
world.spawnEntity(new EntityDebugVector(world, new Vector3f(lastPos), Vector3f.sub(currentRelPos, lastRelPos, null), 10, 1F, 0F, 0F));
}
RayTraceResult hit = world.rayTraceBlocks(lastPos, currentPos, crashInWater);
if(hit != null && hit.typeOfHit == Type.BLOCK)
{
BlockPos pos = hit.getBlockPos();
IBlockState state = world.getBlockState(pos);
Block blockHit = state.getBlock();
float blockHardness = state.getBlockHardness(world, pos);
//Attack the part
if(!attackPart(p.part, DamageSource.IN_WALL, blockHardness * blockHardness * (float)speed) && TeamsManager.driveablesBreakBlocks)
{
//And if it didn't die from the attack, break the block
// TODO: [1.12] Heck
//playAuxSFXAtEntity(null, 2001, pos, Block.getStateId(state));
if(!world.isRemote)
{
WorldServer worldServer = (WorldServer)world;
destroyBlock(worldServer, pos, getDriver(), true);
}
}
else
{
//The part died!
world.createExplosion(this, currentPos.x, currentPos.y, currentPos.z, 1F, false);
}
}
}
}
@Override
public void fall(float distance, float damageMultiplier)
{
if(distance <= 0)
return;
//super.fall(k);
int i = MathHelper.ceil(distance - 10F);
if(i > 0)
attackPart(EnumDriveablePart.core, DamageSource.FALL, damageMultiplier * i / 5);
}
/**
* Attack a certain part of a driveable and return whether it broke or not
*/
public boolean attackPart(EnumDriveablePart ep, DamageSource source, float damage)
{
DriveablePart part = driveableData.parts.get(ep);
return part.attack(damage, source.isFireDamage());
}
/**
* Takes a vector (such as the origin of a seat / gun) and translates it from local coordinates to global coordinates
*/
public Vector3f rotate(Vector3f inVec)
{
return axes.findLocalVectorGlobally(inVec);
}
/**
* Takes a vector (such as the origin of a seat / gun) and translates it from local coordinates to global coordinates
*/
public Vector3f rotate(Vec3d inVec)
{
return rotate(inVec.x, inVec.y, inVec.z);
}
/**
* Takes a vector (such as the origin of a seat / gun) and translates it from local coordinates to global coordinates
*/
public Vector3f rotate(double x, double y, double z)
{
return rotate(new Vector3f((float)x, (float)y, (float)z));
}
//Rotate the plane locally by some angle about the yaw axis
public void rotateYaw(float rotateBy)
{
if(Math.abs(rotateBy) < 0.01F)
return;
axes.rotateLocalYaw(rotateBy);
updatePrevAngles();
}
//Rotate the plane locally by some angle about the pitch axis
public void rotatePitch(float rotateBy)
{
if(Math.abs(rotateBy) < 0.01F)
return;
axes.rotateLocalPitch(rotateBy);
updatePrevAngles();
}
//Rotate the plane locally by some angle about the roll axis
public void rotateRoll(float rotateBy)
{
if(Math.abs(rotateBy) < 0.01F)
return;
axes.rotateLocalRoll(rotateBy);
updatePrevAngles();
}
public void updatePrevAngles()
{
//Correct angles that crossed the +/- 180 line, so that rendering doesnt make them swing 360 degrees in one tick.
double dYaw = axes.getYaw() - prevRotationYaw;
if(dYaw > 180)
prevRotationYaw += 360F;
if(dYaw < -180)
prevRotationYaw -= 360F;
double dPitch = axes.getPitch() - prevRotationPitch;
if(dPitch > 180)
prevRotationPitch += 360F;
if(dPitch < -180)
prevRotationPitch -= 360F;
double dRoll = axes.getRoll() - prevRotationRoll;
if(dRoll > 180)
prevRotationRoll += 360F;
if(dRoll < -180)
prevRotationRoll -= 360F;
}
public void setRotation(float rotYaw, float rotPitch, float rotRoll)
{
axes.setAngles(rotYaw, rotPitch, rotRoll);
}
//Used to stop self collision
public boolean isPartOfThis(Entity ent)
{
for(EntitySeat seat : seats)
{
if(seat == null)
continue;
if(ent == seat)
return true;
if(seat.getControllingPassenger() == ent)
return true;
}
for(EntityWheel wheel : wheels)
{
if(ent == wheel)
return true;
}
return ent == this;
}
public DriveableType getDriveableType()
{
return DriveableType.getDriveable(driveableType);
}
public DriveableData getDriveableData()
{
return driveableData;
}
@Override
public boolean isDead()
{
return isDead;
}
@Override
public Entity getControllingEntity()
{
return seats[0].getControllingEntity();
}
@Override
public ItemStack getPickedResult(RayTraceResult target)
{
ItemStack stack = new ItemStack(getDriveableType().item, 1, 0);
NBTTagCompound tags = new NBTTagCompound();
stack.setTagCompound(tags);
driveableData.writeToNBT(tags);
return stack;
}
public boolean hasFuel()
{
if(seats == null || seats[0] == null || seats[0].getControllingPassenger() == null)
return false;
return driverIsCreative() || driveableData.fuelInTank > 0;
}
public boolean hasEnoughFuel()
{
if(seats == null || seats[0] == null || seats[0].getControllingPassenger() == null)
return false;
return driverIsCreative() || driveableData.fuelInTank > driveableData.engine.fuelConsumption * throttle;
}
//Physics time! Oooh yeah
public double getSpeedXYZ()
{
return Math.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ);
}
public double getSpeedXZ()
{
return Math.sqrt(motionX * motionX + motionZ * motionZ);
}
/**
* To be overriden by vehicles to get alternate collision system
*/
public boolean landVehicle()
{
return false;
}
/**
* Overriden by planes for wheel parts
*/
public boolean gearDown()
{
return true;
}
/**
* Whether or not the plane is on the ground
* TODO : Replace with proper check based on wheels
*/
public boolean onGround()
{
return onGround;
}
/**
* Attack method called by bullets hitting the plane. Does advanced raytracing to detect which part of the plane is hit
*/
public ArrayList<BulletHit> attackFromBullet(Vector3f origin, Vector3f motion)
{
//Make an array to contain the hits
ArrayList<BulletHit> hits = new ArrayList<>();
//Get the position of the bullet origin, relative to the centre of the plane, and then rotate the vectors onto local co-ordinates
Vector3f relativePosVector = Vector3f.sub(origin, new Vector3f((float)posX, (float)posY, (float)posZ), null);
Vector3f rotatedPosVector = axes.findGlobalVectorLocally(relativePosVector);
Vector3f rotatedMotVector = axes.findGlobalVectorLocally(motion);
//Check each part
for(DriveablePart part : getDriveableData().parts.values())
{
//Ray trace the bullet
DriveableHit hit = part.rayTrace(this, rotatedPosVector, rotatedMotVector);
if(hit != null)
hits.add(hit);
}
return hits;
}
/**
* Called if the bullet actually hit the part returned by the raytrace
*
* @param penetratingPower
*/
public float bulletHit(BulletType bulletType, float damage, DriveableHit hit, float penetratingPower)
{
DriveablePart part = getDriveableData().parts.get(hit.part);
part.hitByBullet(bulletType, damage);
//This is server side bsns
if(!world.isRemote)
{
checkParts();
//If it hit, send a damage update packet
FlansMod.getPacketHandler().sendToAllAround(new PacketDriveableDamage(this), posX, posY, posZ, 100, dimension);
}
return penetratingPower - 5F;
}
/**
* A simple raytracer for the driveable. Called by tools
*/
public DriveablePart raytraceParts(Vector3f origin, Vector3f motion)
{
//Get the position of the bullet origin, relative to the centre of the plane, and then rotate the vectors onto local co-ordinates
Vector3f relativePosVector = Vector3f.sub(origin, new Vector3f((float)posX, (float)posY, (float)posZ), null);
Vector3f rotatedPosVector = axes.findGlobalVectorLocally(relativePosVector);
Vector3f rotatedMotVector = axes.findGlobalVectorLocally(motion);
//Check each part
for(DriveablePart part : getDriveableData().parts.values())
{
//Ray trace the bullet
if(part.rayTrace(this, rotatedPosVector, rotatedMotVector) != null)
{
return part;
}
}
return null;
}
/**
* For overriding for toggles such as gear up / down on planes
*/
public boolean canHitPart(EnumDriveablePart part)
{
return true;
}
/**
* Internal method for checking that all parts are ok, destroying broken ones, dropping items and making sure that child parts are destroyed when their parents are
*/
public void checkParts()
{
for(DriveablePart part : getDriveableData().parts.values())
{
if(part != null && !part.dead && part.health <= 0 && part.maxHealth > 0)
{
killPart(part);
}
}
//If the core was destroyed, kill the driveable
if(getDriveableData().parts.get(EnumDriveablePart.core).dead)
{
if(!world.isRemote)
{
for(DriveablePart part : driveableData.parts.values())
{
if(part.health > 0 && !part.dead)
killPart(part);
}
}
setDead();
}
}
/**
* Internal method for killing driveable parts
*/
private void killPart(DriveablePart part)
{
if(part.dead)
return;
part.health = 0;
part.dead = true;
//Drop items
DriveableType type = getDriveableType();
if(!world.isRemote)
{
Vector3f pos = new Vector3f(0, 0, 0);
//Get the midpoint of the part
if(part.box != null)
pos = axes.findLocalVectorGlobally(new Vector3f(part.box.x / 16F + part.box.w / 32F, part.box.y / 16F + part.box.h / 32F, part.box.z / 16F + part.box.d / 32F));
ArrayList<ItemStack> drops = type.getItemsRequired(part, getDriveableData().engine);
if(drops != null)
{
//Drop each itemstack
for(ItemStack stack : drops)
{
world.spawnEntity(new EntityItem(world, posX + pos.x, posY + pos.y, posZ + pos.z, stack.copy()));
}
}
dropItemsOnPartDeath(pos, part);
//Inventory is in the core, so drop it if the core is broken
if(part.type == EnumDriveablePart.core)
{
for(int i = 0; i < getDriveableData().getSizeInventory(); i++)
{
ItemStack stack = getDriveableData().getStackInSlot(i);
if(stack != null && !stack.isEmpty())
{
world.spawnEntity(new EntityItem(world, posX + rand.nextGaussian(), posY + rand.nextGaussian(), posZ + rand.nextGaussian(), stack));
}
}
}
}
//Kill all child parts to stop things floating unconnected
for(EnumDriveablePart child : part.type.getChildren())
{
killPart(getDriveableData().parts.get(child));
}
}
/**
* Method for planes, vehicles and whatnot to drop their own specific items if they wish
*/
protected abstract void dropItemsOnPartDeath(Vector3f midpoint, DriveablePart part);
@Override
public float getPlayerRoll()
{
return axes.getRoll();
}
@Override
public float getPrevPlayerRoll()
{
return prevAxes.getRoll();
}
@Override
public void explode()
{
}
@Override
public float getCameraDistance()
{
return getDriveableType().cameraDistance;
}
public boolean isPartIntact(EnumDriveablePart part)
{
DriveablePart thisPart = getDriveableData().parts.get(part);
return thisPart.maxHealth == 0 || thisPart.health > 0;
}
public abstract boolean hasMouseControlMode();
public abstract String getBombInventoryName();
public abstract String getMissileInventoryName();
public boolean rotateWithTurret(Seat seat)
{
return seat.part == EnumDriveablePart.turret;
}
@Override
public String getName()
{
return getDriveableType().name;
}
@SideOnly(Side.CLIENT)
public boolean showInventory(int seat)
{
return seat != 0 || !FlansModClient.controlModeMouse;
}
//-------------------------------------
// Getters and setters for dual fields
//-------------------------------------
public float getShootDelay(boolean secondary)
{
return secondary ? shootDelaySecondary : shootDelayPrimary;
}
public float getMinigunSpeed(boolean secondary)
{
return secondary ? minigunSpeedSecondary : minigunSpeedPrimary;
}
public int getCurrentGun(boolean secondary)
{
return secondary ? currentGunSecondary : currentGunPrimary;
}
public void setShootDelay(float f, boolean secondary)
{
if(secondary)
shootDelaySecondary = f;
else shootDelayPrimary = f;
}
public void setMinigunSpeed(float f, boolean secondary)
{
if(secondary)
minigunSpeedSecondary = f;
else minigunSpeedPrimary = f;
}
public void setCurrentGun(int i, boolean secondary)
{
if(secondary)
currentGunSecondary = i;
else currentGunPrimary = i;
}
@Override
protected boolean canFitPassenger(Entity passenger)
{
if(passenger instanceof EntitySeat || passenger instanceof EntityWheel)
{
return getPassengers().size() < getDriveableType().numPassengers + getDriveableType().wheelPositions.length + 1;
}
return false;
}
@Override
public void updatePassenger(Entity passenger)
{
// They can handle themselves, but maybe the code should be moved to here
}
@Override
public void removePassenger(Entity passenger)
{
super.removePassenger(passenger);
}
public EntitySeat getSeat(EntityLivingBase passenger)
{
for(EntitySeat seat : seats)
{
if(seat.getControllingEntity() == passenger)
{
return seat;
}
}
return null;
}
@Override
protected void addPassenger(Entity passenger)
{
super.addPassenger(passenger);
if(world.isRemote)
{
// We need to do some handling to work out which seat to get into. Or not?
}
}
public void registerSeat(EntitySeat seat)
{
seats[seat.getExpectedSeatID()] = seat;
}
public void registerWheel(EntityWheel wheel)
{
wheels[wheel.getExpectedWheelID()] = wheel;
}
public EntitySeat[] getSeats()
{
return seats;
}
public EntitySeat getSeat(int id)
{
if(seats[id] == null)
{
for(Entity passenger : getPassengers())
{
if(passenger instanceof EntitySeat)
{
EntitySeat seat = (EntitySeat)passenger;
if(seat.getExpectedSeatID() == id)
{
seats[id] = seat;
seats[id].driveable = this;
break;
}
}
}
}
return seats[id];
}
public EntityWheel getWheel(int id)
{
if(wheels[id] == null)
{
for(Entity passenger : getPassengers())
{
if(passenger instanceof EntityWheel)
{
EntityWheel wheel = (EntityWheel)passenger;
if(wheel.getExpectedWheelID() == id)
{
wheels[id] = wheel;
break;
}
}
}
}
return wheels[id];
}
}
package com.flansmod.common.guns;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.lwjgl.input.Mouse;
import com.google.common.collect.Multimap;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.EnumAction;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.ActionResult;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.NonNullList;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import com.flansmod.client.FlansModClient;
import com.flansmod.client.FlansModResourceHandler;
import com.flansmod.client.debug.EntityDebugDot;
import com.flansmod.client.debug.EntityDebugVector;
import com.flansmod.client.model.GunAnimations;
import com.flansmod.client.model.GunAnimations.LookAtState;
import com.flansmod.client.model.InstantBulletRenderer;
import com.flansmod.client.model.InstantBulletRenderer.InstantShotTrail;
import com.flansmod.common.EntityItemCustomRender;
import com.flansmod.common.FlansMod;
import com.flansmod.common.PlayerData;
import com.flansmod.common.PlayerHandler;
import com.flansmod.common.guns.ShotData.InstantShotData;
import com.flansmod.common.guns.ShotData.SpawnEntityShotData;
import com.flansmod.common.guns.raytracing.FlansModRaytracer;
import com.flansmod.common.guns.raytracing.FlansModRaytracer.BlockHit;
import com.flansmod.common.guns.raytracing.FlansModRaytracer.BulletHit;
import com.flansmod.common.guns.raytracing.FlansModRaytracer.DriveableHit;
import com.flansmod.common.guns.raytracing.FlansModRaytracer.EntityHit;
import com.flansmod.common.guns.raytracing.FlansModRaytracer.PlayerBulletHit;
import com.flansmod.common.network.PacketPlaySound;
import com.flansmod.common.network.PacketReload;
import com.flansmod.common.network.PacketShotData;
import com.flansmod.common.teams.EntityFlag;
import com.flansmod.common.teams.EntityFlagpole;
import com.flansmod.common.teams.EntityGunItem;
import com.flansmod.common.teams.Team;
import com.flansmod.common.types.IPaintableItem;
import com.flansmod.common.types.InfoType;
import com.flansmod.common.types.PaintableType;
import com.flansmod.common.vector.Vector3f;
public class ItemGun extends Item implements IPaintableItem
{
private static final int CLIENT_TO_SERVER_UPDATE_INTERVAL = 1;
private static final int SERVER_TO_CLIENT_UPDATE_INTERVAL = 2;
private GunType type;
public GunType GetType()
{
return type;
}
@Override
public InfoType getInfoType()
{
return type;
}
@Override
public PaintableType GetPaintableType()
{
return type;
}
private int soundDelay = 0;
private static boolean rightMouseHeld;
private static boolean lastRightMouseHeld;
private static boolean leftMouseHeld;
private static boolean lastLeftMouseHeld;
private static boolean GetMouseHeld(EnumHand hand)
{
if(FlansMod.shootOnRightClick)
return hand == EnumHand.MAIN_HAND ? leftMouseHeld : rightMouseHeld;
else return hand == EnumHand.MAIN_HAND ? rightMouseHeld : leftMouseHeld;
}
private static boolean GetLastMouseHeld(EnumHand hand)
{
if(FlansMod.shootOnRightClick)
return hand == EnumHand.MAIN_HAND ? lastLeftMouseHeld : lastRightMouseHeld;
else return hand == EnumHand.MAIN_HAND ? lastRightMouseHeld : lastLeftMouseHeld;
}
private static List<ShotData> shotsFiredClient = new ArrayList<>(), shotsFiredServer = new ArrayList<>();
public ItemGun(GunType type)
{
maxStackSize = 1;
this.type = type;
type.item = this;
setMaxDamage(0);
setRegistryName(type.shortName);
setCreativeTab(FlansMod.tabFlanGuns);
}
/**
* Get the bullet item stack stored in the gun's NBT data (the loaded magazine / bullets)
*/
public ItemStack getBulletItemStack(ItemStack gun, int id)
{
//If the gun has no tags, give it some
if(!gun.hasTagCompound())
{
gun.setTagCompound(new NBTTagCompound());
return ItemStack.EMPTY.copy();
}
//If the gun has no ammo tags, give it some
if(!gun.getTagCompound().hasKey("ammo"))
{
NBTTagList ammoTagsList = new NBTTagList();
for(int i = 0; i < type.numAmmoItemsInGun; i++)
{
ammoTagsList.appendTag(new NBTTagCompound());
}
gun.getTagCompound().setTag("ammo", ammoTagsList);
return ItemStack.EMPTY.copy();
}
//Take the list of ammo tags
NBTTagList ammoTagsList = gun.getTagCompound().getTagList("ammo", Constants.NBT.TAG_COMPOUND);
//Get the specific ammo tags required
NBTTagCompound ammoTags = ammoTagsList.getCompoundTagAt(id);
return new ItemStack(ammoTags);
}
/**
* Set the bullet item stack stored in the gun's NBT data (the loaded magazine / bullets)
*/
public void setBulletItemStack(ItemStack gun, ItemStack bullet, int id)
{
//If the gun has no tags, give it some
if(!gun.hasTagCompound())
{
gun.setTagCompound(new NBTTagCompound());
}
//If the gun has no ammo tags, give it some
if(!gun.getTagCompound().hasKey("ammo"))
{
NBTTagList ammoTagsList = new NBTTagList();
for(int i = 0; i < type.numAmmoItemsInGun; i++)
{
ammoTagsList.appendTag(new NBTTagCompound());
}
gun.getTagCompound().setTag("ammo", ammoTagsList);
}
//Take the list of ammo tags
NBTTagList ammoTagsList = gun.getTagCompound().getTagList("ammo", Constants.NBT.TAG_COMPOUND);
//Get the specific ammo tags required
NBTTagCompound ammoTags = ammoTagsList.getCompoundTagAt(id);
//Represent empty slots by nulltypes
if(bullet == null)
{
ammoTags = new NBTTagCompound();
}
//Set the tags to match the bullet stack
bullet.writeToNBT(ammoTags);
}
/**
* Method for dropping items on reload and on shoot
*/
public static void dropItem(World world, Entity entity, String itemName)
{
if(itemName != null)
{
int damage = 0;
if(itemName.contains("."))
{
damage = Integer.parseInt(itemName.split("\\.")[1]);
itemName = itemName.split("\\.")[0];
}
ItemStack dropStack = InfoType.getRecipeElement(itemName, damage);
entity.entityDropItem(dropStack, 0.5F);
}
}
/**
* Deployable guns only
*/
@Override
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer entityplayer, EnumHand hand)
{
ItemStack itemstack = entityplayer.getHeldItem(hand);
if(type.deployable)
{
//Raytracing
float cosYaw = MathHelper.cos(-entityplayer.rotationYaw * 0.01745329F - 3.141593F);
float sinYaw = MathHelper.sin(-entityplayer.rotationYaw * 0.01745329F - 3.141593F);
float cosPitch = -MathHelper.cos(-entityplayer.rotationPitch * 0.01745329F);
float sinPitch = MathHelper.sin(-entityplayer.rotationPitch * 0.01745329F);
double length = 5D;
Vec3d posVec = new Vec3d(entityplayer.posX, entityplayer.posY + 1.62D - entityplayer.getYOffset(), entityplayer.posZ);
Vec3d lookVec = posVec.add(sinYaw * cosPitch * length, sinPitch * length, cosYaw * cosPitch * length);
RayTraceResult look = world.rayTraceBlocks(posVec, lookVec, true);
//Result check
if(look != null && look.typeOfHit == Type.BLOCK)
{
if(look.sideHit == EnumFacing.UP)
{
int playerDir = MathHelper.floor(((entityplayer.rotationYaw * 4F) / 360F) + 0.5D) & 3;
int i = look.getBlockPos().getX();
int j = look.getBlockPos().getY();
int k = look.getBlockPos().getZ();
if(!world.isRemote)
{
if(world.getBlockState(new BlockPos(i, j, k)).getBlock() == Blocks.SNOW)
{
j--;
}
if(isSolid(world, i, j, k) &&
(world.getBlockState(new BlockPos(i, j + 1, k)).getBlock() == Blocks.AIR || world.getBlockState(new BlockPos(i, j + 1, k)).getBlock() == Blocks.SNOW)
&&
(world.getBlockState(new BlockPos(i + (playerDir == 1 ? 1 : 0) - (playerDir == 3 ? 1 : 0), j + 1, k - (playerDir == 0 ? 1 : 0) + (playerDir == 2 ? 1 : 0))).getBlock() == Blocks.AIR)
&&
(world.getBlockState(new BlockPos(i + (playerDir == 1 ? 1 : 0) - (playerDir == 3 ? 1 : 0), j, k - (playerDir == 0 ? 1 : 0) + (playerDir == 2 ? 1 : 0))).getBlock() == Blocks.AIR
|| world.getBlockState(new BlockPos(i + (playerDir == 1 ? 1 : 0) - (playerDir == 3 ? 1 : 0), j, k - (playerDir == 0 ? 1 : 0) + (playerDir == 2 ? 1 : 0))).getBlock() == Blocks.SNOW))
{
for(EntityMG mg : EntityMG.mgs)
{
if(mg.blockX == i && mg.blockY == j + 1 && mg.blockZ == k && !mg.isDead)
return new ActionResult<>(EnumActionResult.SUCCESS, itemstack);
}
EntityMG mg = new EntityMG(world, i, j + 1, k, playerDir, type);
if(getBulletItemStack(itemstack, 0) != null)
{
mg.ammo = getBulletItemStack(itemstack, 0);
}
world.spawnEntity(mg);
if(!entityplayer.capabilities.isCreativeMode)
itemstack.setCount(0);
}
}
}
}
}
//Stop the gun bobbing up and down when holding shoot and looking at a block
if(world.isRemote)
{
for(int i = 0; i < 3; i++)
Minecraft.getMinecraft().entityRenderer.itemRenderer.updateEquippedItem();
}
return new ActionResult<>(EnumActionResult.PASS, itemstack);
}
// _____________________________________________________________________________
//
// Shooting code
// _____________________________________________________________________________
@SideOnly(Side.CLIENT)
public void onUpdateClient(ItemStack gunstack, int gunSlot, World world, Entity entity, EnumHand hand, boolean hasOffHand)
{
if(!(entity instanceof EntityPlayer))
{
return;
}
// Get useful objects
Minecraft mc = Minecraft.getMinecraft();
EntityPlayer player = (EntityPlayer)entity;
PlayerData data = PlayerHandler.getPlayerData(player, Side.CLIENT);
// Play idle sounds
if(soundDelay <= 0 && type.idleSound != null)
{
PacketPlaySound.sendSoundPacket(entity.posX, entity.posY, entity.posZ, FlansMod.soundRange, entity.dimension, type.idleSound, false);
soundDelay = type.idleSoundLength;
}
// This code is not for deployables
if(type.deployable)
return;
// Do not shoot ammo bags, flags or dropped gun items
if(mc.objectMouseOver != null && (mc.objectMouseOver.entityHit instanceof EntityFlagpole || mc.objectMouseOver.entityHit instanceof EntityFlag || mc.objectMouseOver.entityHit instanceof EntityGunItem || (mc.objectMouseOver.entityHit instanceof EntityGrenade && ((EntityGrenade)mc.objectMouseOver.entityHit).type.isDeployableBag)))
return;
if(hasOffHand && !type.oneHanded)
return;
// If we have an off hand item, then disable our secondary functions
boolean secondaryFunctionsEnabled = true;
if(type.usableByPlayers)
{
boolean needsToReload = needsToReload(gunstack);
boolean shouldShootThisTick = false;
switch(type.getFireMode(gunstack))
{
case BURST:
{
if(data.GetBurstRoundsRemaining(hand) > 0)
{
shouldShootThisTick = true;
}
// Fallthrough to semi auto
}
case SEMIAUTO:
{
if(GetMouseHeld(hand) && !GetLastMouseHeld(hand))
{
shouldShootThisTick = true;
}
else needsToReload = false;
break;
}
case MINIGUN:
{
if(needsToReload)
{
needsToReload = GetMouseHeld(hand);
break;
}
if(GetMouseHeld(hand))
{
data.minigunSpeed += 2.0f;
data.minigunSpeed *= 0.9f;
// TODO : Re-add looping sounds
if(data.minigunSpeed < type.minigunStartSpeed)
{
if(type.useLoopingSounds)
{
data.shouldPlayWarmupSound = true;
}
break;
}
}
else if(data.minigunSpeed > 0.0f)
{
data.shouldPlayCooldownSound = true;
}
//else fallthrough to full auto
}
case FULLAUTO:
{
shouldShootThisTick = GetMouseHeld(hand);
if(!shouldShootThisTick)
{
needsToReload = false;
}
break;
}
default:
needsToReload = false;
break;
}
// Do reload if we pressed fire.
if(needsToReload)
{
if(Reload(gunstack, world, player, player.inventory, hand, hasOffHand, false, player.capabilities.isCreativeMode))
{
//Set player shoot delay to be the reload delay
//Set both gun delays to avoid reloading two guns at once
data.shootTimeRight = data.shootTimeLeft = (int)type.getReloadTime(gunstack);
GunAnimations animations = FlansModClient.getGunAnimations(player, hand);
int pumpDelay = type.model == null ? 0 : type.model.pumpDelayAfterReload;
int pumpTime = type.model == null ? 1 : type.model.pumpTime;
animations.doReload(type.reloadTime, pumpDelay, pumpTime);
if(hand == EnumHand.OFF_HAND)
{
data.reloadingLeft = true;
data.burstRoundsRemainingLeft = 0;
}
else
{
data.reloadingRight = true;
data.burstRoundsRemainingRight = 0;
}
//Send reload packet to server
FlansMod.getPacketHandler().sendToServer(new PacketReload(hand, false));
}
}
// Fire!
else if(shouldShootThisTick)
{
GunAnimations animations = FlansModClient.getGunAnimations(player, hand);
animations.lookAt = LookAtState.NONE;
float shootTime = data.GetShootTime(hand);
// For each
while(shootTime <= 0.0f)
{
// Add the delay for this shot and shoot it!
shootTime += type.GetShootDelay(gunstack);
ItemStack shootableStack = getBestNonEmptyShootableStack(gunstack);
if(shootableStack == null || shootableStack.isEmpty())
{
continue;
}
ItemShootable shootableItem = (ItemShootable)shootableStack.getItem();
ShootableType shootableType = shootableItem.type;
// Instant bullets. Do a raytrace
if(type.bulletSpeed == 0.0f)
{
for(int i = 0; i < type.numBullets * shootableType.numBullets; i++)
{
Vector3f rayTraceOrigin = new Vector3f(player.getPositionEyes(0.0f));
Vector3f rayTraceDirection = new Vector3f(player.getLookVec());
float spread = 0.0025f * type.getSpread(gunstack) * shootableType.bulletSpread;
rayTraceDirection.x += (float)world.rand.nextGaussian() * spread;
rayTraceDirection.y += (float)world.rand.nextGaussian() * spread;
rayTraceDirection.z += (float)world.rand.nextGaussian() * spread;
rayTraceDirection.scale(500.0f);
List<BulletHit> hits = FlansModRaytracer.Raytrace(world, player, false, null, rayTraceOrigin, rayTraceDirection, 0);
Entity victim = null;
Vector3f hitPos = Vector3f.add(rayTraceOrigin, rayTraceDirection, null);
BulletHit firstHit = null;
if(!hits.isEmpty())
{
firstHit = hits.get(0);
hitPos = Vector3f.add(rayTraceOrigin, (Vector3f)rayTraceDirection.scale(firstHit.intersectTime), null);
victim = firstHit.GetEntity();
}
Vector3f gunOrigin = FlansModRaytracer.GetPlayerMuzzlePosition(player, hand);
if(FlansMod.DEBUG)
{
world.spawnEntity(new EntityDebugDot(world, gunOrigin, 100, 1.0f, 1.0f, 1.0f));
}
boolean silenced = type.getBarrel(gunstack) != null && type.getBarrel(gunstack).silencer;
ShotData shotData = new InstantShotData(gunSlot, hand, type, shootableType, player, gunOrigin, firstHit, hitPos, type.getDamage(gunstack), i < type.numBullets * shootableType.numBullets - 1, silenced);
shotsFiredClient.add(shotData);
}
}
// Else, spawn an entity
else
{
ShotData shotData = new SpawnEntityShotData(gunSlot, hand, type, shootableType, player, new Vector3f(player.getLookVec()));
shotsFiredClient.add(shotData);
}
// Now do client side things
int pumpDelay = type.model == null ? 0 : type.model.pumpDelay;
int pumpTime = type.model == null ? 1 : type.model.pumpTime;
animations.doShoot(pumpDelay, pumpTime);
FlansModClient.playerRecoil += type.getRecoil(gunstack);
animations.recoil += type.getRecoil(gunstack);
if(type.consumeGunUponUse)
player.inventory.setInventorySlotContents(gunSlot, ItemStack.EMPTY.copy());
// Update burst fire
if(type.getFireMode(gunstack) == EnumFireMode.BURST)
{
int burstRoundsRemaining = data.GetBurstRoundsRemaining(hand);
if(burstRoundsRemaining > 0)
burstRoundsRemaining--;
else burstRoundsRemaining = type.numBurstRounds;
data.SetBurstRoundsRemaining(hand, burstRoundsRemaining);
}
}
data.SetShootTime(hand, shootTime);
}
Vector3f gunOrigin = FlansModRaytracer.GetPlayerMuzzlePosition(player, hand);
if(FlansMod.DEBUG)
{
world.spawnEntity(new EntityDebugDot(world, gunOrigin, 100, 1.0f, 1.0f, 1.0f));
}
// Now send shooting data to the server
if(!shotsFiredClient.isEmpty() && player.ticksExisted % CLIENT_TO_SERVER_UPDATE_INTERVAL == 0)
{
FlansMod.getPacketHandler().sendToServer(new PacketShotData(shotsFiredClient));
shotsFiredClient.clear();
}
// Check for scoping in / out
IScope currentScope = type.getCurrentScope(gunstack);
if(!hasOffHand)
{
switch(hand)
{
case MAIN_HAND:
{
if(GetMouseHeld(EnumHand.OFF_HAND) && !GetLastMouseHeld(EnumHand.OFF_HAND)
&& (type.secondaryFunction == EnumSecondaryFunction.ADS_ZOOM || type.secondaryFunction == EnumSecondaryFunction.ZOOM))
{
FlansModClient.SetScope(currentScope);
}
break;
}
case OFF_HAND:
{
if(GetMouseHeld(EnumHand.MAIN_HAND) && !GetLastMouseHeld(EnumHand.MAIN_HAND)
&& (type.secondaryFunction == EnumSecondaryFunction.ADS_ZOOM || type.secondaryFunction == EnumSecondaryFunction.ZOOM))
{
FlansModClient.SetScope(currentScope);
}
break;
}
}
}
}
// And finally do sounds
if(soundDelay > 0)
{
soundDelay--;
}
}
public void ServerHandleShotData(ItemStack gunstack, int gunSlot, World world, Entity entity, boolean isOffHand, ShotData shotData)
{
// Get useful things
if(!(entity instanceof EntityPlayerMP)) //Vehicle entities quickly make the method return nothing, and so the shots are never handled.
{
return;
}
EntityPlayerMP player = (EntityPlayerMP)entity;
PlayerData data = PlayerHandler.getPlayerData(player, Side.SERVER);
if(data == null)
{
return;
}
boolean isExtraBullet = shotData instanceof InstantShotData && ((InstantShotData)shotData).isExtraBullet;
//Go through the bullet stacks in the gun and see if any of them are not null
int bulletID = 0;
ItemStack bulletStack = ItemStack.EMPTY.copy();
for(; bulletID < type.numAmmoItemsInGun; bulletID++)
{
ItemStack checkingStack = getBulletItemStack(gunstack, bulletID);
if(checkingStack != null && checkingStack.getItemDamage() < checkingStack.getMaxDamage())
{
bulletStack = checkingStack;
break;
}
}
// We have no bullet stack. So we need to reload. The player will send us a message requesting we do a reload
if(bulletStack.isEmpty())
{
return;
}
if(bulletStack.getItem() instanceof ItemShootable)
{
ShootableType bullet = ((ItemShootable)bulletStack.getItem()).type;
if(!isExtraBullet)
{
// Drop item on shooting if bullet requires it
if(bullet.dropItemOnShoot != null && !player.capabilities.isCreativeMode)
dropItem(world, player, bullet.dropItemOnShoot);
// Drop item on shooting if gun requires it
if(type.dropItemOnShoot != null)// && !entityplayer.capabilities.isCreativeMode)
dropItem(world, player, type.dropItemOnShoot);
if(type.knockback > 0)
{
//TODO : Apply knockback
}
//Damage the bullet item
bulletStack.setItemDamage(bulletStack.getItemDamage() + 1);
//Update the stack in the gun
setBulletItemStack(gunstack, bulletStack, bulletID);
if(type.consumeGunUponUse && gunSlot != -1)
player.inventory.setInventorySlotContents(gunSlot, ItemStack.EMPTY.copy());
}
// Spawn an entity, classic style
if(shotData instanceof SpawnEntityShotData)
{
// Play a sound if the previous sound has finished
if(soundDelay <= 0 && type.shootSound != null)
{
AttachmentType barrel = type.getBarrel(gunstack);
boolean silenced = barrel != null && barrel.silencer;
//world.playSoundAtEntity(entityplayer, type.shootSound, 10F, type.distortSound ? 1.0F / (world.rand.nextFloat() * 0.4F + 0.8F) : 1.0F);
PacketPlaySound.sendSoundPacket(player.posX, player.posY, player.posZ, FlansMod.soundRange, player.dimension, type.shootSound, type.distortSound, silenced);
soundDelay = type.shootSoundLength;
}
//Shoot
// Spawn the bullet entities
for(int k = 0; k < type.numBullets * bullet.numBullets; k++)
{
// Actually shoot the bullet
((ItemShootable)bulletStack.getItem()).Shoot(world,
new Vector3f(player.posX, player.posY + player.getEyeHeight(), player.posZ),
new Vector3f(player.getLookVec()),
type.getDamage(gunstack),
(player.isSneaking() ? 0.7F : 1F) * type.getSpread(gunstack) * bullet.bulletSpread,
type.getBulletSpeed(gunstack),
type,
player);
}
}
// Do a raytrace check on what they've sent us.
else if(shotData instanceof InstantShotData)
{
InstantShotData instantData = (InstantShotData)shotData;
//if(stuff)
//{
// calculate our own raytrace to verify they're not cheating
//}
// else
{
// Take a point halfway along. Then make the radius encapsulate both ends and then some
//Vector3f targetPoint = Vector3f.add(instantData.origin, instantData.hitPos, null);
//targetPoint.scale(0.5f);
//float radius = Vector3f.sub(instantData.origin, instantData.hitPos, null).length();
//radius += 50.0f;
AttachmentType barrel = type.getBarrel(gunstack);
boolean silenced = barrel != null && barrel.silencer;
DoInstantShot(world, player, type, (BulletType)bullet, instantData.origin, instantData.hitPos, instantData.hitData, type.getDamage(gunstack), isExtraBullet, silenced);
shotsFiredServer.add(shotData);
}
}
}
}
// I wanted to make a method specific to handling vehicle weapons, but I don't know enough about flans to hack one up. This whole thing doesn't work, don't expect it to.
public void ServerHandleDrivableShotData(ItemStack gunstack, int gunSlot, World world, Entity entity, Entity driver, boolean isOffHand, ShotData shotData)
{
// Get useful things
if(!(driver instanceof EntityPlayerMP))
{
return;
}
EntityPlayerMP player = (EntityPlayerMP)driver;
PlayerData data = PlayerHandler.getPlayerData(player, Side.SERVER);
if(data == null)
{
return;
}
boolean isExtraBullet = shotData instanceof InstantShotData && ((InstantShotData)shotData).isExtraBullet;
//Go through the bullet stacks in the gun and see if any of them are not null
int bulletID = 0;
ItemStack bulletStack = ItemStack.EMPTY.copy();
for(; bulletID < type.numAmmoItemsInGun; bulletID++)
{
ItemStack checkingStack = getBulletItemStack(gunstack, bulletID); //Hey
if(checkingStack != null && checkingStack.getItemDamage() < checkingStack.getMaxDamage())
{
bulletStack = checkingStack;
break;
}
}
// We have no bullet stack. So we need to reload. The player will send us a message requesting we do a reload
// if(bulletStack.isEmpty())
// {
// return;
// }
// if(bulletStack.getItem() instanceof ItemShootable)
if(true)
{
// ShootableType bullet = ((ItemShootable)bulletStack.getItem()).type;
//
// if(!isExtraBullet)
// {
// //Damage the bullet item
// bulletStack.setItemDamage(bulletStack.getItemDamage() + 1);
//
// //Update the stack in the gun
// setBulletItemStack(gunstack, bulletStack, bulletID);
//
// }
// Spawn an entity, classic style
if(shotData instanceof SpawnEntityShotData)
{
// Play a sound if the previous sound has finished
if(soundDelay <= 0 && type.shootSound != null)
{
AttachmentType barrel = type.getBarrel(gunstack);
boolean silenced = barrel != null && barrel.silencer;
//world.playSoundAtEntity(entityplayer, type.shootSound, 10F, type.distortSound ? 1.0F / (world.rand.nextFloat() * 0.4F + 0.8F) : 1.0F);
PacketPlaySound.sendSoundPacket(player.posX, player.posY, player.posZ, FlansMod.soundRange, player.dimension, type.shootSound, type.distortSound, silenced);
soundDelay = type.shootSoundLength;
}
//Shoot
// Spawn the bullet entities
for(int k = 0; k < type.numBullets * bullet.numBullets; k++)
{
// Actually shoot the bullet
((ItemShootable)bulletStack.getItem()).Shoot(world,
new Vector3f(player.posX, player.posY + player.getEyeHeight(), player.posZ),
new Vector3f(player.getLookVec()),
type.getDamage(gunstack),
(player.isSneaking() ? 0.7F : 1F) * type.getSpread(gunstack) * bullet.bulletSpread,
type.getBulletSpeed(gunstack),
type,
player);
}
}
// Do a raytrace check on what they've sent us.
else if(shotData instanceof InstantShotData)
{
InstantShotData instantData = (InstantShotData)shotData;
//if(stuff)
//{
// calculate our own raytrace to verify they're not cheating
//}
// else
{
// Take a point halfway along. Then make the radius encapsulate both ends and then some
//Vector3f targetPoint = Vector3f.add(instantData.origin, instantData.hitPos, null);
//targetPoint.scale(0.5f);
//float radius = Vector3f.sub(instantData.origin, instantData.hitPos, null).length();
//radius += 50.0f;
AttachmentType barrel = type.getBarrel(gunstack);
boolean silenced = barrel != null && barrel.silencer;
DoInstantShot(world, player, type, (BulletType)bullet, instantData.origin, instantData.hitPos, instantData.hitData, type.getDamage(gunstack), isExtraBullet, silenced);
shotsFiredServer.add(shotData);
}
}
}
}
@SideOnly(Side.CLIENT)
private void PlayShotSound(World world, boolean silenced, float x, float y, float z)
{
FMLClientHandler.instance().getClient().getSoundHandler().playSound(
new PositionedSoundRecord(FlansModResourceHandler.getSoundEvent(type.shootSound),
SoundCategory.PLAYERS,
silenced ? 5F : 10F,
(type.distortSound ? 1.0F / (world.rand.nextFloat() * 0.4F + 0.8F) : 1.0F) * (silenced ? 2F : 1F),
x, y, z));
}
public void DoInstantShot(World world, EntityLivingBase shooter, InfoType shotFrom, BulletType shotType, Vector3f origin, Vector3f hit, BulletHit hitData, float damage, boolean isExtraBullet, boolean silenced)
{
if(EntityBullet.OnHit(world, origin, hit, shooter, shotFrom, shotType, null, damage, hitData))
{
EntityBullet fakeBullet = new EntityBullet(world, hit.toVec3(), 0f, 0f, shooter, 0f, 0f, shotType, 0f, shotFrom);
EntityBullet.OnDetonate(world, hit, shooter, fakeBullet, shotFrom, shotType);
}
if(world.isRemote)
{
// Play a sound if the previous sound has finished
if(!isExtraBullet && soundDelay <= 0 && type.shootSound != null && shooter != null)
{
PlayShotSound(world, silenced, (float)shooter.posX, (float)shooter.posY, (float)shooter.posZ);
soundDelay = type.shootSoundLength;
}
if(FlansMod.DEBUG)
{
world.spawnEntity(new EntityDebugVector(world, origin, Vector3f.sub(hit, origin, null), 100, 0.5f, 0.5f, 1.0f));
}
InstantBulletRenderer.AddTrail(new InstantShotTrail(origin, hit, shotType));
if(hitData instanceof BlockHit)
{
BlockHit blockHit = (BlockHit)hitData;
BlockPos blockPos = blockHit.raytraceResult.getBlockPos();
IBlockState blockState = world.getBlockState(blockHit.raytraceResult.getBlockPos());
Vec3i normal = blockHit.raytraceResult.sideHit.getDirectionVec();
Vector3f bulletDir = Vector3f.sub(hit, origin, null);
bulletDir.normalise();
bulletDir.scale(0.5f);
for(int i = 0; i < 2; i++)
{
// TODO: [1.12] Check why this isn't moving right
float scale = (float)world.rand.nextGaussian() * 0.1f + 0.5f;
double motionX = (double)normal.getX() * scale + world.rand.nextGaussian() * 0.025d;
double motionY = (double)normal.getY() * scale + world.rand.nextGaussian() * 0.025d;
double motionZ = (double)normal.getZ() * scale + world.rand.nextGaussian() * 0.025d;
motionX += bulletDir.x;
motionY += bulletDir.y;
motionZ += bulletDir.z;
Minecraft.getMinecraft().effectRenderer.spawnEffectParticle(
EnumParticleTypes.BLOCK_CRACK.getParticleID(), hit.x, hit.y, hit.z, motionX, motionY, motionZ,
Block.getIdFromBlock(blockState.getBlock()));
}
double scale = world.rand.nextGaussian() * 0.05d + 0.05d;
double motionX = (double)normal.getX() * scale + world.rand.nextGaussian() * 0.025d;
double motionY = (double)normal.getY() * scale + world.rand.nextGaussian() * 0.025d;
double motionZ = (double)normal.getZ() * scale + world.rand.nextGaussian() * 0.025d;
Particle fx = Minecraft.getMinecraft().effectRenderer.spawnEffectParticle(EnumParticleTypes.CLOUD.getParticleID(), hit.x, hit.y, hit.z, motionX, motionY, motionZ);
}
if(shooter == Minecraft.getMinecraft().player)
{
if(hitData instanceof EntityHit || hitData instanceof DriveableHit)
{
// Add a hit marker
FlansModClient.AddHitMarker();
}
else if(hitData instanceof PlayerBulletHit)
{
// Check teams
if(FlansModClient.teamInfo != null)
{
Team shooterTeam = FlansModClient.teamInfo.getTeam((EntityPlayer)shooter);
Team victimTeam = FlansModClient.teamInfo.getTeam(((PlayerBulletHit)hitData).hitbox.player);
if(shooterTeam == null || shooterTeam != victimTeam)
{
FlansModClient.AddHitMarker();
}
}
else // No teams mod, just add marker
{
FlansModClient.AddHitMarker();
}
}
}
}
}
public void onUpdateServer(ItemStack itemstack, int gunSlot, World world, Entity entity, EnumHand hand, boolean hasOffHand)
{
if(!(entity instanceof EntityPlayerMP))
{
return;
}
EntityPlayerMP player = (EntityPlayerMP)entity;
PlayerData data = PlayerHandler.getPlayerData(player);
if(data == null)
return;
if(player.inventory.getCurrentItem() != itemstack)
{
//If the player is no longer holding a gun, emulate a release of the shoot button
if(player.inventory.getCurrentItem().isEmpty() || !(player.inventory.getCurrentItem().getItem() instanceof ItemGun))
{
data.isShootingRight = data.isShootingLeft = false;
}
return;
}
if(!shotsFiredServer.isEmpty())// && entity.ticksExisted % SERVER_TO_CLIENT_UPDATE_INTERVAL == 0)
{
FlansMod.getPacketHandler().sendToDimension(new PacketShotData(shotsFiredServer), player.dimension);
shotsFiredServer.clear();
}
}
/**
* Generic update method. If we have an off hand weapon, it will also make calls for that
* Passes on to onUpdateEach
*/
@Override
public void onUpdate(ItemStack itemstack, World world, Entity entity, int i, boolean flag)
{
if(entity instanceof EntityPlayer)
{
EntityPlayer player = (EntityPlayer)entity;
EnumHand hand;
if(itemstack == player.getHeldItemMainhand())
{
hand = EnumHand.MAIN_HAND;
}
else if(itemstack == player.getHeldItemOffhand())
{
hand = EnumHand.OFF_HAND;
}
else
{
return;
}
if(world.isRemote && Minecraft.getMinecraft().currentScreen == null)
{
// Get button presses. Do this before splitting into each hand. Prevents second pass wiping the data
lastRightMouseHeld = rightMouseHeld;
lastLeftMouseHeld = leftMouseHeld;
rightMouseHeld = Mouse.isButtonDown(1);
leftMouseHeld = Mouse.isButtonDown(0);
}
ItemStack main = player.getHeldItemMainhand();
ItemStack off = player.getHeldItemOffhand();
boolean hasOffHand = !main.isEmpty() && !off.isEmpty();
onUpdateEach(itemstack, i, world, entity, hand, hasOffHand);
}
}
/**
* Called once for each weapon we are weilding
*/
private void onUpdateEach(ItemStack itemstack, int gunSlot, World world, Entity entity, EnumHand hand, boolean hasOffHand)
{
if(world.isRemote)
onUpdateClient(itemstack, gunSlot, world, entity, hand, hasOffHand);
else onUpdateServer(itemstack, gunSlot, world, entity, hand, hasOffHand);
}
public boolean Reload(ItemStack gunstack, World world, Entity entity, IInventory inventory, EnumHand hand, boolean hasOffHand, boolean forceReload, boolean isCreative)
{
//Deployable guns cannot be reloaded in the inventory
if(type.deployable)
return false;
//If you cannot reload half way through a clip, reject the player for trying to do so
if(forceReload && !type.canForceReload)
return false;
//For playing sounds afterwards
boolean reloadedSomething = false;
//Check each ammo slot, one at a time
for(int i = 0; i < type.numAmmoItemsInGun; i++)
{
//Get the stack in the slot
ItemStack bulletStack = getBulletItemStack(gunstack, i);
//If there is no magazine, if the magazine is empty or if this is a forced reload
if(bulletStack == null || bulletStack.isEmpty() || bulletStack.getItemDamage() == bulletStack.getMaxDamage() || forceReload)
{
//Iterate over all inventory slots and find the magazine / bullet item with the most bullets
int bestSlot = -1;
int bulletsInBestSlot = 0;
for(int j = 0; j < inventory.getSizeInventory(); j++)
{
ItemStack item = inventory.getStackInSlot(j);
if(item.getItem() instanceof ItemShootable && type.isAmmo(((ItemShootable)(item.getItem())).type))
{
int bulletsInThisSlot = item.getMaxDamage() - item.getItemDamage();
if(bulletsInThisSlot > bulletsInBestSlot)
{
bestSlot = j;
bulletsInBestSlot = bulletsInThisSlot;
}
}
}
//If there was a valid non-empty magazine / bullet item somewhere in the inventory, load it
if(bestSlot != -1)
{
ItemStack newBulletStack = inventory.getStackInSlot(bestSlot);
ShootableType newBulletType = ((ItemShootable)newBulletStack.getItem()).type;
//Unload the old magazine (Drop an item if it is required and the player is not in creative mode)
if(bulletStack != null && bulletStack.getItem() instanceof ItemShootable && ((ItemShootable)bulletStack.getItem()).type.dropItemOnReload != null && !isCreative && bulletStack.getItemDamage() == bulletStack.getMaxDamage())
{
if(!world.isRemote)
dropItem(world, entity, ((ItemShootable)bulletStack.getItem()).type.dropItemOnReload);
}
//The magazine was not finished, pull it out and give it back to the player or, failing that, drop it
if(bulletStack != null && !bulletStack.isEmpty() && bulletStack.getItemDamage() < bulletStack.getMaxDamage())
{
if(!InventoryHelper.addItemStackToInventory(inventory, bulletStack, isCreative))
{
if(!world.isRemote)
entity.entityDropItem(bulletStack, 0.5F);
}
}
//Load the new magazine
ItemStack stackToLoad = newBulletStack.copy();
stackToLoad.setCount(1);
setBulletItemStack(gunstack, stackToLoad, i);
//Remove the magazine from the inventory
if(!isCreative)
newBulletStack.setCount(newBulletStack.getCount() - 1);
if(newBulletStack.getCount() <= 0)
newBulletStack = ItemStack.EMPTY.copy();
inventory.setInventorySlotContents(bestSlot, newBulletStack);
//Tell the sound player that we reloaded something
reloadedSomething = true;
}
}
}
return reloadedSomething;
}
// TODO : All this bunk
/* Melee MESS
* @Override
public void onUpdate(ItemStack itemstack, World world, Entity pEnt, int i, boolean flag)
{
if(world.isRemote)
onUpdateClient(itemstack, world, pEnt, i, flag);
else onUpdateServer(itemstack, world, pEnt, i, flag);
if(pEnt instanceof EntityPlayer)
{
EntityPlayer player = (EntityPlayer)pEnt;
PlayerData data = PlayerHandler.getPlayerData(player);
if(data == null)
return;
//if(data.lastMeleePositions == null || data.lastMeleePositions.length != type.meleeDamagePoints.size())
//{
// data.lastMeleePositions = new Vector3f[type.meleeDamagePoints.size()];
// for(int j = 0; j < type.meleeDamagePoints.size(); j++)
// data.lastMeleePositions[j] = new Vector3f(player.posX, player.posY, player.posZ);
//}
//Melee weapon
if(data.meleeLength > 0 && type.meleePath.size() > 0 && player.inventory.getCurrentItem() == itemstack)
{
for(int k = 0; k < type.meleeDamagePoints.size(); k++)
{
Vector3f meleeDamagePoint = type.meleeDamagePoints.get(k);
//Do a raytrace from the prev pos to the current pos and attack anything in the way
Vector3f nextPos = type.meleePath.get((data.meleeProgress + 1) % type.meleePath.size());
Vector3f nextAngles = type.meleePathAngles.get((data.meleeProgress + 1) % type.meleePathAngles.size());
RotatedAxes nextAxes = new RotatedAxes().rotateGlobalRoll(-nextAngles.x).rotateGlobalPitch(-nextAngles.z).rotateGlobalYaw(-nextAngles.y);
Vector3f nextPosInGunCoords = nextAxes.findLocalVectorGlobally(meleeDamagePoint);
Vector3f.add(nextPos, nextPosInGunCoords, nextPosInGunCoords);
Vector3f.add(new Vector3f(0F, 0F, 0F), nextPosInGunCoords, nextPosInGunCoords);
Vector3f nextPosInPlayerCoords = new RotatedAxes(player.rotationYaw + 90F, player.rotationPitch, 0F).findLocalVectorGlobally(nextPosInGunCoords);
if(!FlansMod.proxy.isThePlayer(player))
nextPosInPlayerCoords.y += 1.6F;
Vector3f nextPosInWorldCoords = new Vector3f(player.posX + nextPosInPlayerCoords.x, player.posY + nextPosInPlayerCoords.y, player.posZ + nextPosInPlayerCoords.z);
Vector3f dPos = data.lastMeleePositions[k] == null ? new Vector3f() : Vector3f.sub(nextPosInWorldCoords, data.lastMeleePositions[k], null);
if(player.world.isRemote && FlansMod.DEBUG)
player.world.spawnEntity(new EntityDebugVector(player.world, data.lastMeleePositions[k], dPos, 200, 1F, 0F, 0F));
//Do the raytrace
{
//Create a list for all bullet hits
ArrayList<BulletHit> hits = new ArrayList<BulletHit>();
//Iterate over all entities
for(int j = 0; j < world.loadedEntityList.size(); j++)
{
Object obj = world.loadedEntityList.get(j);
//Get players
if(obj instanceof EntityPlayer)
{
EntityPlayer otherPlayer = (EntityPlayer)obj;
PlayerData otherData = PlayerHandler.getPlayerData(otherPlayer);
boolean shouldDoNormalHitDetect = false;
if(otherPlayer == player)
continue;
if(otherData != null)
{
if(otherPlayer.isDead || otherData.team == Team.spectators)
{
continue;
}
int snapshotToTry = player instanceof EntityPlayerMP ? ((EntityPlayerMP)player).ping / 50 : 0;
if(snapshotToTry >= otherData.snapshots.length)
snapshotToTry = otherData.snapshots.length - 1;
PlayerSnapshot snapshot = otherData.snapshots[snapshotToTry];
if(snapshot == null)
snapshot = otherData.snapshots[0];
//DEBUG
//snapshot = new PlayerSnapshot(player);
//Check one last time for a null snapshot. If this is the case, fall back to normal hit detection
if(snapshot == null)
shouldDoNormalHitDetect = true;
else
{
//Raytrace
ArrayList<BulletHit> playerHits = snapshot.raytrace(data.lastMeleePositions[k] == null ? nextPosInWorldCoords : data.lastMeleePositions[k], dPos);
hits.addAll(playerHits);
}
}
//If we couldn't get a snapshot, use normal entity hitbox calculations
if(otherData == null || shouldDoNormalHitDetect)
{
RayTraceResult mop = data.lastMeleePositions[k] == null ? player.getEntityBoundingBox().calculateIntercept(nextPosInWorldCoords.toVec3(), new Vec3d(0F, 0F, 0F)) : player.getBoundingBox().calculateIntercept(data.lastMeleePositions[k].toVec3(), nextPosInWorldCoords.toVec3());
if(mop != null)
{
Vector3f hitPoint = new Vector3f(mop.hitVec.x - data.lastMeleePositions[k].x, mop.hitVec.y - data.lastMeleePositions[k].y, mop.hitVec.z - data.lastMeleePositions[k].z);
float hitLambda = 1F;
if(dPos.x != 0F)
hitLambda = hitPoint.x / dPos.x;
else if(dPos.y != 0F)
hitLambda = hitPoint.y / dPos.y;
else if(dPos.z != 0F)
hitLambda = hitPoint.z / dPos.z;
if(hitLambda < 0)
hitLambda = -hitLambda;
hits.add(new PlayerBulletHit(new PlayerHitbox(otherPlayer, new RotatedAxes(), new Vector3f(), new Vector3f(), new Vector3f(), EnumHitboxType.BODY), hitLambda));
}
}
}
else
{
Entity entity = (Entity)obj;
if(entity != player && !entity.isDead && (entity instanceof EntityLivingBase || entity instanceof EntityAAGun))
{
RayTraceResult mop = entity.getEntityBoundingBox().calculateIntercept(data.lastMeleePositions[k].toVec3(), nextPosInWorldCoords.toVec3());
if(mop != null)
{
Vector3f hitPoint = new Vector3f(mop.hitVec.x - data.lastMeleePositions[k].x, mop.hitVec.y - data.lastMeleePositions[k].y, mop.hitVec.z - data.lastMeleePositions[k].z);
float hitLambda = 1F;
if(dPos.x != 0F)
hitLambda = hitPoint.x / dPos.x;
else if(dPos.y != 0F)
hitLambda = hitPoint.y / dPos.y;
else if(dPos.z != 0F)
hitLambda = hitPoint.z / dPos.z;
if(hitLambda < 0)
hitLambda = -hitLambda;
hits.add(new EntityHit(entity, hitLambda));
}
}
}
}
//We hit something
if(!hits.isEmpty())
{
//Sort the hits according to the intercept position
Collections.sort(hits);
float swingDistance = dPos.length();
for(BulletHit bulletHit : hits)
{
if(bulletHit instanceof PlayerBulletHit)
{
PlayerBulletHit playerHit = (PlayerBulletHit)bulletHit;
float damageMultiplier = 1F;
switch(playerHit.hitbox.type)
{
case LEFTITEM : case RIGHTITEM : //Hit a shield. Stop the swing.
{
data.meleeProgress = data.meleeLength = 0;
return;
}
case HEAD : damageMultiplier = 2F; break;
case RIGHTARM : case LEFTARM : damageMultiplier = 0.6F; break;
default :
}
if(playerHit.hitbox.player.attackEntityFrom(getMeleeDamage(player), swingDistance * type.meleeDamage))
{
//If the attack was allowed, we should remove their immortality cooldown so we can shoot them again. Without this, any rapid fire gun become useless
playerHit.hitbox.player.arrowHitTimer++;
playerHit.hitbox.player.hurtResistantTime = playerHit.hitbox.player.maxHurtResistantTime / 2;
}
if(FlansMod.DEBUG)
world.spawnEntity(new EntityDebugDot(world, new Vector3f(data.lastMeleePositions[k].x + dPos.x * playerHit.intersectTime, data.lastMeleePositions[k].y + dPos.y * playerHit.intersectTime, data.lastMeleePositions[k].z + dPos.z * playerHit.intersectTime), 1000, 1F, 0F, 0F));
}
else if(bulletHit instanceof EntityHit)
{
EntityHit entityHit = (EntityHit)bulletHit;
if(entityHit.entity.attackEntityFrom(DamageSource.causePlayerDamage(player), swingDistance * type.meleeDamage) && entityHit.entity instanceof EntityLivingBase)
{
EntityLivingBase living = (EntityLivingBase)entityHit.entity;
//If the attack was allowed, we should remove their immortality cooldown so we can shoot them again. Without this, any rapid fire gun become useless
living.arrowHitTimer++;
living.hurtResistantTime = living.maxHurtResistantTime / 2;
}
if(FlansMod.DEBUG)
world.spawnEntity(new EntityDebugDot(world, new Vector3f(data.lastMeleePositions[k].x + dPos.x * entityHit.intersectTime, data.lastMeleePositions[k].y + dPos.y * entityHit.intersectTime, data.lastMeleePositions[k].z + dPos.z * entityHit.intersectTime), 1000, 1F, 0F, 0F));
}
}
}
}
//End raytrace
data.lastMeleePositions[k] = nextPosInWorldCoords;
}
//Increment the progress meter
data.meleeProgress++;
//If we are done, reset the counters
if(data.meleeProgress == data.meleeLength)
data.meleeProgress = data.meleeLength = 0;
}
}
}
*
*/
private boolean needsToReload(ItemStack stack)
{
for(int i = 0; i < type.numAmmoItemsInGun; i++)
{
ItemStack bulletStack = getBulletItemStack(stack, i);
if(bulletStack != null && !bulletStack.isEmpty() && bulletStack.getItemDamage() < bulletStack.getMaxDamage())
{
return false;
}
}
return true;
}
public boolean CanReload(ItemStack gunstack, IInventory inventory)
{
for(int i = 0; i < inventory.getSizeInventory(); i++)
{
ItemStack stack = inventory.getStackInSlot(i);
if(type.isAmmo(stack))
{
return true;
}
}
return false;
}
private ItemStack getBestNonEmptyShootableStack(ItemStack stack)
{
for(int i = 0; i < type.numAmmoItemsInGun; i++)
{
ItemStack shootableStack = getBulletItemStack(stack, i);
if(shootableStack != null && !shootableStack.isEmpty() && shootableStack.getItemDamage() < shootableStack.getMaxDamage())
{
return shootableStack;
}
}
return null;
}
// _____________________________________________________________________________
//
// Minecraft base item overrides
// _____________________________________________________________________________
@Override
public void addInformation(ItemStack stack, World world, List<String> lines, ITooltipFlag b)
{
if(type.description != null)
{
Collections.addAll(lines, type.description.split("_"));
}
if(type.showDamage)
lines.add("\u00a79Damage" + "\u00a77: " + type.getDamage(stack));
if(type.showRecoil)
lines.add("\u00a79Recoil" + "\u00a77: " + type.getRecoil(stack));
if(type.showSpread)
lines.add("\u00a79Accuracy" + "\u00a77: " + type.getSpread(stack));
if(type.showReloadTime)
lines.add("\u00a79Reload Time" + "\u00a77: " + type.getReloadTime(stack) / 20 + "s");
for(AttachmentType attachment : type.getCurrentAttachments(stack))
{
if(type.showAttachments)
{
String line = attachment.name;
lines.add(line);
}
}
for(int i = 0; i < type.numAmmoItemsInGun; i++)
{
ItemStack bulletStack = getBulletItemStack(stack, i);
if(bulletStack != null && bulletStack.getItem() instanceof ItemBullet)
{
BulletType bulletType = ((ItemBullet)bulletStack.getItem()).type;
//String line = bulletType.name + (bulletStack.getMaxDamage() == 1 ? "" : " " + (bulletStack.getMaxDamage() - bulletStack.getItemDamage()) + "/" + bulletStack.getMaxDamage());
String line = bulletType.name + " " + (bulletStack.getMaxDamage() - bulletStack.getItemDamage()) + "/" + bulletStack.getMaxDamage();
lines.add(line);
}
}
}
@Override
/** Make sure client and server side NBTtags update */
public boolean getShareTag()
{
return true;
}
public DamageSource getMeleeDamage(EntityPlayer attacker)
{
return new EntityDamageSourceGun(type.shortName, attacker, attacker, type, false);
}
private boolean isSolid(World world, int i, int j, int k)
{
IBlockState state = world.getBlockState(new BlockPos(i, j, k));
return state.getMaterial().isSolid() && state.isOpaqueCube();
}
//Stop damage being done to entities when scoping etc.
@Override
public boolean onLeftClickEntity(ItemStack stack, EntityPlayer player, Entity entity)
{
return type.secondaryFunction != EnumSecondaryFunction.MELEE;
}
@Override
public boolean hasCustomEntity(ItemStack stack)
{
return true;
}
@Nullable
@Override
public Entity createEntity(World world, Entity location, ItemStack itemstack)
{
return new EntityItemCustomRender(location, itemstack);
}
@Override
public boolean isFull3D()
{
return true;
}
@Override
public boolean onEntitySwing(EntityLivingBase entityLiving, ItemStack stack)
{
if(type.meleeSound != null)
PacketPlaySound.sendSoundPacket(entityLiving.posX, entityLiving.posY, entityLiving.posZ, FlansMod.soundRange, entityLiving.dimension, type.meleeSound, true);
//Do custom melee code here
if(type.secondaryFunction == EnumSecondaryFunction.CUSTOM_MELEE)
{
//Do animation
if(entityLiving.world.isRemote)
{
GunAnimations animations = FlansModClient.getGunAnimations(entityLiving, EnumHand.MAIN_HAND);
animations.doMelee(type.meleeTime);
}
//Do custom melee hit detection
if(entityLiving instanceof EntityPlayer)
{
PlayerData data = PlayerHandler.getPlayerData((EntityPlayer)entityLiving);
data.doMelee((EntityPlayer)entityLiving, type.meleeTime, type);
}
}
return type.secondaryFunction != EnumSecondaryFunction.MELEE;
}
@Override
public boolean onBlockStartBreak(ItemStack itemstack, BlockPos pos, EntityPlayer player)
{
World world = player.world;
if(!world.isRemote)
{
// Client will still render block break if player is in creative so update block state
IBlockState state = world.getBlockState(pos);
world.notifyBlockUpdate(pos, state, state, 3);
}
return true;
}
@Override
public boolean canHarvestBlock(IBlockState state, ItemStack stack)
{
return false;
}
public boolean isItemStackDamageable()
{
return true;
}
// ----------------- Paintjobs -----------------
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items)
{
if(tab != FlansMod.tabFlanGuns && tab != CreativeTabs.SEARCH)
return;
PaintableType type = ((IPaintableItem)this).GetPaintableType();
if(FlansMod.addAllPaintjobsToCreative)
{
for(Paintjob paintjob : type.paintjobs)
addPaintjobToList(this, type, paintjob, items);
}
else addPaintjobToList(this, type, type.defaultPaintjob, items);
}
private void addPaintjobToList(Item item, PaintableType type, Paintjob paintjob, List<ItemStack> list)
{
ItemStack paintableStack = new ItemStack(item, 1, paintjob.ID);
NBTTagCompound tags = new NBTTagCompound();
paintableStack.setTagCompound(tags);
list.add(paintableStack);
}
// ---------------------------------------------
@Override
public int getMaxItemUseDuration(ItemStack par1ItemStack)
{
return 100;
}
@Override
public EnumAction getItemUseAction(ItemStack par1ItemStack)
{
return EnumAction.BOW;
}
protected static final UUID KNOCKBACK_RESIST_MODIFIER = UUID.fromString("77777777-645C-4F38-A497-9C13A33DB5CF");
protected static final UUID MOVEMENT_SPEED_MODIFIER = UUID.fromString("99999999-4180-4865-B01B-BCCE9785ACA3");
@Override
public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack)
{
Multimap<String, AttributeModifier> multimap = super.getAttributeModifiers(slot, stack);
if(slot == EntityEquipmentSlot.MAINHAND)
{
multimap.put(SharedMonsterAttributes.KNOCKBACK_RESISTANCE.getName(), new AttributeModifier(KNOCKBACK_RESIST_MODIFIER, "KnockbackResist", type.knockbackModifier, 0));
multimap.put(SharedMonsterAttributes.MOVEMENT_SPEED.getName(), new AttributeModifier(MOVEMENT_SPEED_MODIFIER, "MovementSpeed", type.moveSpeedModifier - 1.0f, 2));
multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(), new AttributeModifier(ATTACK_DAMAGE_MODIFIER, "Weapon modifier", type.meleeDamage, 0));
}
return multimap;
}
@Override
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged)
{
return slotChanged;
}
// For when we have custom paintjob names
//@Override
//public String getTranslationKey(ItemStack stack)
//{
// return getTranslationKey();//stack.getTagCompound().getString("Paint");
//}
@Override
public boolean canItemEditBlocks()
{
return false;
}
}
@Dellroy1908
Copy link

How do we implement this bug fix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment