Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,27 @@ public void setFromParameters(IAmmoType<?, T> ammoType, AmmoCore core, CoreType
this.components = components;
this.height = this.width = Math.max(0.25f, (ammoType.getCaliber()/16f));
float fraction = height/2f;
this.setEntityBoundingBox(this.aabb = new AxisAlignedBB(-fraction, -fraction, -fraction, fraction, fraction, fraction));
this.aabb = new AxisAlignedBB(-fraction, -fraction, -fraction, fraction, fraction, fraction);
this.setPosition(this.posX, this.posY, this.posZ);
}

@Override
public void setPosition(double x, double y, double z)
{
this.posX = x;
this.posY = y;
this.posZ = z;
float fraction = this.width / 2.0F;
this.setEntityBoundingBox(new AxisAlignedBB(x - fraction, y - fraction, z - fraction, x + fraction, y + fraction, z + fraction));
}

@Override
public void resetPositionToBB()
{
AxisAlignedBB box = this.getEntityBoundingBox();
this.posX = (box.minX + box.maxX) / 2.0D;
this.posY = (box.minY + box.maxY) / 2.0D; // We use center instead of minY
this.posZ = (box.minZ + box.maxZ) / 2.0D;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,49 +69,134 @@ else if(spinTicks > 0)
spin = MathHelper.wrapDegrees(spin+(spinDirection?-SPIN_DEGREES: SPIN_DEGREES));
}

private boolean hasRicochetedThisTick = false;

@Override
protected void doProjectileMotion()
{
this.hasRicochetedThisTick = false;

//Volumetric raytrace through the projectile's flight path
if(velocity <= 0)
{
double initX = motionX, initY = motionY, initZ = motionZ;
applyInMotionColision(this.world.getCollisionBoxes(this, aabb
.offset(posX, posY, posZ)
.expand(motionX, motionY, motionZ))
);
if(initX!=motionX||initY!=motionY||initZ!=motionZ)
spinTicks /= 2;
}
else
flightTracer.stepTrace(world, getPositionVector(), getNextPositionVector(), hit -> {
if(hit!=null)
flightTracer.stepTrace(world, getPositionVector(), getNextPositionVector(), hit -> {
if(hit!=null)
{
if(shouldDetonateAfterContact())
{
//Proximity fuses don't use penetration logic
if(shouldDetonateAfterContact())
{
detonate();
return true;
}
detonate();
return true;
}

//But other fuses do
switch(hit.typeOfHit)
{
case BLOCK:
return handleBlockDamage(hit);
case ENTITY:
return handleEntityDamage(hit);
}
switch(hit.typeOfHit)
{
case BLOCK:
return handleBlockDamage(hit);
case ENTITY:
return handleEntityDamage(hit);
}
return false;
});
}
return false;
});

if (this.isDead) return;

// Apply AABB collision (handles slow collisions, rolling, prevents clipping, and catches edge ricochets)
applyInMotionColision();

//Finalize the motion
posX += motionX;
posY += motionY;
posZ += motionZ;
this.setPosition(posX, posY, posZ);
}

