Skip to content

Commit

Permalink
Implementation 20241228 - Full Cannon Turret
Browse files Browse the repository at this point in the history
Implemented the entirety of Cannon Turret, along with its projectile, sounds, animations, and effects.

[CHANGELOG]
🟢 Added the Cannonball Model class.
🟢 Added the Cannonball Renderer class.
🟢 Added the Cannonball Entity class.
🟢 Added entity registry methods in the Registry Utility class.
🟢 Added Cannonball resources (model and texture).
🟢 Added bullet impacting metal sound files.
🟢 Added bullet impacting wood sound files.
🟢 Added metal repair sound files.
🟡 Updated the projectile of Cannon Turret entity.
🟡 Updated sound ID of "Turret Metal Repair" from "turret.repair.iron" to "turret.repair.metal".
🟡 Fixed typo for the Arrowhead death message.
🟡 Updated item tooltip regarding Cannon Turret item's range from 20 to 16, matching the real range of the turret entity.
🟡 Updated sounds.json to properly reflect changes done to the sound lists.
🟡 Renamed the bullet impacting dirt sounds to start from 1 instead of 3.
🟡 Updated cannonball texture to have transparent background instead of black.
🔴 Removed the TODO in the Cannon Turret Model class.
  • Loading branch information
Virus5600 committed Dec 28, 2024
1 parent fd0316c commit 7d09ba9
Show file tree
Hide file tree
Showing 24 changed files with 330 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@

