Skip to content

Commit

Permalink
Refactor 20250113 - Class Refactorization
Browse files Browse the repository at this point in the history
Another refactorization commit to properly structure the classes of projectiles.

Also, parabolic trajectory is currently being experimented on for potential future implementation of such turrets like mortars.

[CHANGELOG]
🟢 Added the Explosive Projectile interface. This interface will handle all explosive projectiles similar to how Explosive Projectile Entity class is. This interface will soon replace the said superclass in Cannonball entity class.
🟢 Added Kinetic Projectile base class for all kinetic-base projectiles such as the Ballista Arrow and even (WIP) Cannonball.
🟢 Added several new properties for Turret Projectile, allowing its subclasses to let their damage be affected by their speed.
🟢 Added new damage and pierce level static constants to the Ballista Turret entity class.
🟢 Added new damage static constants to the Cannon Turret entity class.
🟢 Implemented the two new additional abstract methods from Turret Entity base class to all its subclasses.
🟢 Added a default (base) level to Turret Entity base class's level property.
🟢 Added a new method that returns the current barrel the turret is using.
🟢 Added safety clamps to some data to prevent any error from happening.
🟢 Added an overridable method "getMaxLevel" which dictates the maximum level a turret can be.
🟢 Refactored the Turret Projectile Velocity class to allow recalculation of velocity when some attributes are changed.
🟢 Began experimentation for the implementation of parabolic projectiles. If successful, implementations of turrets like mortars may proceed.
🟡 Updated Ballista Arrow and Cannonball entity's superclass to Kinetic Projectile, allowing it to remove some redundant codes.
🟡 Moved some repeating logics and codes from the projectile entities towards the Turret Projectile entity base class.
🟡 Updated Cannon Turret's max attack and follow range from 16 to 24.
🟡 Updated the public method "getMaxAttackRange" which previously limits the range of the turrets to 16.625. It now uses the subclass's "FOLLOW_RANGE" attribute as its maximum attack range + its eye height.
🟡 Updated the Turret Projectile Velocity class to normalize its values and allow developers to easily comprehend some values such as power and upward velocity multiplier.
  • Loading branch information
Virus5600 committed Jan 12, 2025
1 parent 4e9314e commit 4c269d9
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 135 deletions.
Original file line number Diff line number Diff line change
@@ -1,45 +1,39 @@
package com.virus5600.defensive_measures.entity.projectiles;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.data.DataTracker.Builder;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.world.World;

import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager.ControllerRegistrar;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.animation.AnimationState;
import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;

import com.virus5600.defensive_measures.entity.ModEntities;

import java.util.Map;

public class BallistaArrowEntity extends TurretProjectileEntity implements GeoEntity {
public class BallistaArrowEntity extends KineticProjectileEntity {
private static final Map<String, RawAnimation> ANIMATIONS;

private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);

////////////////////
/// CONSTRUCTORS ///
////////////////////
// ////////////// //
// CONSTRUCTORS //
// ////////////// //
public BallistaArrowEntity(
EntityType<? extends TurretProjectileEntity> entityType,
World world
) {
super(entityType, world);

this.setDamage(4);
this.setPierceLevel(this.getMaxPierceLevel());
}

public BallistaArrowEntity(World world, LivingEntity owner) {
this(ModEntities.BALLISTA_ARROW, world);

this.setPierceLevel(this.getMaxPierceLevel());
this.setOwner(owner);
}

Expand All @@ -64,55 +58,6 @@ protected void initDataTracker(Builder builder) {
}

@Override
protected void onEntityHit(EntityHitResult entityHitResult) {
super.onEntityHit(entityHitResult);

// Reduces the speed of the arrow when it hits an entity
Entity entity = entityHitResult.getEntity();
// For reference, max armor points is 30
int armor = entity instanceof LivingEntity livingEntity ? livingEntity.getArmor() : 0;
boolean hasHeavyArmor = armor > 15,
hasLightArmor = armor <= 15 && armor > 0;

if (entity instanceof LivingEntity livingEntity) {
double targetH = livingEntity.getHeight(),
arrowH = this.getHeight(),
variance = arrowH * 0.125; // 12.5% of the arrow's height

double arrowMin = arrowH - variance,
arrowMax = arrowH + variance,
reducedVelocity = 0.125;

// Reduce velocity and pierce level based on the side of the entity hit
if (this.getPierceLevel() > 0) {
// If the arrow is smaller than the target or has heavy armor, reduce the pierce level by 2 and velocity by 50%
if (targetH > arrowMax || hasHeavyArmor) {
this.setPierceLevel((byte) (this.getPierceLevel() - 2));
reducedVelocity = 0.5;
}
// If the arrow is almost the same size as the target or has a light armor, reduce the pierce level by 1 and velocity by 25%
else if ((targetH < arrowMax && arrowMin < targetH) || hasLightArmor) {
this.setPierceLevel((byte) (this.getPierceLevel() - 1));
reducedVelocity = 0.25;
}
// Otherwise, just reduce the velocity by 12.5% without reducing the pierce level
}

// Apply reduced velocity
this.addVelocity(
this.getVelocity()
.multiply(reducedVelocity)
.negate()
);
}

this.addVelocity(
this.getVelocity()
.negate()
.multiply(0.1)
);
}

public byte getMaxPierceLevel() {
return 5;
}
Expand Down Expand Up @@ -146,11 +91,6 @@ public void registerControllers(final ControllerRegistrar controllers) {
);
}