private void applyInMotionColision()
{
double initX = motionX;
double initY = motionY;
double initZ = motionZ;

AxisAlignedBB offsetAABB = this.getEntityBoundingBox();

List<AxisAlignedBB> boxes = new java.util.ArrayList<>();
for (AxisAlignedBB box : this.world.getCollisionBoxes(this, offsetAABB.expand(motionX, motionY, motionZ))) {
if (owner != null && owner.getEntityBoundingBox() != null && box.intersects(owner.getEntityBoundingBox())) continue;

// Ignore fragile blocks (like leaves and glass) in AABB sweep so we pass through them
boolean isFragile = false;
net.minecraft.util.math.BlockPos bp = new net.minecraft.util.math.BlockPos(
box.minX + (box.maxX - box.minX)/2.0,
box.minY + (box.maxY - box.minY)/2.0,
box.minZ + (box.maxZ - box.minZ)/2.0
);
net.minecraft.block.state.IBlockState state = world.getBlockState(bp);
if (state.getBlock() != net.minecraft.init.Blocks.AIR) {
pl.pabilo8.immersiveintelligence.api.ammo.penetration.IPenetrationHandler pen = pl.pabilo8.immersiveintelligence.api.ammo.PenetrationRegistry.getPenetrationHandler(state);
if (pen != null && pen.getPenetrationHardness().compareTo(pl.pabilo8.immersiveintelligence.api.ammo.enums.PenetrationHardness.FRAGILE) <= 0) {
isFragile = true;
}
}

if (!isFragile) {
boxes.add(box);
}
}

for(AxisAlignedBB axisalignedbb : boxes)
motionY = axisalignedbb.calculateYOffset(offsetAABB, motionY);
offsetAABB = offsetAABB.offset(0, motionY, 0);

for(AxisAlignedBB axisalignedbb : boxes)
motionX = axisalignedbb.calculateXOffset(offsetAABB, motionX);
offsetAABB = offsetAABB.offset(motionX, 0, 0);

for(AxisAlignedBB axisalignedbb : boxes)
motionZ = axisalignedbb.calculateZOffset(offsetAABB, motionZ);

//check Y axis motion
if(this.onGround = (initY != motionY && initY < 0.0D))
gravityMotionY = 0;

// Handle grazing/edge bounces that FactoryTracer missed
// Skip if onHitRicochet already triggered this tick to prevent double-inversion!
if (!this.hasRicochetedThisTick && (initX != motionX || initY != motionY || initZ != motionZ)) {
double impactY = Math.abs(initY - motionY);

// If we ONLY hit the floor, and the impact is very weak (just gravity pulling us down while resting), do not bounce!
if (initX == motionX && initZ == motionZ && impactY > 0 && impactY <= 0.15) {
// Just resting on the ground, kill vertical base motion so friction can apply!
if (this.baseMotion.y > 0) {
Vec3d flattened = new Vec3d(this.baseMotion.x, 0, this.baseMotion.z);
if (flattened.lengthVector() > 0) {
this.baseMotion = flattened.normalize();
} else {
this.baseMotion = Vec3d.ZERO;
}
}
} else {
double bounceX = initX != motionX ? -initX : initX;
double bounceY = initY != motionY ? -initY : initY;
double bounceZ = initZ != motionZ ? -initZ : initZ;

Vec3d bouncedVelocity = new Vec3d(bounceX, bounceY, bounceZ).scale(0.45);

this.velocity = (float) bouncedVelocity.lengthVector();
if (this.velocity > 0.001) {
this.baseMotion = bouncedVelocity.normalize();
}
this.gravityMotionY = 0;

spinDirection = !spinDirection;
spinTicks += 180;

playSound(net.minecraft.init.SoundEvents.BLOCK_METAL_HIT, 0.5f, 1f);
}
}

// If the grenade has upward momentum after bouncing, it is NOT resting on the ground!
// This prevents updatePhysics from destroying 99% of its velocity on the next tick.
if (baseMotion.y > 0 && velocity > 0.05) {
this.onGround = false;
}
}

@Override
Expand Down Expand Up @@ -141,46 +226,59 @@ protected boolean handleBlockDamage(RayTraceResult hit)
@Override
protected void onHitRicochet(RayTraceResult hit, IPenetrationHandler handler)
{
//Get colision boxes
List<AxisAlignedBB> boxes = new ArrayList<>();
ForgeEventFactory.gatherCollisionBoxes(world, this, new AxisAlignedBB(hit.getBlockPos()).grow(width), boxes);
world.getBlockState(hit.getBlockPos()).addCollisionBoxToList(world, hit.getBlockPos(),
aabb.offset(hit.hitVec), boxes, this, false);
this.hasRicochetedThisTick = true;

//If ricochets are disabled, end the bullet's lifecycle
if(!pl.pabilo8.immersiveintelligence.api.ammo.utils.IIAmmoUtils.ammoRicochets)
{
detonate();
return;
}

// Don't let it bounce infinitely without sideHit
if (hit.sideHit == null)
{
// Guess side hit based on actual motion
hit.sideHit = net.minecraft.util.EnumFacing.getFacingFromVector((float)motionX, (float)motionY, (float)motionZ).getOpposite();
}

//Apply collisions
applyInMotionColision(boxes);
// We no longer manually teleport the grenade or zero out its motion!
// The AABB sweep (applyInMotionColision) will cleanly push the grenade exactly to the wall this tick,
// and the newly calculated baseMotion/velocity will take over on the next tick!

//Decay velocity
this.velocity *= 0.3f;
// Compute the true velocity vector before bounce
Vec3d trueVelocity = new Vec3d(baseMotion.x * velocity, baseMotion.y * velocity + gravityMotionY, baseMotion.z * velocity);

penetrationDepth -= handler.getThickness()*2;
gravityMotionY = 0;
spinDirection = !spinDirection;
spinTicks += 180;

//Sync with client
// Reflect the true velocity off the surface normal
Vec3d surfaceNormal = new Vec3d(hit.sideHit.getDirectionVec());
Vec3d bouncedVelocity = trueVelocity.subtract(
surfaceNormal.scale(2 * trueVelocity.dotProduct(surfaceNormal))
);

// Apply decay
bouncedVelocity = bouncedVelocity.scale(0.45);

// Set the new velocity and baseMotion
this.velocity = (float) bouncedVelocity.lengthVector();

if (this.velocity > 0.001)
this.baseMotion = bouncedVelocity.normalize();

updateEntityForEvent(SyncEvents.ENTITY_COLLISION);
}

