diff --git a/build.gradle b/build.gradle index 0efd2e8..7410bfb 100644 --- a/build.gradle +++ b/build.gradle @@ -39,11 +39,11 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" // 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-skies:1.20.1-fabric-2.4.11" + modImplementation "maven.modrinth:eureka:1.20.1-fabric-1.6.3" + modImplementation "maven.modrinth:vlib:1.20.1-0.1.1" + modImplementation "maven.modrinth:valkyrien-sails:1.20.1-0.1.7-fabric" + modCompileOnly "maven.modrinth:musket-mod:1.5.4" modImplementation "maven.modrinth:architectury-api:9.2.14+fabric" diff --git a/gradle-compile-final.err.log b/gradle-compile-final.err.log new file mode 100644 index 0000000..4d56c9b --- /dev/null +++ b/gradle-compile-final.err.log @@ -0,0 +1,23 @@ +D:\Games\SKLaunchersVersions\ValkyrienPirateCodeWorskspace\pirates\src\main\java\ace\actually\pirates\blocks\entity\MotionInvokingBlockEntity.java:24: error: cannot find symbol +import org.valkyrienskies.mod.common.util.GameTickForceApplier; + ^ + symbol: class GameTickForceApplier + location: package org.valkyrienskies.mod.common.util +D:\Games\SKLaunchersVersions\ValkyrienPirateCodeWorskspace\pirates\src\main\java\ace\actually\pirates\blocks\entity\StableBlockEntity.java:14: error: cannot find symbol +import org.valkyrienskies.mod.common.util.GameTickForceApplier; + ^ + symbol: class GameTickForceApplier + location: package org.valkyrienskies.mod.common.util +2 errors + +FAILURE: Build failed with an exception. + +* What went wrong: +Execution failed for task ':compileJava'. +> Compilation failed; see the compiler error output for details. + +* Try: +> Run with --info option to get more log output. +> Run with --scan to get full insights. + +BUILD FAILED in 2m 16s diff --git a/gradle-compile-final.log b/gradle-compile-final.log new file mode 100644 index 0000000..35b220e --- /dev/null +++ b/gradle-compile-final.log @@ -0,0 +1,30 @@ +To honour the JVM settings for this build a single-use Daemon process will be forked. For more on this, please refer to https://docs.gradle.org/8.7/userguide/gradle_daemon.html#sec:disabling_the_daemon in the Gradle documentation. +Daemon will be stopped at the end of the build + +> Configure project : +Fabric Loom: 1.6.12 +Refresh dependencies is in use, loom will be significantly slower. +"Lock for cache='C:\Users\Admin\.gradle\caches\fabric-loom', project=':'" is currently held by pid '2264'. +Run with --info or --debug to show arguments, may reveal sensitive info +- pid: 2264 +- command: C:\Users\Admin\.jdks\ms-21.0.12\bin\java.exe +- started at: 2026-08-01T08:08:25.670Z +- user: ADMIN-PC\Admin +- parent: + - title: 'pirates build.gradle (pirates)' + - pid: 21876 + - command: D:\IntelliJ IDEA 2026.2.0.1\bin\idea64.exe + - started at: 2026-08-01T08:05:14.989Z + - user: ADMIN-PC\Admin +Waiting for lock to be released... +:remapping 60 mods from modImplementation (java-api) +:remapping 60 mods from modImplementation (java-runtime) +:remapping 1 mods from modCompileOnly (java-api) + +> Task :checkKotlinGradlePluginConfigurationErrors +> Task :compileKotlin NO-SOURCE + +> Task :compileJava + +> Task :compileJava FAILED +2 actionable tasks: 2 executed diff --git a/gradle-compile.err.log b/gradle-compile.err.log new file mode 100644 index 0000000..e5d42e8 --- /dev/null +++ b/gradle-compile.err.log @@ -0,0 +1,44 @@ + +FAILURE: Build completed with 2 failures. + +1: Task failed with an exception. +----------- +* What went wrong: +A problem occurred configuring root project 'pirates'. +> Could not resolve all files for configuration ':modImplementation'. + > Could not find maven.modrinth:vlib:1.20.1-0.1.1+fabric. + Searched in the following locations: + - https://api.modrinth.com/maven/maven/modrinth/vlib/1.20.1-0.1.1+fabric/vlib-1.20.1-0.1.1+fabric.pom + Required by: + project : + +* Try: +> If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration. +> Run with --stacktrace option to get the stack trace. +> Run with --info or --debug option to get more log output. +> Run with --scan to get full insights. +> Get more help at https://help.gradle.org. +============================================================================== + +2: Task failed with an exception. +----------- +* What went wrong: +Failed to query the value of property 'buildFlowServiceProperty'. +> Could not isolate value org.jetbrains.kotlin.gradle.plugin.statistics.BuildFlowService$Parameters_Decorated@2dad279 of type BuildFlowService.Parameters + > A problem occurred configuring root project 'pirates'. + > Could not resolve all files for configuration ':modImplementation'. + > Could not find maven.modrinth:vlib:1.20.1-0.1.1+fabric. + Searched in the following locations: + - https://api.modrinth.com/maven/maven/modrinth/vlib/1.20.1-0.1.1+fabric/vlib-1.20.1-0.1.1+fabric.pom + Required by: + project : + +* Try: +> If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration. +> Run with --stacktrace option to get the stack trace. +> Run with --info or --debug option to get more log output. +> Run with --scan to get full insights. +> Get more help at https://help.gradle.org. +============================================================================== + +BUILD FAILED in 47s diff --git a/gradle-compile.log b/gradle-compile.log new file mode 100644 index 0000000..abb818e --- /dev/null +++ b/gradle-compile.log @@ -0,0 +1,8 @@ +To honour the JVM settings for this build a single-use Daemon process will be forked. For more on this, please refer to https://docs.gradle.org/8.7/userguide/gradle_daemon.html#sec:disabling_the_daemon in the Gradle documentation. +Daemon will be stopped at the end of the build + +> Configure project : +Fabric Loom: 1.6.12 +Refresh dependencies is in use, loom will be significantly slower. +Previous process has disowned the lock due to abrupt termination. +Found existing cache lock file (ACQUIRED_PREVIOUS_OWNER_DISOWNED), rebuilding loom cache. This may have been caused by a failed or canceled build. diff --git a/gradle.properties b/gradle.properties index f7137e3..2b41080 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.6 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 76e46ed..0436b17 100644 --- a/src/main/java/ace/actually/pirates/Pirates.java +++ b/src/main/java/ace/actually/pirates/Pirates.java @@ -57,7 +57,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)); @@ -133,7 +133,7 @@ public void onInitialize() { ConfigUtils.checkConfigs(); baseShotPower = Float.parseFloat(ConfigUtils.config.getOrDefault("base-shot-power","2.2")); - cannonRange = Float.parseFloat(ConfigUtils.config.getOrDefault("cannon-range","1.7")); + cannonRange = Float.parseFloat(ConfigUtils.config.getOrDefault("cannon-range","3.2")); pursuitDistance = Integer.parseInt(ConfigUtils.config.getOrDefault("pursuit-distance","10000")); shouldEnableFlyingPirates = ConfigUtils.config.getOrDefault("should-enable-flying-pirates","false").equals("true"); @@ -193,6 +193,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); @@ -203,6 +215,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); } @@ -236,6 +250,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 9bba869..cd4d439 100644 --- a/src/main/java/ace/actually/pirates/blocks/entity/CannonPrimingBlockEntity.java +++ b/src/main/java/ace/actually/pirates/blocks/entity/CannonPrimingBlockEntity.java @@ -83,9 +83,10 @@ private static boolean checkShouldFire(ServerWorld world, BlockPos pos, BlockSta 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 ba673e5..117cfff 100644 --- a/src/main/java/ace/actually/pirates/blocks/entity/CrewSpawnerBlockEntity.java +++ b/src/main/java/ace/actually/pirates/blocks/entity/CrewSpawnerBlockEntity.java @@ -1,11 +1,13 @@ 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.events.IPirateSpawns; import ace.actually.pirates.entities.CrewTypes; import ace.actually.pirates.util.ConfigUtils; +import ace.actually.pirates.compat.MusketModCompat; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.enchantment.Enchantments; @@ -15,7 +17,9 @@ 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.registry.Registries; import net.minecraft.server.world.ServerWorld; import net.minecraft.state.property.Properties; @@ -27,6 +31,10 @@ import org.jetbrains.annotations.Nullable; import org.valkyrienskies.core.api.ships.Ship; import org.valkyrienskies.mod.common.VSGameUtilsKt; +import net.minecraft.world.EntityList; + +import java.util.Optional; + public class CrewSpawnerBlockEntity extends BlockEntity { @@ -108,19 +116,21 @@ private static BlockPos checkForBlocksToCrew (World world, BlockPos origin) { private static Entity getEntityFromState(World world, BlockEntity be) { Entity crew = null; + 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)); + MusketModCompat.equipRandomGunOrBow((AbstractPirateEntity) crew, world.random); } 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); + Item randomGun = MusketModCompat.randomGun(world.random); + ItemStack itemStack = new ItemStack(randomGun == null ? net.minecraft.item.Items.BOW : 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/blocks/entity/MotionInvokingBlockEntity.java b/src/main/java/ace/actually/pirates/blocks/entity/MotionInvokingBlockEntity.java index a70d84e..763784a 100644 --- a/src/main/java/ace/actually/pirates/blocks/entity/MotionInvokingBlockEntity.java +++ b/src/main/java/ace/actually/pirates/blocks/entity/MotionInvokingBlockEntity.java @@ -21,7 +21,8 @@ import org.valkyrienskies.core.api.ships.Ship; import org.valkyrienskies.mod.api.SeatedControllingPlayer; import org.valkyrienskies.mod.common.VSGameUtilsKt; -import org.valkyrienskies.mod.common.util.GameTickForceApplier; +import org.valkyrienskies.mod.common.ValkyrienSkiesMod; +import org.valkyrienskies.mod.common.util.GameToPhysicsAdapter; import java.util.List; @@ -214,16 +215,16 @@ private void moveShipForward(LoadedServerShip ship) { double mass = ship.getInertiaData().getMass(); Vector3d qdc = ship.getTransform().getShipToWorldRotation().getEulerAnglesZXY(new Vector3d()).normalize().mul(mass*10); qdc = new Vector3d(-qdc.x,0,-qdc.z); - GameTickForceApplier gtfa = ship.getAttachment(GameTickForceApplier.class); + GameToPhysicsAdapter gtpa = ValkyrienSkiesMod.getOrCreateGTPA(getWorld().getRegistryKey().getValue().toString()); - if(gtfa!=null) { + if(gtpa!=null) { Vector3dc v3dc = ship.getInertiaData().getCenterOfMassInShip(); Vector3d loc = new Vector3d(v3dc.x()+1,v3dc.y(),v3dc.z()+1); //if(world instanceof ServerWorld serverWorld) //{ // serverWorld.spawnParticles(ParticleTypes.BUBBLE,loc.x,loc.y,loc.z,1,0,0,0,0); //} - gtfa.applyInvariantForceToPos(qdc,loc.sub(ship.getTransform().getPositionInShip())); + gtpa.applyInvariantForceToPos(ship.getId(), qdc, loc.sub(ship.getTransform().getPositionInShip())); } } } diff --git a/src/main/java/ace/actually/pirates/blocks/entity/StableBlockEntity.java b/src/main/java/ace/actually/pirates/blocks/entity/StableBlockEntity.java index 9252812..5cbc648 100644 --- a/src/main/java/ace/actually/pirates/blocks/entity/StableBlockEntity.java +++ b/src/main/java/ace/actually/pirates/blocks/entity/StableBlockEntity.java @@ -11,7 +11,8 @@ import org.joml.Vector3d; import org.valkyrienskies.core.api.ships.LoadedServerShip; import org.valkyrienskies.mod.common.VSGameUtilsKt; -import org.valkyrienskies.mod.common.util.GameTickForceApplier; +import org.valkyrienskies.mod.common.ValkyrienSkiesMod; +import org.valkyrienskies.mod.common.util.GameToPhysicsAdapter; import org.valkyrienskies.mod.common.util.VectorConversionsMCKt; public class StableBlockEntity extends BlockEntity { @@ -47,15 +48,15 @@ public static void tick(World world, BlockPos pos, BlockState state, StableBlock LoadedServerShip ship = VSGameUtilsKt.getShipObjectManagingPos(serverWorld,pos); if(ship!=null) { - GameTickForceApplier gtfa = ship.getAttachment(GameTickForceApplier.class); + GameToPhysicsAdapter gtpa = ValkyrienSkiesMod.getOrCreateGTPA(serverWorld.getRegistryKey().getValue().toString()); - if(gtfa!=null) + if(gtpa!=null) { Vec3i vec3i = Vec3i.ZERO.up(); Vector3d v3d = VectorConversionsMCKt.toJOMLD(vec3i).mul(be.multiplier*ship.getInertiaData().getMass()); Vector3d loc = new Vector3d(pos.getX(),pos.getY(),pos.getZ()).sub(ship.getTransform().getPositionInShip()); - gtfa.applyInvariantForceToPos(v3d,loc); + gtpa.applyInvariantForceToPos(ship.getId(), v3d, loc); //gtfa.applyInvariantForce(v3d); } diff --git a/src/main/java/ace/actually/pirates/compat/MusketModCompat.java b/src/main/java/ace/actually/pirates/compat/MusketModCompat.java new file mode 100644 index 0000000..e349566 --- /dev/null +++ b/src/main/java/ace/actually/pirates/compat/MusketModCompat.java @@ -0,0 +1,71 @@ +package ace.actually.pirates.compat; + +import ace.actually.pirates.entities.pirate_abstract.AbstractPirateEntity; +import ace.actually.pirates.entities.pirate_abstract.PirateBowAttackGoal; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.ai.RangedAttackMob; +import net.minecraft.entity.ai.goal.Goal; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.random.Random; + +import java.lang.reflect.Constructor; + +public final class MusketModCompat { + public static final String MOD_ID = "musketmod"; + private static final Identifier[] GUN_IDS = { + id("musket"), id("musket_with_bayonet"), id("blunderbuss"), id("pistol"), id("musket_with_scope") + }; + private static final Identifier PISTOL_ID = id("pistol"); + + private MusketModCompat() {} + + public static boolean isLoaded() { + return FabricLoader.getInstance().isModLoaded(MOD_ID); + } + + public static Item randomGun(Random random) { + if (!isLoaded()) return null; + Item item = Registries.ITEM.get(GUN_IDS[random.nextInt(GUN_IDS.length)]); + return item == Items.AIR ? null : item; + } + + public static void equipRandomGunOrBow(AbstractPirateEntity pirate, Random random) { + Item gun = randomGun(random); + pirate.equipStack(EquipmentSlot.MAINHAND, new ItemStack(gun == null ? Items.BOW : gun)); + if (gun != null && Registries.ITEM.getId(gun).equals(PISTOL_ID)) { + pirate.equipStack(EquipmentSlot.OFFHAND, new ItemStack(gun)); + } + } + + public static boolean isHoldingGun(AbstractPirateEntity pirate) { + return isLoaded() && Registries.ITEM.getId(pirate.getMainHandStack().getItem()).getNamespace().equals(MOD_ID); + } + + public static boolean isHoldingPistol(AbstractPirateEntity pirate) { + return isLoaded() && Registries.ITEM.getId(pirate.getMainHandStack().getItem()).equals(PISTOL_ID); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static Goal createRangedGoal(T pirate) { + if (isLoaded()) { + try { + Class type = Class.forName("ace.actually.pirates.entities.pirate_abstract.PirateGunAttackGoal"); + Constructor constructor = type.getConstructor(HostileEntity.class, double.class, int.class, float.class); + return (Goal) constructor.newInstance(pirate, 1.0D, 20, 20.0F); + } catch (ReflectiveOperationException | LinkageError ignored) { + // Missing or incompatible optional dependency: retain bow behavior. + } + } + return new PirateBowAttackGoal(pirate, 1.0D, 20, 20.0F); + } + + private static Identifier id(String path) { + return new Identifier(MOD_ID, path); + } +} 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..856e3e5 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,14 @@ 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 ace.actually.pirates.compat.MusketModCompat; 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 +22,12 @@ 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 net.minecraft.nbt.NbtCompound; import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; @@ -90,9 +95,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, MusketModCompat.createRangedGoal(this)); + 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 +126,21 @@ 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)); + MusketModCompat.equipRandomGunOrBow(this, random); } @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..f1d733c 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,23 @@ 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 ace.actually.pirates.compat.MusketModCompat; 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 +25,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 +41,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 +74,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 +99,29 @@ public void readCustomDataFromNbt(NbtCompound nbt) { this.blockToDisable = new BlockPos(x, y, z); } } + // for musket mod with reloading and firing + 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 (MusketModCompat.isHoldingGun(this)) { + if (this.isAttacking()) + return State.CROSSBOW_HOLD; + return (MusketModCompat.isHoldingPistol(this) ? 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..6b4a45e 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,7 +4,9 @@ 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 ace.actually.pirates.compat.MusketModCompat; import net.minecraft.entity.*; import net.minecraft.entity.ai.RangedAttackMob; import net.minecraft.entity.ai.goal.*; @@ -16,6 +18,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 +41,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, MusketModCompat.createRangedGoal(this)); 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 +52,21 @@ protected void initGoals() { @Override protected void initEquipment(Random random, LocalDifficulty localDifficulty) { super.initEquipment(random, localDifficulty); - this.equipStack(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + MusketModCompat.equipRandomGunOrBow(this, random); } @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..819af7f 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,7 +3,9 @@ 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.compat.MusketModCompat; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.*; @@ -53,28 +55,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, MusketModCompat.createRangedGoal(this)); } @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)); + MusketModCompat.equipRandomGunOrBow(this, random); } @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..313dd1d 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; } @@ -43,7 +57,7 @@ public ShotEntity(World world) public void tick () { if (this.tickAge > 500) { if (!this.getWorld().isClient()) { - explode(); + explode(null); } } else { this.tickAge++; @@ -59,7 +73,10 @@ public void tick () { protected void onCollision(HitResult hitResult) { super.onCollision(hitResult); if (!this.getWorld().isClient) { - explode(); + BlockPos impactPos = hitResult instanceof BlockHitResult blockHitResult + ? blockHitResult.getBlockPos() + : null; + explode(impactPos); } } @@ -68,11 +85,128 @@ protected void onEntityHit(EntityHitResult entityHitResult) { super.onEntityHit(entityHitResult); Entity entity = entityHitResult.getEntity(); entity.damage(this.getDamageSources().explosion(null), damage); - if (!this.getWorld().isClient) { - 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); @@ -83,8 +217,17 @@ 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); + private void explode(BlockPos exactImpactPos) { + World world = this.getWorld(); + BlockPos impactPos = exactImpactPos != null ? exactImpactPos : this.getBlockPos(); + BlockState state = world.getBlockState(impactPos); + + // Destroy only the exact block reported by the collision result. + if (!state.isAir() && state.getHardness(world, impactPos) >= 0) { + world.breakBlock(impactPos, true); + } + + applyExplosionEffects(world, impactPos); this.discard(); } 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/java/ace/actually/pirates/util/ConfigUtils.java b/src/main/java/ace/actually/pirates/util/ConfigUtils.java index 46c29b3..87b26d5 100644 --- a/src/main/java/ace/actually/pirates/util/ConfigUtils.java +++ b/src/main/java/ace/actually/pirates/util/ConfigUtils.java @@ -66,8 +66,8 @@ private static List makeDefaults() defaults.add("#The minimum rate the pirates will fire their cannons at"); defaults.add("cannon-firing-pause=40"); defaults.add(""); - defaults.add("#The speed the cannonballs will fire at, default=1.7"); - defaults.add("cannon-range=1.7"); + defaults.add("#The speed the cannonballs will fire at, default=3.2 (about 80 blocks on level ground)"); + defaults.add("cannon-range=3.2"); defaults.add(""); defaults.add("#The max amount of blocks for the new ship builder, set to -1 to use the Eureka/VS version"); defaults.add("max-ship-blocks=-1"); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9d5bba6..cd7b423 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", @@ -36,6 +36,7 @@ "vlib": "*" }, "suggests": { + "musketmod": "*", "vs_sails": ">=0.1.7", "eureka": "*" }