@Environment(EnvType.CLIENT)
public class CannonTurretModel extends GeoModel<CannonTurretEntity> {
///////////////////////
// INTERFACE METHODS //
///////////////////////
/////////////////////////
/// INTERFACE METHODS ///
/////////////////////////

@Override
public Identifier getModelResource(CannonTurretEntity cannonTurretEntity, @Nullable GeoRenderer<CannonTurretEntity> geoRenderer) {
Expand Down Expand Up @@ -62,9 +62,4 @@ public void setCustomAnimations(CannonTurretEntity animatable, long instanceId,
base.get().setRotZ(0);
}
}

/*
* TODO: Consult Discord Servers for potential issue why this is thrown specifically when placed on Lightning Rods:
* [09:11:45] [Netty Server IO #1/ERROR] (Minecraft) Error sending packet clientbound/minecraft:set_entity_data
*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.virus5600.defensive_measures.model.projectiles;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.util.Identifier;

import org.jetbrains.annotations.Nullable;

import software.bernie.geckolib.model.GeoModel;
import software.bernie.geckolib.renderer.GeoRenderer;

import com.virus5600.defensive_measures.DefensiveMeasures;
import com.virus5600.defensive_measures.entity.projectiles.CannonballEntity;

@Environment(EnvType.CLIENT)
public class CannonballModel extends GeoModel<CannonballEntity> {
/////////////////////////
/// INTERFACE METHODS ///
/////////////////////////

@Override
public Identifier getModelResource(CannonballEntity cannonballEntity, @Nullable GeoRenderer<CannonballEntity> geoRenderer) {
return Identifier.of(DefensiveMeasures.MOD_ID, "geo/projectiles/cannonball.geo.json");
}

@Override
public Identifier getTextureResource(CannonballEntity cannonballEntity, @Nullable GeoRenderer<CannonballEntity> geoRenderer) {
return Identifier.of(DefensiveMeasures.MOD_ID, "textures/entity/cannon_turret/cannonball.png");
}

@Override
public Identifier getAnimationResource(CannonballEntity animatable) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.virus5600.defensive_measures.DefensiveMeasures;
import com.virus5600.defensive_measures.entity.ModEntities;
import com.virus5600.defensive_measures.renderer.entity.CannonTurretRenderer;
import com.virus5600.defensive_measures.renderer.projectiles.CannonballRenderer;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;

public class ModEntityRenderer {
Expand All @@ -15,5 +16,12 @@ public static void registerEntityRenderers() {

// v1.0.0
EntityRendererRegistry.register(ModEntities.CANNON_TURRET, CannonTurretRenderer::new);

/////////////////
// PROJECTILES //
/////////////////

// v1.0.0
EntityRendererRegistry.register(ModEntities.CANNONBALL, CannonballRenderer::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.virus5600.defensive_measures.renderer.projectiles;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;

import net.minecraft.client.render.entity.EntityRendererFactory.Context;
import software.bernie.geckolib.renderer.GeoEntityRenderer;

import com.virus5600.defensive_measures.entity.projectiles.CannonballEntity;
import com.virus5600.defensive_measures.model.projectiles.CannonballModel;

@Environment(EnvType.CLIENT)
public class CannonballRenderer extends GeoEntityRenderer<CannonballEntity> {
public CannonballRenderer(Context ctx) {
super(ctx, new CannonballModel());
}

@Override
protected float getDeathMaxRotation(CannonballEntity animatable, float partialTick) {
return 0.0f;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,25 @@

import com.virus5600.defensive_measures.DefensiveMeasures;

import com.virus5600.defensive_measures.entity.turrets.CannonTurretEntity;
import com.virus5600.defensive_measures.entity.projectiles.*;
import com.virus5600.defensive_measures.entity.turrets.*;
import com.virus5600.defensive_measures.util.RegistryUtil;

import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;

import net.minecraft.entity.EntityType;
import net.minecraft.entity.EntityType.Builder;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;

public class ModEntities {
// TURRETS //
// v1.0.0
public static final EntityType<CannonTurretEntity> CANNON_TURRET = Registry.register(
Registries.ENTITY_TYPE,
Identifier.of(DefensiveMeasures.MOD_ID, "cannon_turret"),
public static final EntityType<CannonTurretEntity> CANNON_TURRET = RegistryUtil.registerEntity(
"cannon_turret",
Builder
.create(CannonTurretEntity::new, SpawnGroup.MISC)
.dimensions(1F, 1F)
.eyeHeight(0.51F)
.build(RegistryKey.of(RegistryKeys.ENTITY_TYPE, Identifier.of(DefensiveMeasures.MOD_ID, "cannon_turret")))
);

// public static final EntityType<BallistaTurretEntity> BALLISTA = Registry.register(
Expand All @@ -48,14 +43,15 @@ public class ModEntities {

// PROJECTILES //
// v1.0.0
// public static final EntityType<CannonballEntity> CANNONBALL = Registry.register(
// Registries.ENTITY_TYPE,
// Identifier.of(DefensiveMeasures.MOD_ID, "cannonball"),
// Builder
// .create(CannonballEntity::new, SpawnGroup.MISC)
// .dimensions(EntityDimensions.fixed(0.125f, 0.125f))
// .build()
// );
public static final EntityType<CannonballEntity> CANNONBALL = RegistryUtil.registerEntity(
"cannonball",
Builder
.<CannonballEntity>create(CannonballEntity::new, SpawnGroup.MISC)
.dropsNothing()
.dimensions(0.125f, 0.125f)
.maxTrackingRange(4)
.trackingTickInterval(10)
);

// public static final EntityType<BallistaArrowEntity> BALLISTA_ARROW = Registry.register(
// Registries.ENTITY_TYPE,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package com.virus5600.defensive_measures.entity.projectiles;

import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.projectile.ExplosiveProjectileEntity;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.explosion.ExplosionBehavior;

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

import com.virus5600.defensive_measures.entity.ModEntities;

public class CannonballEntity extends ExplosiveProjectileEntity implements GeoEntity {
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private LivingEntity shooter;

////////////////////
/// CONSTRUCTORS ///
////////////////////
public CannonballEntity(EntityType<? extends ExplosiveProjectileEntity> entityType, World world) {
super(entityType, world);
this.setFireTicks(0);
this.setOnFire(false);
this.setNoGravity(false);
}

public CannonballEntity(World world, LivingEntity owner) {
this(ModEntities.CANNONBALL, world);
this.setOwner(owner);
}

public CannonballEntity(LivingEntity owner, double directionX, double directionY, double directionZ, World world) {
this(world, owner);
this.setVelocity(directionX, directionY, directionZ);
}

///////////////
/// METHODS ///
///////////////
// PROTECTED
@Override
protected void onBlockHit(BlockHitResult blockHitResult) {
super.onBlockHit(blockHitResult);

if (!this.getWorld().isClient) {
this.doDamage();
}
}

@Override
protected void onEntityHit(EntityHitResult entityHitResult) {
if (!this.getWorld().isClient) {
this.doDamage();
}
}

@Override
protected boolean isBurning() {
return false;
}

@Override
protected ParticleEffect getParticleType() {
return ParticleTypes.CLOUD;
}

@Override
protected void onCollision(HitResult hitResult) {
super.onCollision(hitResult);
if (!this.getWorld().isClient) {
this.doDamage();
}
}

protected RegistryEntry.Reference<SoundEvent> getHitSound() {
return SoundEvents.ENTITY_GENERIC_EXPLODE;
}

// PUBLIC
private double radius = 0;
public void doDamage() {
// Create damage source
DamageSource dmgSrc = this.getDamageSources().explosion(
this,
this.getOwner() == null ? this : this.getOwner()
);

// Create explosion
this.getWorld()
.createExplosion(
this,
dmgSrc,
new ExplosionBehavior(),
this.getX(),
this.getBodyY(0.0625),
this.getZ(),
1.25F,
false,
World.ExplosionSourceType.NONE
);

// Damage entities within a 4 block radius
double maxEffectiveDmgRad = 2;
double maxDmgRad = 4;
double dmgReduction = 3.75;
double baseDmg = 10;

for (radius = 0; radius < maxDmgRad; radius += 0.25) {
if (radius < maxEffectiveDmgRad) radius += 0.75;

double x1 = MathHelper.floor(this.getX() - radius - 1.0D);
double x2 = MathHelper.floor(this.getX() + radius + 1.0D);
double y1 = MathHelper.floor(this.getY() - radius - 1.0D);
double y2 = MathHelper.floor(this.getY() + radius + 1.0D);
double z1 = MathHelper.floor(this.getZ() - radius - 1.0D);
double z3 = MathHelper.floor(this.getZ() + radius + 1.0D);

this.getWorld()
.getOtherEntities(
this,
new Box(x1, y1, z1, x2, y2, z3)
)
.forEach(entity -> {
float dmg = (float) baseDmg;

if (radius > maxEffectiveDmgRad) {
dmg -= (float) (dmgReduction * (radius - maxEffectiveDmgRad));
}

if (entity instanceof LivingEntity) {
entity.damage(
(ServerWorld) this.getWorld(),
dmgSrc,
dmg
);
}
});
}

this.discard();
}

@Override
public void tick() {
super.tick();

double acceleration = 0.08;
if (!this.hasNoGravity()) {
this.setVelocity(
this.getVelocity()
.add(
0,
-acceleration / 4.0,
0
)
);
}
}

/////////////////////////////////
/// INTERFACE IMPLEMENTATIONS ///
/////////////////////////////////

// GeoEntity //
@Override
public void registerControllers(final ControllerRegistrar controllers) {
}

@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.geoCache;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.virus5600.defensive_measures.entity.TurretMaterial;
import com.virus5600.defensive_measures.entity.ai.goal.ProjectileAttackGoal;
import com.virus5600.defensive_measures.entity.ai.goal.TargetOtherTeamGoal;
import com.virus5600.defensive_measures.entity.projectiles.CannonballEntity;
import com.virus5600.defensive_measures.item.ModItems;
import com.virus5600.defensive_measures.particle.ModParticles;
import com.virus5600.defensive_measures.sound.ModSoundEvents;
Expand Down Expand Up @@ -120,9 +121,7 @@ public void shootAt(LivingEntity target, float pullProgress) {
double vz = (target.getZ() - this.getZ()) * 1.0625;
double variance = Math.sqrt(vx * vx + vz * vz);
float divergence = this.getWorld().getDifficulty().getId() * 2;
// ProjectileEntity projectile = new CannonballEntity(ModEntities.CANNONBALL, this, vx, vy, vz, this.getWorld());
ProjectileEntity projectile = new ArrowEntity(EntityType.ARROW, this.getWorld());
projectile.setOwner(this);
ProjectileEntity projectile = new CannonballEntity(this, vx, vy, vz, this.getWorld());

projectile.setPos(this.getX(), this.getY() + 0.5, this.getZ());
projectile.setVelocity(vx, vy + variance * 0.1f, vz, 1.5f, divergence);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class ModSoundEvents {
// V1.0
public final static SoundEvent TURRET_REMOVED_METAL = ModSoundEvents.registerSoundEvent("turret.remove.metal");
public final static SoundEvent TURRET_REMOVED_WOOD = ModSoundEvents.registerSoundEvent("turret.remove.wood");
public final static SoundEvent TURRET_REPAIR_METAL = ModSoundEvents.registerSoundEvent("turret.repair.iron");
public final static SoundEvent TURRET_REPAIR_METAL = ModSoundEvents.registerSoundEvent("turret.repair.metal");
public final static SoundEvent TURRET_REPAIR_WOOD = ModSoundEvents.registerSoundEvent("turret.repair.wood");
public final static SoundEvent TURRET_REPAIR_BOW = ModSoundEvents.registerSoundEvent("turret.repair.bow");

Expand Down
Loading

0 comments on commit 7d09ba9

Please sign in to comment.