/**
* Applies collision with selected bounding boxes for a moving grenade
*
* @param boxes List of bounding boxes to collide with
*/
private void applyInMotionColision(List<AxisAlignedBB> boxes)
{
double initY = motionY;
AxisAlignedBB offsetAABB = aabb.offset(posX, posY, posZ);
for(AxisAlignedBB axisalignedbb : boxes)
{
motionY = axisalignedbb.calculateYOffset(offsetAABB, motionY);
offsetAABB = offsetAABB.offset(0, motionY, 0);
motionX = axisalignedbb.calculateXOffset(offsetAABB, motionX);
offsetAABB = offsetAABB.offset(motionX, 0, 0);
motionZ = axisalignedbb.calculateZOffset(offsetAABB, motionZ);
offsetAABB = offsetAABB.offset(0, 0, motionZ);
}
//Clear the lists
ignoredEntities.clear();
ignoredPositions.clear();

//check Y axis motion
if(this.onGround = initY!=motionY)
gravityMotionY = 0;
//Play impact sound
net.minecraft.util.SoundEvent sound = handler.getSpecialSound(pl.pabilo8.immersiveintelligence.api.ammo.enums.HitEffect.RICOCHET);
if(sound!=null)
playSound(sound, 0.5f, 1f);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,14 @@ public RayTraceResult stepTrace(World world, Vec3d posStart, Vec3d posEnd, Predi
boolean skipPos = false;
//Step
Vector3d pDiff = new Vector3d(posEnd.x-posStart.x, posEnd.y-posStart.y, posEnd.z-posStart.z);
pDiff.normalize();
pDiff.scale(precision);
double totalDist = Math.sqrt(pDiff.x*pDiff.x + pDiff.y*pDiff.y + pDiff.z*pDiff.z);
if(totalDist > 0)
pDiff.normalize();

//Expand the bounding box
AxisAlignedBB aabb;
int totalSteps = (int)Math.max(1, posStart.distanceTo(posEnd)/precision);
int totalSteps = (int)Math.max(1, totalDist/precision);
pDiff.scale(totalDist / totalSteps);

//Cache entities
List<Entity> allEntities = allowEntities?listAllEntities(world, posStart, posEnd): null;
Expand Down Expand Up @@ -180,10 +182,9 @@ else if(entity.getEntityBoundingBox().intersects(aabb))
continue;

//Perform a precise raytrace on the block
RayTraceResult trace = state.collisionRayTrace(world, pos,
new Vec3d(aabb.minX, aabb.minY, aabb.minZ),
new Vec3d(aabb.maxX, aabb.maxY, aabb.maxZ)
);
Vec3d stepStart = new Vec3d(pX - pDiff.x, pY - pDiff.y, pZ - pDiff.z);
Vec3d stepEnd = new Vec3d(pX, pY, pZ);
RayTraceResult trace = state.collisionRayTrace(world, pos, stepStart, stepEnd);
//Avoid scanning the same position twice
if(trace!=null&&trace.typeOfHit!=Type.MISS)
skipPos = true;
Expand Down
Loading