@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.geoCache;
}

///////////////////////
// STATIC INITIALIZE //
///////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,16 @@
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.DataTracker.Builder;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;

import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.util.GeckoLibUtil;

import com.virus5600.defensive_measures.entity.ModEntities;

public class BulletEntity extends TurretProjectileEntity implements GeoEntity {
protected static final TrackedData<Byte> PIERCE_LEVEL = DataTracker.registerData(BulletEntity.class, TrackedDataHandlerRegistry.BYTE);

public class BulletEntity extends TurretProjectileEntity {
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);

////////////////////
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.virus5600.defensive_measures.entity.projectiles;

import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.data.DataTracker.Builder;
import net.minecraft.entity.EntityType;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.world.World;

public abstract class KineticProjectileEntity extends TurretProjectileEntity {

// ///////////// //
// CONSTRUCTORS //
// ///////////// //
public KineticProjectileEntity(EntityType<? extends TurretProjectileEntity> entityType, World world) {
super(entityType, world);
this.setDamage(4);
this.setPierceLevel(this.getMaxPierceLevel());
this.setSpeedAffectsDamage(false);
}

// /////////////// //
// PROCESS METHODS //
// /////////////// //
// PROTECTED
@Override
protected void initDataTracker(Builder builder) {
super.initDataTracker(builder);
}

/**
* {@inheritDoc}
* <hr>
* <h2>{@link KineticProjectileEntity}</h2>
* <br><br>
* The method in {@code KineticProjectileEntity} reduces the speed of the arrow
* along with the pierce level based on the amount of armor points the entity has.
* <br><br>
* In this implementation, the projectile will have the following behavior
* (assuming the projectile is affected by armor points):
* <table>
* <tr>
* <th>Armor Points</th>
* <th>Pierce Level Reduction</th>
* <th>Velocity Reduction</th>
* <th>Base Damage Reduction</th>
* </tr>
* <tr>
* <td>Heavy Armor</td>
* <td>2</td>
* <td>50%</td>
* <td>50%</td>
* </tr>
* <tr>
* <td>Light Armor</td>
* <td>1</td>
* <td>25%</td>
* <td>20%</td>
* </tr>
* <tr>
* <td>No Armor</td>
* <td>N/A</td>
* <td>12.5%</td>
* <td>5%</td>
* </tr>
* </table>
*
* @param entityHitResult {@inheritDoc EntityHitResult}
*/
@Override
protected void onEntityHit(EntityHitResult entityHitResult) {
super.onEntityHit(entityHitResult);

if (this.armorAffectsPiercing()) {
// Reduces the speed of the arrow when it hits an entity
Entity entity = entityHitResult.getEntity();
// For reference, max armor points is 30
int armor = entity instanceof LivingEntity livingEntity ? livingEntity.getArmor() : 0;
double dmgReduction = 0.05;
boolean hasHeavyArmor = armor > 15,
hasLightArmor = armor <= 15 && armor > 0;

if (entity instanceof LivingEntity livingEntity) {
double targetH = livingEntity.getHeight(),
arrowH = this.getHeight(),
variance = arrowH * 0.125; // 12.5% of the arrow's height

double arrowMin = arrowH - variance,
arrowMax = arrowH + variance,
reducedVelocity = 0.125;

// Reduce velocity and pierce level based on the side of the entity hit
if (this.getPierceLevel() > 0) {
// If the arrow is smaller than the target or has heavy armor, reduce the pierce level by 2, velocity by 50%, and base damage by 50%
if (targetH > arrowMax || hasHeavyArmor) {
this.setPierceLevel((byte) (this.getPierceLevel() - 2));
reducedVelocity = 0.5;
dmgReduction = 0.5;
}
// If the arrow is almost the same size as the target or has a light armor, reduce the pierce level by 1, velocity by 25%, and base damage by 20%
else if ((targetH < arrowMax && arrowMin < targetH) || hasLightArmor) {
this.setPierceLevel((byte) (this.getPierceLevel() - 1));
reducedVelocity = 0.25;
dmgReduction = 0.2;
}
// Otherwise, just reduce the velocity by 12.5% and base damage by 5% without reducing the pierce level
}

// Apply reduced velocity
this.addVelocity(
this.getVelocity()
.multiply(reducedVelocity)
.negate()
);

// Apply reduced damage
this.setDamage(this.getDamage() * (1 - dmgReduction));
}

this.addVelocity(
this.getVelocity()
.negate()
.multiply(0.1)
);
}
}

// /////////////////////////////// //
// GETTERS, SETTERS, AND QUESTIONS //
// /////////////////////////////// //

// PUBLIC
/**
* An overridable method that determines whether an armor point affects the
* piercing of the projectile.
* <br><br>
* By default, this method returns {@code true}.
*
* @return {@code true} if the armor point affects the piercing of the projectile,
* {@code false} otherwise.
*/
public boolean armorAffectsPiercing() {
return true;
}
}
Loading

0 comments on commit 4c269d9

Please sign in to comment.