diff --git a/build.gradle b/build.gradle index 0efd2e8..500a2e7 100644 --- a/build.gradle +++ b/build.gradle @@ -41,9 +41,9 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "maven.modrinth:valkyrien-skies:1.20.1-fabric-2.3.0-beta.5" modImplementation "maven.modrinth:eureka:1.20.1-fabric-1.5.1-beta.3" - modImplementation "maven.modrinth:valkyrien-sails:1.20.1-0.1.6-fabric" - modImplementation "maven.modrinth:vlib:1.20.1-0.0.11-alpha+fabric" + modImplementation "maven.modrinth:valkyrien-sails:1.20.1-0.1.7-fabric" + modImplementation "maven.modrinth:musket-mod:1.5.4" modImplementation "maven.modrinth:architectury-api:9.2.14+fabric" diff --git a/gradle.properties b/gradle.properties index 33ab15a..f3300e9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ fabric_kotlin_version=1.10.19+kotlin.1.9.23 # Mod Properties mod_version=1.8.2 maven_group=ace.actually.pirates -archives_base_name=ValkyrienPirates +archives_base_name=DinBrosValkyrienPirates # Dependencies fabric_version=0.92.1+1.20.1 \ No newline at end of file diff --git a/src/main/java/ace/actually/pirates/ClientPirates.java b/src/main/java/ace/actually/pirates/ClientPirates.java index b921823..4bfdc02 100644 --- a/src/main/java/ace/actually/pirates/ClientPirates.java +++ b/src/main/java/ace/actually/pirates/ClientPirates.java @@ -33,6 +33,37 @@ public void onInitializeClient() { //EntityModelLayerRegistry.registerModelLayer(SKELETON_PIRATE, SkeletonPirateModel::getTexturedModelData); - } + net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking.registerGlobalReceiver( + Pirates.CANNON_SMOKE_PACKET_ID, + (client, handler, buf, sender) -> { + double x = buf.readDouble(); + double y = buf.readDouble(); + double z = buf.readDouble(); + int dirId = buf.readInt(); + + client.execute(() -> { + var world = client.world; + if (world == null) return; + + var direction = net.minecraft.util.math.Direction.byId(dirId); + for (int i = 0; i < 40; ++i) { + world.addParticle(net.minecraft.particle.ParticleTypes.FLAME, + x + direction.getOffsetX() * (0.5 + world.random.nextDouble() * 1.5), + y + direction.getOffsetY() + (world.random.nextDouble() * 1.0) - 0.5, + z + direction.getOffsetZ() * (0.5 + world.random.nextDouble() * 1.5), + (world.random.nextDouble() * 0.3) - 0.15, + (world.random.nextDouble() * 0.1) - 0.05, + (world.random.nextDouble() * 0.3) - 0.15); + + world.addParticle(net.minecraft.particle.ParticleTypes.CLOUD, + x + direction.getOffsetX() * (3.5 + world.random.nextDouble() * 2) + (4 * world.random.nextDouble()) - 2, + y + (world.random.nextDouble() * 3.0) - 1.0, + z + direction.getOffsetZ() * (3.5 + world.random.nextDouble() * 2) + (4 * world.random.nextDouble()) - 2, + 0.0, 0.0, 0.0); + } + }); + } + ); + } } diff --git a/src/main/java/ace/actually/pirates/Pirates.java b/src/main/java/ace/actually/pirates/Pirates.java index 7e0366f..cef8c51 100644 --- a/src/main/java/ace/actually/pirates/Pirates.java +++ b/src/main/java/ace/actually/pirates/Pirates.java @@ -56,7 +56,7 @@ public class Pirates implements ModInitializer { // That way, it's clear which mod wrote info, warnings, and errors. public static final String MOD_ID = "pirates"; public static final Logger LOGGER = LoggerFactory.getLogger("pirates"); - + public static final Identifier CANNON_SMOKE_PACKET_ID = new Identifier(MOD_ID, "cannon_smoke"); public static final GameRules.Key PIRATES_IS_LIVE_WORLD = GameRuleRegistry.register("piratesIsLive", GameRules.Category.MISC, GameRuleFactory.createBooleanRule(true)); @@ -192,6 +192,18 @@ private void registerEntityThings() public static final StableBlock STABLE_BLOCK = new StableBlock(AbstractBlock.Settings.create()); public static final ShipIdBlock SHIP_ID_BLOCK = new ShipIdBlock(AbstractBlock.Settings.create()); public static final Block HEAVY_BLOCK = new Block(AbstractBlock.Settings.copy(Blocks.OBSIDIAN)); + public static final DamagedHullBlock DAMAGED_HULL_BLOCK = new DamagedHullBlock( + AbstractBlock.Settings.copy(Blocks.NETHERITE_BLOCK) + .strength(0.5f, 1200.0f) + .noBlockBreakParticles() + .noCollision() + .dropsNothing() + ); + public static final BlockEntityType DAMAGED_HULL_BLOCK_ENTITY = Registry.register( + Registries.BLOCK_ENTITY_TYPE, + new Identifier("pirates", "damaged_hull_block_entity"), + FabricBlockEntityTypeBuilder.create(DamagedHullBlockEntity::new, DAMAGED_HULL_BLOCK).build() + ); private void registerBlocks() { Registry.register(Registries.BLOCK,new Identifier("pirates","cannon_priming_block"),CANNON_PRIMING_BLOCK); @@ -202,6 +214,8 @@ private void registerBlocks() Registry.register(Registries.BLOCK,new Identifier("pirates","ship_id_block"),SHIP_ID_BLOCK); Registry.register(Registries.BLOCK,new Identifier("pirates","heavy_block"),HEAVY_BLOCK); + // my custom blocks + Registry.register(Registries.BLOCK, new Identifier("pirates", "damaged_hull_block"), DAMAGED_HULL_BLOCK); } @@ -232,6 +246,9 @@ private void registerItems() Registry.register(Registries.ITEM,new Identifier("pirates","crew_spawner_block"),new BlockItem(CREW_SPAWNER_BLOCK,new Item.Settings())); Registry.register(Registries.ITEM,new Identifier("pirates","ship_id_block"),new BlockItem(SHIP_ID_BLOCK,new Item.Settings())); + // my custom blocks + Registry.register(Registries.ITEM, new Identifier("pirates", "damaged_hull_block"), + new BlockItem(DAMAGED_HULL_BLOCK, new Item.Settings())); } diff --git a/src/main/java/ace/actually/pirates/blocks/DamagedHullBlock.java b/src/main/java/ace/actually/pirates/blocks/DamagedHullBlock.java new file mode 100644 index 0000000..68b2304 --- /dev/null +++ b/src/main/java/ace/actually/pirates/blocks/DamagedHullBlock.java @@ -0,0 +1,34 @@ +package ace.actually.pirates.blocks; +import ace.actually.pirates.Pirates; +import ace.actually.pirates.blocks.entity.DamagedHullBlockEntity; +import net.minecraft.block.BlockRenderType; +import net.minecraft.block.BlockState; +import net.minecraft.block.BlockWithEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class DamagedHullBlock extends BlockWithEntity { + + public DamagedHullBlock(Settings settings) { + super(settings); + } + + @Override + public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new DamagedHullBlockEntity(pos, state); + } + + @Override + public BlockRenderType getRenderType(BlockState state) { + return BlockRenderType.INVISIBLE; + } + + @Override + public BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType type) { + return checkType(type, Pirates.DAMAGED_HULL_BLOCK_ENTITY, DamagedHullBlockEntity::tick); + } +} + diff --git a/src/main/java/ace/actually/pirates/blocks/DispenserCannonBlock.java b/src/main/java/ace/actually/pirates/blocks/DispenserCannonBlock.java index 40df7f3..0e037bd 100644 --- a/src/main/java/ace/actually/pirates/blocks/DispenserCannonBlock.java +++ b/src/main/java/ace/actually/pirates/blocks/DispenserCannonBlock.java @@ -13,10 +13,7 @@ import net.minecraft.state.property.Properties; import net.minecraft.text.Text; import net.minecraft.util.Util; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Position; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.*; import net.minecraft.world.BlockView; import net.minecraft.world.World; import net.minecraft.world.WorldAccess; @@ -86,5 +83,4 @@ public void neighborUpdate(BlockState state, World world, BlockPos pos, Block so public ItemStack getPickStack(BlockView world, BlockPos pos, BlockState state) { return new ItemStack(Items.DISPENSER); } - } diff --git a/src/main/java/ace/actually/pirates/blocks/entity/CannonPrimingBlockEntity.java b/src/main/java/ace/actually/pirates/blocks/entity/CannonPrimingBlockEntity.java index faee369..6ebe71b 100644 --- a/src/main/java/ace/actually/pirates/blocks/entity/CannonPrimingBlockEntity.java +++ b/src/main/java/ace/actually/pirates/blocks/entity/CannonPrimingBlockEntity.java @@ -80,9 +80,10 @@ private static boolean checkShouldFire(World world, BlockPos pos, BlockState sta return false; } + // original is (2;32) RaycastContext context = new RaycastContext( - VSGameUtilsKt.toWorldCoordinates(world, Vec3d.ofCenter(pos.add(raycastStart.multiply(2)))), - VSGameUtilsKt.toWorldCoordinates(world, Vec3d.ofCenter(pos.add(raycastStart.multiply(32)))), + VSGameUtilsKt.toWorldCoordinates(world, Vec3d.ofCenter(pos.add(raycastStart.multiply(30)))), + VSGameUtilsKt.toWorldCoordinates(world, Vec3d.ofCenter(pos.add(raycastStart.multiply(80)))), RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, null); diff --git a/src/main/java/ace/actually/pirates/blocks/entity/CrewSpawnerBlockEntity.java b/src/main/java/ace/actually/pirates/blocks/entity/CrewSpawnerBlockEntity.java index 2556f99..f874e1c 100644 --- a/src/main/java/ace/actually/pirates/blocks/entity/CrewSpawnerBlockEntity.java +++ b/src/main/java/ace/actually/pirates/blocks/entity/CrewSpawnerBlockEntity.java @@ -1,6 +1,7 @@ package ace.actually.pirates.blocks.entity; import ace.actually.pirates.Pirates; +import ace.actually.pirates.blocks.CannonPrimingBlock; import ace.actually.pirates.entities.pirate_abstract.AbstractPirateEntity; import ace.actually.pirates.entities.pirate_default.PirateEntity; import ace.actually.pirates.entities.pirate_skeleton.SkeletonPirateEntity; @@ -8,6 +9,7 @@ import ace.actually.pirates.entities.CrewSpawnType; import ace.actually.pirates.entities.CrewTypes; import ace.actually.pirates.util.ConfigUtils; +import ewewukek.musketmod.Items; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.enchantment.Enchantments; @@ -15,8 +17,9 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.passive.VillagerEntity; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; +//import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.server.network.EntityTrackerEntry; import net.minecraft.server.world.ServerWorld; @@ -31,6 +34,7 @@ import net.minecraft.world.EntityList; import java.util.Optional; +import java.util.Random; public class CrewSpawnerBlockEntity extends BlockEntity { @@ -112,19 +116,20 @@ private static BlockPos checkForBlocksToCrew (World world, BlockPos origin) { private static Entity getEntityFromState(World world, BlockEntity be) { Entity crew = null; + Item randomGun = AbstractPirateEntity.guns[new Random().nextInt(AbstractPirateEntity.guns.length)]; switch (be.getCachedState().get(CrewTypes.CREW_SPAWN_TYPE)) { case PIRATE -> { crew = new PirateEntity(world, checkForBlocksToCrew(world, be.getPos())); - crew.equipStack(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + crew.equipStack(EquipmentSlot.MAINHAND, new ItemStack(randomGun)); } case VILLAGER -> crew = new VillagerEntity(EntityType.VILLAGER, world, VillagerType.forBiome(world.getBiome(be.getPos()))); case SKELETON_PIRATE -> { BlockPos blockToCrew = checkForBlocksToCrew(world, be.getPos()); crew = new PirateEntity(world, blockToCrew); - ItemStack itemStack = new ItemStack(Items.BOW); + ItemStack itemStack = new ItemStack(randomGun); if (world.getBlockState(blockToCrew).isOf(Pirates.MOTION_INVOKING_BLOCK)) { itemStack.addEnchantment(Enchantments.POWER, 2); } diff --git a/src/main/java/ace/actually/pirates/blocks/entity/DamagedHullBlockEntity.java b/src/main/java/ace/actually/pirates/blocks/entity/DamagedHullBlockEntity.java new file mode 100644 index 0000000..0dd48ca --- /dev/null +++ b/src/main/java/ace/actually/pirates/blocks/entity/DamagedHullBlockEntity.java @@ -0,0 +1,32 @@ +package ace.actually.pirates.blocks.entity; + +import ace.actually.pirates.Pirates; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.valkyrienskies.core.api.ships.Ship; +import org.valkyrienskies.mod.common.VSGameUtilsKt; + +public class DamagedHullBlockEntity extends BlockEntity { + + private double currentMass = 1000.0; // Starts light → increases over time + + public DamagedHullBlockEntity(BlockPos pos, BlockState state) { + super(Pirates.DAMAGED_HULL_BLOCK_ENTITY, pos, state); + } + + public static void tick(World world, BlockPos pos, BlockState state, DamagedHullBlockEntity blockEntity) { + if (world.isClient) return; + + // Simulate water flooding + blockEntity.currentMass += 100.0; // Increase mass per tick (tune this!) + if (blockEntity.currentMass > 10000.0) { + blockEntity.currentMass = 10000.0; // Cap at max mass (e.g. Netherite mass) + } + } + + public double getCurrentMass() { + return currentMass; + } +} diff --git a/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateEntity.java b/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateEntity.java index 58d6837..c274503 100644 --- a/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateEntity.java +++ b/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateEntity.java @@ -3,10 +3,15 @@ import ace.actually.pirates.Pirates; import ace.actually.pirates.entities.pirate_abstract.AbstractPirateEntity; import ace.actually.pirates.entities.pirate_abstract.PirateBowAttackGoal; +import ace.actually.pirates.entities.pirate_abstract.PirateGunAttackGoal; import ace.actually.pirates.entities.pirate_abstract.PirateWanderArroundFarGoal; +import ace.actually.pirates.entities.pirate_default.PirateEntity; import ace.actually.pirates.util.DisarmUtils; +import ewewukek.musketmod.GunItem; +import ewewukek.musketmod.MusketItem; import net.minecraft.entity.*; import net.minecraft.entity.ai.RangedAttackMob; +import net.minecraft.entity.ai.goal.ActiveTargetGoal; import net.minecraft.entity.ai.goal.LookAroundGoal; import net.minecraft.entity.ai.goal.LookAtEntityGoal; import net.minecraft.entity.ai.goal.RevengeGoal; @@ -18,11 +23,13 @@ import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.PillagerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.PersistentProjectileEntity; import net.minecraft.entity.projectile.ProjectileUtil; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; +import ewewukek.musketmod.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; @@ -90,9 +97,11 @@ public void genCustomName(World world) @Override protected void initGoals() { super.initGoals(); - this.goalSelector.add(3, new PirateBowAttackGoal<>(this, 1.0D, 20, 20.0F)); - this.targetSelector.add(1, new RevengeGoal(this)); - +// this.goalSelector.add(3, new PirateBowAttackGoal<>(this, 1.0D, 20, 20.0F)); + this.goalSelector.add(3, new PirateGunAttackGoal<>(this, 1.0D, 20, 20.0F)); + this.targetSelector.add(3, new ActiveTargetGoal(this, PirateEntity.class, true)); + this.targetSelector.add(3, new ActiveTargetGoal(this, PillagerEntity.class, true)); +// this.targetSelector.add(1, new RevengeGoal(this)); } @Override @@ -119,22 +128,24 @@ public static DefaultAttributeContainer.Builder attributes() { @Override protected void initEquipment(Random random, LocalDifficulty localDifficulty) { super.initEquipment(random, localDifficulty); - this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + Item rand = guns[random.nextInt(guns.length)]; + this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(rand)); + if (rand == Items.PISTOL) + this.equipStack(EquipmentSlot.OFFHAND, new ItemStack(Items.PISTOL)); } @Override public void attack(LivingEntity target, float pullProgress) { - ItemStack itemStack = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, Items.BOW)); - PersistentProjectileEntity persistentProjectileEntity = this.createArrowProjectile(itemStack, pullProgress); - double d = target.getX() - this.getX(); - double e = target.getBodyY(0.3333333333333333) - persistentProjectileEntity.getY(); - double f = target.getZ() - this.getZ(); - double g = Math.sqrt(d * d + f * f); - persistentProjectileEntity.setVelocity(d, e + g * 0.20000000298023224, f, 1.6F, (float) (14 - this.getEntityWorld().getDifficulty().getId() * 4)); - this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); - this.getEntityWorld().spawnEntity(persistentProjectileEntity); - +// ItemStack itemStack = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, Items.BLUNDERBUSS)); +// PersistentProjectileEntity persistentProjectileEntity = this.createArrowProjectile(itemStack, pullProgress); +// double d = target.getX() - this.getX(); +// double e = target.getBodyY(0.3333333333333333) - persistentProjectileEntity.getY(); +// double f = target.getZ() - this.getZ(); +// double g = Math.sqrt(d * d + f * f); +// persistentProjectileEntity.setVelocity(d, e + g * 0.20000000298023224, f, 1.6F, (float) (14 - this.getEntityWorld().getDifficulty().getId() * 4)); +// this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); +// this.getEntityWorld().spawnEntity(persistentProjectileEntity); } protected PersistentProjectileEntity createArrowProjectile(ItemStack arrow, float damageModifier) { diff --git a/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateRenderer.java b/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateRenderer.java index 5f1291e..9231199 100644 --- a/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateRenderer.java +++ b/src/main/java/ace/actually/pirates/entities/friendly_pirate/FriendlyPirateRenderer.java @@ -2,15 +2,17 @@ import ace.actually.pirates.entities.pirate_default.PirateEntity; import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.render.entity.IllagerEntityRenderer; import net.minecraft.client.render.entity.MobEntityRenderer; import net.minecraft.client.render.entity.feature.HeldItemFeatureRenderer; import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.render.entity.model.EntityModelLayers; +import net.minecraft.client.render.entity.model.IllagerEntityModel; import net.minecraft.util.Identifier; -public class FriendlyPirateRenderer extends MobEntityRenderer> { +public class FriendlyPirateRenderer extends IllagerEntityRenderer { public FriendlyPirateRenderer(EntityRendererFactory.Context context) { - super(context, new FriendlyPirateModel(context.getPart(EntityModelLayers.PILLAGER)), 0.5F); + super(context, new IllagerEntityModel<>(context.getPart(EntityModelLayers.PILLAGER)), 0.5F); this.addFeature(new HeldItemFeatureRenderer(this, context.getHeldItemRenderer())); } diff --git a/src/main/java/ace/actually/pirates/entities/pirate_abstract/AbstractPirateEntity.java b/src/main/java/ace/actually/pirates/entities/pirate_abstract/AbstractPirateEntity.java index 876926f..890dadd 100644 --- a/src/main/java/ace/actually/pirates/entities/pirate_abstract/AbstractPirateEntity.java +++ b/src/main/java/ace/actually/pirates/entities/pirate_abstract/AbstractPirateEntity.java @@ -1,14 +1,24 @@ package ace.actually.pirates.entities.pirate_abstract; +import ace.actually.pirates.Pirates; +import ace.actually.pirates.blocks.CannonPrimingBlock; +import ace.actually.pirates.blocks.MotionInvokingBlock; +import ace.actually.pirates.entities.friendly_pirate.FriendlyPirateEntity; import ace.actually.pirates.events.IPirateDies; import ace.actually.pirates.util.DisarmUtils; +import ewewukek.musketmod.GunItem; +import ewewukek.musketmod.Items; import net.minecraft.entity.EntityData; import net.minecraft.entity.EntityType; import net.minecraft.entity.SpawnReason; import net.minecraft.entity.ai.goal.LookAroundGoal; import net.minecraft.entity.ai.goal.LookAtEntityGoal; -import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.data.DataTracker; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.entity.data.TrackedDataHandlerRegistry; +import net.minecraft.entity.mob.IllagerEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; import net.minecraft.nbt.NbtCompound; import net.minecraft.util.math.BlockPos; import net.minecraft.world.LocalDifficulty; @@ -16,11 +26,13 @@ import net.minecraft.world.World; import org.valkyrienskies.mod.common.VSGameUtilsKt; -public abstract class AbstractPirateEntity extends HostileEntity { +import java.util.Objects; + +public abstract class AbstractPirateEntity extends IllagerEntity { protected BlockPos blockToDisable; - protected AbstractPirateEntity(EntityType entityType, World world, BlockPos blockToDisable) { + protected AbstractPirateEntity(EntityType entityType, World world, BlockPos blockToDisable) { super(entityType, world); this.blockToDisable = blockToDisable; @@ -30,7 +42,14 @@ protected AbstractPirateEntity(EntityType entityType, W public boolean isPersistent() { return true; } - + @Override + public void addBonusForWave(int wave, boolean unused) { + // Your pirate might not need any wave bonus, so leave it empty or log something + } + @Override + public net.minecraft.sound.SoundEvent getCelebratingSound() { + return null; // or a custom pirate celebration sound if you have one + } @Override public EntityData initialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, EntityData entityData, NbtCompound entityTag) { @@ -56,9 +75,6 @@ public void remove(RemovalReason reason) { IPirateDies.EVENT.invoker().interact(attackingPlayer,this); super.remove(reason); } - - - public boolean isOnShip() { return VSGameUtilsKt.getShipManaging(this) != null; } @@ -84,4 +100,36 @@ public void readCustomDataFromNbt(NbtCompound nbt) { this.blockToDisable = new BlockPos(x, y, z); } } + // for musket mod with reloading and firing + public static final Item[] guns = { + Items.MUSKET, + Items.MUSKET_WITH_BAYONET, + Items.BLUNDERBUSS, + Items.PISTOL, + Items.MUSKET_WITH_SCOPE + }; + protected static final TrackedData CHARGING = + DataTracker.registerData(FriendlyPirateEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + @Override + protected void initDataTracker() { + super.initDataTracker(); + dataTracker.startTracking(CHARGING, false); + } + public boolean isCharging() { + return this.dataTracker.get(CHARGING); + } + public void setCharging(boolean charging) { + this.dataTracker.set(CHARGING, charging); + } + @Override + public IllagerEntity.State getState() { + if (this.isCharging()) { + return State.CROSSBOW_CHARGE; + } else if (GunItem.isHoldingGun(this)) { + if (this.isAttacking()) + return State.CROSSBOW_HOLD; + return ((this.isHolding(Items.PISTOL)) ? State.NEUTRAL : State.CROSSBOW_CHARGE); + } + return State.CROSSBOW_CHARGE; // almost never reach this state + } } diff --git a/src/main/java/ace/actually/pirates/entities/pirate_abstract/PirateGunAttackGoal.java b/src/main/java/ace/actually/pirates/entities/pirate_abstract/PirateGunAttackGoal.java new file mode 100644 index 0000000..93f8ff9 --- /dev/null +++ b/src/main/java/ace/actually/pirates/entities/pirate_abstract/PirateGunAttackGoal.java @@ -0,0 +1,168 @@ +package ace.actually.pirates.entities.pirate_abstract; +import ace.actually.pirates.entities.friendly_pirate.FriendlyPirateEntity; +import ewewukek.musketmod.GunItem; +import ewewukek.musketmod.RangedGunAttackGoal; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.goal.Goal; +import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.RaycastContext; +import org.valkyrienskies.mod.common.VSGameUtilsKt; + +import java.util.Objects; + +public class PirateGunAttackGoal extends RangedGunAttackGoal { + private final double speed; + private final float squaredRange; + private int targetSeeingTicker = 0; + private boolean movingToLeft = false; + private boolean backward = false; + private int combatTicks = -1; + private int attackDelay = 0; + protected final AbstractPirateEntity pirate; + public PirateGunAttackGoal(T mob, double speed, int attackInterval, float range) { + super(mob); + this.speed = speed; + this.squaredRange = range * range; + this.pirate = (AbstractPirateEntity) mob; + } + + @Override + public void tick() { + super.tick(); + + LivingEntity target = this.mob.getTarget(); + if (target != null) { + double distance = this.mob.squaredDistanceTo(target); + boolean canSee = this.mob.getWorld().raycast(new RaycastContext( + this.mob.getEyePos(), + target.getEyePos(), + RaycastContext.ShapeType.COLLIDER, + RaycastContext.FluidHandling.NONE, + this.mob + )).getType() == HitResult.Type.MISS; + + + if (canSee != (targetSeeingTicker > 0)) { + targetSeeingTicker = 0; + } + if (canSee) targetSeeingTicker++; + else targetSeeingTicker--; + + // 🏹 Bonus: Prevent chasing while reloading from elevated position + if (this.mob.getY() - target.getY() > 3.0 && !this.isReady()) { + this.mob.getNavigation().stop(); + combatTicks = -1; + return; + } + + if (distance <= this.squaredRange && targetSeeingTicker >= 20) { + this.mob.getNavigation().stop(); + combatTicks++; + if (Math.abs(this.mob.getY() - target.getY()) > 5) { + // Vertical target too far — stop navigation to avoid spinning + this.mob.getNavigation().stop(); + this.mob.getLookControl().lookAt(target.getX(), target.getEyeY(), target.getZ(), 30.0F, 30.0F); + return; + } + + } else if ( + this.isReady() && + VSGameUtilsKt.getShipManaging(this.mob) != null && + VSGameUtilsKt.getShipManaging(target) != null && + Objects.equals(VSGameUtilsKt.getShipManaging(this.mob), VSGameUtilsKt.getShipManaging(target)) + ) { + BlockPos targetBlockPos = BlockPos.ofFloored(target.getX(), target.getY(), target.getZ()); + + if ( + VSGameUtilsKt.getShipManaging(this.mob) == + VSGameUtilsKt.getShipObjectManagingPos((ServerWorld) this.mob.getWorld(), targetBlockPos) + ) { + this.mob.getNavigation().startMovingTo(target, this.speed); + combatTicks = -1; + } else { + this.mob.getNavigation().stop(); // Prevent walking off ship + } + } else { + this.mob.addStatusEffect(new StatusEffectInstance(StatusEffects.REGENERATION)); + } + + if (combatTicks >= 20) { + if (this.mob.getRandom().nextFloat() < 0.3) movingToLeft = !movingToLeft; + if (this.mob.getRandom().nextFloat() < 0.3) backward = !backward; + combatTicks = 0; + } + + if (combatTicks > -1) { + if (distance > squaredRange * 0.75f) backward = false; + else if (distance < squaredRange * 0.25f) backward = true; + this.mob.getMoveControl().strafeTo(backward ? -0.5f : 0.5f, movingToLeft ? 0.5f : -0.5f); + + Vec3d targetEyePos = target.getEyePos(); + this.mob.getLookControl().lookAt(targetEyePos.x, targetEyePos.y, targetEyePos.z, 30.0F, 30.0F); + + Entity vehicle = this.mob.getControllingVehicle(); + if (vehicle instanceof MobEntity mobVehicle) { + mobVehicle.getLookControl().lookAt(targetEyePos.x, targetEyePos.y, targetEyePos.z, 30.0F, 30.0F); + } + } else { + Vec3d targetEyePos = target.getEyePos(); + this.mob.getLookControl().lookAt(targetEyePos.x, targetEyePos.y, targetEyePos.z, 30.0F, 30.0F); + } + + // 🔫 Firing logic (unchanged) + if (this.isReady()) { + pirate.setCharging(false); + this.mob.setAttacking(true); + if (attackDelay > 0) { + attackDelay--; + } else if (canSee) { + this.fire(2.0F); + attackDelay = 20 + this.mob.getRandom().nextInt(10); + } + } else { + if (!this.mob.isUsingItem()) { + this.reload(); + pirate.setCharging(true); + this.mob.setAttacking(false); + } + } + } + } + + @Override + public void onReady() { + this.attackDelay = 20; + pirate.setCharging(false); // ✅ Stop showing reload pose + } + @Override + public boolean canStart() { + return GunItem.isHoldingGun(this.mob); // Ignore target + } + @Override + public void stop() { + this.mob.setAttacking(false); + + if (this.mob.isUsingItem()) { + this.mob.clearActiveItem(); + } + + // ☑️ If gun is not ready, reload after combat ends + if (!this.isReady() && !this.mob.isUsingItem()) { + this.reload(); + pirate.setCharging(true); // show reload animation + } else { + pirate.setCharging(false); + } + } +} + diff --git a/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntity.java b/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntity.java index c35f6a6..9266c8e 100644 --- a/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntity.java +++ b/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntity.java @@ -4,6 +4,7 @@ import ace.actually.pirates.entities.friendly_pirate.FriendlyPirateEntity; import ace.actually.pirates.entities.pirate_abstract.AbstractPirateEntity; import ace.actually.pirates.entities.pirate_abstract.PirateBowAttackGoal; +import ace.actually.pirates.entities.pirate_abstract.PirateGunAttackGoal; import ace.actually.pirates.entities.pirate_abstract.PirateWanderArroundFarGoal; import net.minecraft.entity.*; import net.minecraft.entity.ai.RangedAttackMob; @@ -16,6 +17,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.PersistentProjectileEntity; import net.minecraft.entity.projectile.ProjectileUtil; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.sound.SoundEvents; @@ -38,7 +40,8 @@ public PirateEntity(World world, BlockPos blockToDisable) { @Override protected void initGoals() { super.initGoals(); - this.goalSelector.add(3, new PirateBowAttackGoal<>(this, 1.0D, 20, 20.0F)); +// this.goalSelector.add(3, new PirateBowAttackGoal<>(this, 1.0D, 20, 20.0F)); + this.goalSelector.add(3, new PirateGunAttackGoal<>(this, 1.0D, 20, 20.0F)); this.targetSelector.add(1, new ActiveTargetGoal<>(this, PlayerEntity.class, true)); this.targetSelector.add(3, new ActiveTargetGoal(this, MerchantEntity.class, false)); this.targetSelector.add(3, new ActiveTargetGoal(this, IronGolemEntity.class, true)); @@ -48,21 +51,21 @@ protected void initGoals() { @Override protected void initEquipment(Random random, LocalDifficulty localDifficulty) { super.initEquipment(random, localDifficulty); - this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(guns[random.nextInt(guns.length)])); } @Override public void attack(LivingEntity target, float pullProgress) { - ItemStack itemStack = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, Items.BOW)); - PersistentProjectileEntity persistentProjectileEntity = this.createArrowProjectile(itemStack, pullProgress); - double d = target.getX() - this.getX(); - double e = target.getBodyY(0.3333333333333333) - persistentProjectileEntity.getY(); - double f = target.getZ() - this.getZ(); - double g = Math.sqrt(d * d + f * f); - persistentProjectileEntity.setVelocity(d, e + g * 0.20000000298023224, f, 1.6F, (float) (14 - this.getEntityWorld().getDifficulty().getId() * 4)); - this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); - this.getEntityWorld().spawnEntity(persistentProjectileEntity); +// ItemStack itemStack = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, Items.BOW)); +// PersistentProjectileEntity persistentProjectileEntity = this.createArrowProjectile(itemStack, pullProgress); +// double d = target.getX() - this.getX(); +// double e = target.getBodyY(0.3333333333333333) - persistentProjectileEntity.getY(); +// double f = target.getZ() - this.getZ(); +// double g = Math.sqrt(d * d + f * f); +// persistentProjectileEntity.setVelocity(d, e + g * 0.20000000298023224, f, 1.6F, (float) (14 - this.getEntityWorld().getDifficulty().getId() * 4)); +// this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); +// this.getEntityWorld().spawnEntity(persistentProjectileEntity); } diff --git a/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntityRenderer.java b/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntityRenderer.java index ae82e10..fb45710 100644 --- a/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntityRenderer.java +++ b/src/main/java/ace/actually/pirates/entities/pirate_default/PirateEntityRenderer.java @@ -1,14 +1,15 @@ package ace.actually.pirates.entities.pirate_default; import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.render.entity.IllagerEntityRenderer; import net.minecraft.client.render.entity.MobEntityRenderer; import net.minecraft.client.render.entity.feature.HeldItemFeatureRenderer; import net.minecraft.client.render.entity.model.*; import net.minecraft.util.Identifier; -public class PirateEntityRenderer extends MobEntityRenderer> { +public class PirateEntityRenderer extends IllagerEntityRenderer { public PirateEntityRenderer(EntityRendererFactory.Context context) { - super(context, new PirateEntityModel(context.getPart(EntityModelLayers.PILLAGER)), 0.5F); + super(context, new IllagerEntityModel(context.getPart(EntityModelLayers.PILLAGER)), 0.5F); this.addFeature(new HeldItemFeatureRenderer(this, context.getHeldItemRenderer())); } diff --git a/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntity.java b/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntity.java index 2e3b695..ade6ad5 100644 --- a/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntity.java +++ b/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntity.java @@ -3,6 +3,7 @@ import ace.actually.pirates.Pirates; import ace.actually.pirates.entities.pirate_abstract.AbstractPirateEntity; import ace.actually.pirates.entities.pirate_abstract.PirateBowAttackGoal; +import ace.actually.pirates.entities.pirate_abstract.PirateGunAttackGoal; import ace.actually.pirates.entities.pirate_abstract.PirateWanderArroundFarGoal; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.Enchantments; @@ -53,28 +54,30 @@ public SkeletonPirateEntity(World world, BlockPos blockToDisable) { @Override protected void initGoals() { super.initGoals(); - this.goalSelector.add(3, new PirateBowAttackGoal<>(this, 1.0D, 20, 20.0F)); +// this.goalSelector.add(3, new PirateBowAttackGoal<>(this, 1.0D, 20, 20.0F)); + this.goalSelector.add(3, new PirateGunAttackGoal<>(this, 1.0D, 20, 20.0F)); } @Override protected void initEquipment(Random random, LocalDifficulty localDifficulty) { super.initEquipment(random, localDifficulty); - this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); +// this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(guns[random.nextInt(guns.length)])); } @Override public void attack(LivingEntity target, float pullProgress) { - ItemStack itemStack = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, Items.BOW)); - PersistentProjectileEntity persistentProjectileEntity = this.createArrowProjectile(itemStack, pullProgress); - double d = target.getX() - this.getX(); - double e = target.getBodyY(0.3333333333333333) - persistentProjectileEntity.getY(); - double f = target.getZ() - this.getZ(); - double g = Math.sqrt(d * d + f * f); - persistentProjectileEntity.setVelocity(d, e + g * 0.20000000298023224, f, 1.6F, (float) (14 - this.getEntityWorld().getDifficulty().getId() * 4)); - this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); - persistentProjectileEntity.setPierceLevel((byte)2); - this.getEntityWorld().spawnEntity(persistentProjectileEntity); +// ItemStack itemStack = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, Items.BOW)); +// PersistentProjectileEntity persistentProjectileEntity = this.createArrowProjectile(itemStack, pullProgress); +// double d = target.getX() - this.getX(); +// double e = target.getBodyY(0.3333333333333333) - persistentProjectileEntity.getY(); +// double f = target.getZ() - this.getZ(); +// double g = Math.sqrt(d * d + f * f); +// persistentProjectileEntity.setVelocity(d, e + g * 0.20000000298023224, f, 1.6F, (float) (14 - this.getEntityWorld().getDifficulty().getId() * 4)); +// this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); +// persistentProjectileEntity.setPierceLevel((byte)2); +// this.getEntityWorld().spawnEntity(persistentProjectileEntity); } diff --git a/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntityRenderer.java b/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntityRenderer.java index 7473746..9bde567 100644 --- a/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntityRenderer.java +++ b/src/main/java/ace/actually/pirates/entities/pirate_skeleton/SkeletonPirateEntityRenderer.java @@ -1,18 +1,21 @@ package ace.actually.pirates.entities.pirate_skeleton; import ace.actually.pirates.ClientPirates; +import ace.actually.pirates.entities.pirate_default.PirateEntity; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.render.entity.IllagerEntityRenderer; import net.minecraft.client.render.entity.MobEntityRenderer; import net.minecraft.client.render.entity.feature.HeldItemFeatureRenderer; +import net.minecraft.client.render.entity.model.IllagerEntityModel; import net.minecraft.util.Identifier; @Environment(EnvType.CLIENT) -public class SkeletonPirateEntityRenderer extends MobEntityRenderer> { +public class SkeletonPirateEntityRenderer extends IllagerEntityRenderer { public SkeletonPirateEntityRenderer(EntityRendererFactory.Context context) { - super(context, new SkeletonPirateModel<>(context.getPart(ClientPirates.SKELETON_PIRATE)), 0.5F); + super(context, new IllagerEntityModel<>(context.getPart(ClientPirates.SKELETON_PIRATE)), 0.5F); this.addFeature(new HeldItemFeatureRenderer<>(this, context.getHeldItemRenderer())); } diff --git a/src/main/java/ace/actually/pirates/entities/shot/ShotEntity.java b/src/main/java/ace/actually/pirates/entities/shot/ShotEntity.java index 97f26ba..536582a 100644 --- a/src/main/java/ace/actually/pirates/entities/shot/ShotEntity.java +++ b/src/main/java/ace/actually/pirates/entities/shot/ShotEntity.java @@ -1,6 +1,7 @@ package ace.actually.pirates.entities.shot; import ace.actually.pirates.Pirates; +import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; @@ -12,25 +13,38 @@ import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket; +import net.minecraft.particle.DefaultParticleType; import net.minecraft.particle.ParticleTypes; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.RaycastContext; import net.minecraft.world.World; +import org.joml.Vector3d; +import org.valkyrienskies.core.api.ships.Ship; +import org.valkyrienskies.eureka.fabric.EurekaModFabric; +import org.valkyrienskies.mod.common.VSClientGameUtils; +import org.valkyrienskies.mod.common.VSGameUtilsKt; + +import java.util.function.Consumer; public class ShotEntity extends ThrownItemEntity implements FlyingItemEntity { private LivingEntity in; private float damage=6; private String extra=""; private int tickAge = 0; + private World world; public ShotEntity(EntityType entityType, World world, LivingEntity caster, Item toShow, float damageTo, String special) { super(entityType, world); in=caster; setItem(new ItemStack(toShow)); - damage=damageTo; + damage=20; extra=special; } @@ -72,7 +86,126 @@ protected void onEntityHit(EntityHitResult entityHitResult) { explode(); } } + private BlockPos getCollisionBlock() { + HitResult hitResult = this.getWorld().raycast(new RaycastContext( + this.getPos(), + this.getPos().add(this.getVelocity().normalize().multiply(2)), // 🔹 Extend raycast slightly forward + RaycastContext.ShapeType.COLLIDER, + RaycastContext.FluidHandling.NONE, + this + )); + + if (hitResult instanceof BlockHitResult blockHitResult) { + return blockHitResult.getBlockPos(); // ✅ Return exact impacted block + } + return null; // No impact detected + } + private boolean isBelowWaterLine(BlockPos pos, Ship ship, Vec3d shotDirection) { + // Convert BlockPos to world coordinates + Vector3d worldVec = VSGameUtilsKt.toWorldCoordinates(ship, + pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); + + // Convert to world BlockPos + BlockPos worldPos = new BlockPos((int) Math.floor(worldVec.x()), + (int) Math.floor(worldVec.y()), + (int) Math.floor(worldVec.z())); + + // Step 1️⃣ — Check if water below (depth 3) + boolean waterBelow = false; + for (int i = 0; i < 3; i++) { + BlockPos checkPos = worldPos.down(i); + BlockState state = this.getWorld().getBlockState(checkPos); + if (state.isOf(Blocks.WATER)) { + waterBelow = true; + break; + } + } + if (!waterBelow) { + // Debug: + // System.out.println("No water below → no leak"); + return false; // Not below waterline → skip further checks + } + + // Step 2️⃣ — Check surrounding blocks to determine if hull or thin structure (mast, etc.) + // Calculate "left" and "right" vectors based on shot direction + Vec3d left = shotDirection.crossProduct(new Vec3d(0, 1, 0)).normalize(); // Left + Vec3d right = left.multiply(-1); // Right + + // Prepare surrounding positions + BlockPos leftPos = worldPos.add((int) Math.round(left.x), 0, (int) Math.round(left.z)); + BlockPos rightPos = worldPos.add((int) Math.round(right.x), 0, (int) Math.round(right.z)); + BlockPos upPos = worldPos.up(); + BlockPos downPos = worldPos.down(); + + // Get block states + BlockState centerState = this.getWorld().getBlockState(worldPos); + BlockState leftState = this.getWorld().getBlockState(leftPos); + BlockState rightState = this.getWorld().getBlockState(rightPos); + BlockState upState = this.getWorld().getBlockState(upPos); + BlockState downState = this.getWorld().getBlockState(downPos); + + // Count how many neighbors match center block + int sameCount = 0; + if (leftState.getBlock() == centerState.getBlock()) sameCount++; + if (rightState.getBlock() == centerState.getBlock()) sameCount++; + if (upState.getBlock() == centerState.getBlock()) sameCount++; + if (downState.getBlock() == centerState.getBlock()) sameCount++; + + // Step 3️⃣ — Threshold for "is this block part of hull" + int threshold = 2; // Tune this — recommend 2 or 3 + + if (sameCount >= threshold) { + // Debug: + // System.out.println("HULL detected! sameCount=" + sameCount); + return true; // Surrounded → likely hull → leak + } else { + // Debug: + // System.out.println("THIN structure (mast/beam), sameCount=" + sameCount + " → no leak"); + return false; // Thin structure → no leak + } + } + /** + * 🔹 Applies explosion splash damage & knockback to entities near the impact area. + */ + private void applyExplosionEffects(World world, BlockPos blockPos) { + ServerWorld serverWorld = (ServerWorld) world; + + // 🌫️ **Spawn Debris Particles** + serverWorld.spawnParticles(ParticleTypes.CAMPFIRE_COSY_SMOKE, + blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5, + 15, 0.4, 0.4, 0.4, 0.1); + + // 💥 **Small Shockwave Effect** + serverWorld.spawnParticles(ParticleTypes.SMOKE, + blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5, + 10, 0.3, 0.3, 0.3, 0.05); + // 💨 **Trigger a Small Explosion Effect (Visual Only)** + world.createExplosion(this, blockPos.getX(), blockPos.getY(), blockPos.getZ(), + 0.0f, extra.contains("fire"), World.ExplosionSourceType.TNT); + + // 💣 Apply Knockback & Damage to Nearby Entities + double explosionRadius = 2.0; + double knockbackStrength = 2.0; + world.getOtherEntities(this, this.getBoundingBox().expand(explosionRadius)).forEach(entity -> { + entity.damage(this.getDamageSources().explosion(null), damage); + Vec3d pushDirection = entity.getPos().subtract(this.getPos()).normalize(); + double forceMultiplier = 1.0 - (entity.getPos().distanceTo(this.getPos()) / explosionRadius); + Vec3d knockback = pushDirection.multiply(knockbackStrength * forceMultiplier); + entity.addVelocity(knockback.x, knockback.y + 0.2, knockback.z); + entity.velocityModified = true; + }); + } + private void damageBlockVisual(World world, BlockPos blockPos) { + int damageStage = 9; + if (world instanceof ServerWorld serverWorld) { + // Assign a random entity ID for the visual effect + int entityId = blockPos.hashCode(); + + // Send block breaking animation + serverWorld.setBlockBreakingInfo(entityId, blockPos, damageStage); + } + } @Override protected void onBlockHit(BlockHitResult blockHitResult) { super.onBlockHit(blockHitResult); @@ -84,10 +217,84 @@ protected void onBlockHit(BlockHitResult blockHitResult) { } private void explode() { - this.getWorld().createExplosion(this, this.getX(), this.getY(), this.getZ(), Pirates.baseShotPower, extra.contains("fire"), World.ExplosionSourceType.TNT); + World world = this.getWorld(); + BlockPos impactPos = this.getCollisionBlock(); // ✅ Get exact impact block + if (impactPos == null) { + impactPos = this.getBlockPos(); // Fallback in case collision is null + } + + // 🔹 Shot direction (normalize for precision) + Vec3d shotDirection = this.getVelocity().normalize(); + Vec3d currentPos = new Vec3d(impactPos.getX() + 0.5, impactPos.getY() + 0.5, impactPos.getZ() + 0.5); // Center on block + + int penetrationPower = 3; // 🔹 2 blocks will be pierced + double stepSize = 1; // 🔹 Move forward 1 block per step + + boolean isShipBlock = VSGameUtilsKt.isBlockInShipyard(world, impactPos); + + if (!isShipBlock) { + Consumer breakAndExplode = pos -> { + BlockState state = world.getBlockState(pos); + if (!state.isAir() && state.getHardness(world, pos) >= 0) { + world.breakBlock(pos, true); + } + applyExplosionEffects(world, pos); + }; + + // 🔹 Ensure first block is destroyed + breakAndExplode.accept(impactPos); + // ✅ Handle Non-Ship Blocks (Piercing Shot with Breakable Blocks) + for (int i = 0; i < penetrationPower; i++) { + currentPos = currentPos.add(shotDirection.multiply(stepSize)); // Move forward in exact trajectory + BlockPos blockToBreak = new BlockPos((int) Math.floor(currentPos.x), + (int) Math.floor(currentPos.y), + (int) Math.floor(currentPos.z)); + breakAndExplode.accept(blockToBreak); + } + } else { + // ✅ Handle Ship Blocks (Check if Below Waterline or Not) + Ship ship = VSGameUtilsKt.getShipManagingPos(world, impactPos); + if (ship == null) + return; + + boolean isBelowWater = isBelowWaterLine(impactPos, ship, shotDirection); + // lambda + Consumer breakAndExplodeHull = blockToAffect -> { + BlockState blockStateNext = world.getBlockState(blockToAffect); + if (!blockStateNext.isAir() && blockStateNext.getHardness(world, blockToAffect) >= 0) { + if (isBelowWater) { + // 🌊 Below Waterline: Replace Blocks with Netherite for Sinking Effect +// world.setBlockState(blockToAffect, Pirates.HEAVY_BLOCK.getDefaultState()); + } else { + // 🚢 Above Waterline: Apply Visual Damage & Splash Damage + if (!blockStateNext.isAir() && blockStateNext.getHardness(world, blockToAffect) >= 0) { + world.breakBlock(blockToAffect, true); + } + + damageBlockVisual(world, blockToAffect); + applyExplosionEffects(world, blockToAffect); + } + } else { + // 🛠️ If the block is NOT breakable, just apply knockback & explosion effects + applyExplosionEffects(world, blockToAffect); + } + }; + + // 🔹 Ensure first block is affected + breakAndExplodeHull.accept(impactPos); +// // just for sinking +// penetrationPower = 2; // make water sink this ship in a sensible way :33 +// for (int i = 0; i < penetrationPower; i++) { +// currentPos = currentPos.add(shotDirection.multiply(stepSize)); // Move forward in exact trajectory +// BlockPos blockToAffect = new BlockPos((int) Math.floor(currentPos.x), +// (int) Math.floor(currentPos.y), +// (int) Math.floor(currentPos.z)); +// breakAndExplodeHull.accept(blockToAffect); +// } + } + this.discard(); } - @Override protected Item getDefaultItem() { return Pirates.CANNONBALL_ENT; diff --git a/src/main/java/ace/actually/pirates/util/CannonDispenserBehavior.java b/src/main/java/ace/actually/pirates/util/CannonDispenserBehavior.java index 8b29560..495df94 100644 --- a/src/main/java/ace/actually/pirates/util/CannonDispenserBehavior.java +++ b/src/main/java/ace/actually/pirates/util/CannonDispenserBehavior.java @@ -15,6 +15,7 @@ import org.valkyrienskies.core.api.ships.Ship; import org.valkyrienskies.mod.common.VSGameUtilsKt; import org.valkyrienskies.mod.common.util.VectorConversionsMCKt; +import net.minecraft.particle.DustParticleEffect; /** * A dispenser behavior that spawns a projectile with velocity in front of the dispenser. @@ -35,23 +36,19 @@ public ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { projectileEntity.addVelocity(VectorConversionsMCKt.toMinecraft(ship.getVelocity()).multiply(1/60.0)); } - if (!world.isClient) { - int xmod = 0; - int ymod = 0; - int zmod = 0; + if (!world.isClient()) { + net.minecraft.network.PacketByteBuf buf = net.fabricmc.fabric.api.networking.v1.PacketByteBufs.create(); + buf.writeDouble(position.getX()); + buf.writeDouble(position.getY()); + buf.writeDouble(position.getZ()); + buf.writeInt(direction.getId()); - switch (direction) { - case NORTH -> zmod = -1; - case EAST -> xmod = 1; - case SOUTH -> zmod = 1; - case WEST -> xmod = -1; - case UP -> ymod = 1; - case DOWN -> ymod = -1; - } - for(int i = 0; i < 40; ++i) { - world.spawnParticles(ParticleTypes.CLOUD, position.getX() + xmod + (2 * world.random.nextDouble()) - 1, position.getY() + ymod + (2 * world.random.nextDouble()) - 0.8, position.getZ() + zmod + (2 * world.random.nextDouble()) - 1, 1, 0.0, 0.0, 0.0, 0.005); + for (net.minecraft.server.network.ServerPlayerEntity player : + net.fabricmc.fabric.api.networking.v1.PlayerLookup.tracking(world, pointer.getPos())) { + net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.send(player, Pirates.CANNON_SMOKE_PACKET_ID, buf); } } + stack.decrement(1); return stack; } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9d5bba6..938af1b 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "pirates", "version": "${version}", - "name": "Valkyrien Pirates", + "name": "My Custom Valkyrien Pirates", "description": "Pirates as they should be.", "authors": [ "Acrogenous",