From 653171cbb24e437b039ff7959c66bcd30d147765 Mon Sep 17 00:00:00 2001 From: bruno Date: Mon, 29 Jul 2024 23:32:42 +0200 Subject: [PATCH] Skeleton go pew pew Also zombie go boom --- build.gradle | 4 + .../systems/brn/plasticgun/PlasticGun.java | 60 +++--- .../brn/plasticgun/bullets/BulletEntity.java | 8 +- .../plasticgun/grenades/GrenadeEntity.java | 2 +- .../brn/plasticgun/grenades/GrenadeItem.java | 15 +- .../java/systems/brn/plasticgun/guns/Gun.java | 114 +++++++---- .../brn/plasticgun/guns/WeaponShootGoal.java | 181 ++++++++++++++++++ .../brn/plasticgun/lib/EventHandler.java | 111 +++++------ .../brn/plasticgun/lib/GunComponents.java | 2 + .../java/systems/brn/plasticgun/lib/Util.java | 47 ++++- .../AbstractSkeletonEntityGunMixin.java | 107 +++++++++++ .../plasticgun/mixins/ZombieGrenadeMixin.java | 83 ++++++++ .../plasticgun/shurikens/ShurikenEntity.java | 2 +- .../throwables/ThrowableProjectile.java | 2 +- src/main/resources/fabric.mod.json | 4 + src/main/resources/plasticgun.accesswidener | 5 + src/main/resources/plasticgun.mixins.json | 15 ++ 17 files changed, 623 insertions(+), 139 deletions(-) create mode 100644 src/main/java/systems/brn/plasticgun/guns/WeaponShootGoal.java create mode 100644 src/main/java/systems/brn/plasticgun/mixins/AbstractSkeletonEntityGunMixin.java create mode 100644 src/main/java/systems/brn/plasticgun/mixins/ZombieGrenadeMixin.java create mode 100644 src/main/resources/plasticgun.accesswidener create mode 100644 src/main/resources/plasticgun.mixins.json diff --git a/build.gradle b/build.gradle index 32b58cf..c9259b9 100644 --- a/build.gradle +++ b/build.gradle @@ -109,3 +109,7 @@ publishing { // retrieving dependencies. } } + +loom { + accessWidenerPath = file("src/main/resources/plasticgun.accesswidener") +} diff --git a/src/main/java/systems/brn/plasticgun/PlasticGun.java b/src/main/java/systems/brn/plasticgun/PlasticGun.java index bc68df5..12a9a1b 100644 --- a/src/main/java/systems/brn/plasticgun/PlasticGun.java +++ b/src/main/java/systems/brn/plasticgun/PlasticGun.java @@ -9,6 +9,7 @@ import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; import net.minecraft.entity.EntityType; import net.minecraft.entity.SpawnGroup; +import net.minecraft.item.Item; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import org.slf4j.Logger; @@ -27,7 +28,9 @@ import systems.brn.plasticgun.shurikens.ShurikenItem; import systems.brn.plasticgun.testing.DamageTester; import java.util.ArrayList; +import java.util.Map; +import static systems.brn.plasticgun.lib.Util.generateItemMap; import static systems.brn.plasticgun.lib.Util.id; public class PlasticGun implements ModInitializer { @@ -44,6 +47,11 @@ public class PlasticGun implements ModInitializer { public static final ArrayList craftingItems = new ArrayList<>(); + public static Map itemGunMap; + public static Map itemBulletItemMap; + public static Map itemGrenadeItemMap; + public static Map itemShurikenItemMap; + public static EntityType BULLET_ENTITY_TYPE; public static EntityType GRENADE_ENTITY_TYPE; @@ -80,32 +88,32 @@ public class PlasticGun implements ModInitializer { bullets.add(new BulletItem("force_container", 99, 0, 888, false, 0, 1)); // Guns - guns.add(new Gun("357_revolver", 1, 8, 5, 6, 45, 357, 14, 0, 0, 2, 4, 0.2f, 0.5f, -1, 1)); - guns.add(new Gun("colt_1903", 0.3, 10, 5, 8, 38, 32, 5, 0, 0, 1, 3, 0.1f, 0.3f, -1, 1)); - guns.add(new Gun("colt_45", 0.4, 9, 5, 7, 48, 45, 5, 0, 0, 1.5f, 2, 0.15f, 0.4f, -1, 1)); - guns.add(new Gun("colt_peacemaker", 0.6, 8, 5, 6, 43, 45, 5, 0, 0, 0.9f, 2, 0.2f, 0.5f, -1, 1)); - guns.add(new Gun("p2022", 0.2, 12, 5, 10, 41, 9, 5, 0, 0, 1f, 4, 0.1f, 0.25f, -1, 1)); - guns.add(new Gun("snub_nosed_revolver", 0.4, 7, 3, 5, 36, 38, 14, 0, 0, 1f, 2, 0.2f, 0.45f, -1, 1)); - guns.add(new Gun("tokarev_tt_33", 0.7, 10, 5, 8, 45, 762, 5, 0, 0, 1.5f, 2.5f, 0.25f, 0.5f, -1, 1)); - guns.add(new Gun("ak_47", 0.2, 4, 5, 30, 45, 762, 0, 0, 0, 1f, 2, 0.2f, 0.4f, -1, 1)); - guns.add(new Gun("awp", 1, 4, 20, 1, 75, 762, 20, 0, 0, 2f, 8, 0.3f, 0.6f, -1, 1)); - - guns.add(new Gun("rpg9", 2, 4, 20, 1, 10, 999, 8, 20, 0, 3f, 0.5f, 1, 2, -1, 1)); - guns.add(new Gun("forcegun", 0, 2, 5, 20, 10, 888, 0, 0, 20, 0f, 0f, 5f, 10f, 0, 0)); + guns.add(new Gun("forcegun", 0, 2, 5, 20, 10, 888, 0, 0, 20, 0f, 0f, 5f, 10f, 0, 0)); // 0 + guns.add(new Gun("p2022", 0.2, 12, 5, 10, 41, 9, 5, 0, 0, 1f, 4, 0.1f, 0.25f, -1, 1)); // 1.8 + guns.add(new Gun("colt_1903", 0.3, 10, 5, 8, 38, 32, 5, 0, 0, 1, 3, 0.1f, 0.3f, -1, 1)); // 3 + guns.add(new Gun("ak_47", 0.2, 4, 5, 30, 45, 762, 0, 0, 0, 1f, 2, 0.2f, 0.4f, -1, 1)); // 9 + guns.add(new Gun("colt_45", 0.4, 9, 5, 7, 48, 45, 5, 0, 0, 1.5f, 2, 0.15f, 0.4f, -1, 1)); // 3.6 + guns.add(new Gun("snub_nosed_revolver", 0.4, 7, 3, 5, 36, 38, 14, 0, 0, 1f, 2, 0.2f, 0.45f, -1, 1)); // 2.8 + guns.add(new Gun("colt_peacemaker", 0.6, 8, 5, 6, 43, 45, 5, 0, 0, 0.9f, 2, 0.2f, 0.5f, -1, 1)); // 4.8 + guns.add(new Gun("tokarev_tt_33", 0.7, 10, 5, 8, 45, 762, 5, 0, 0, 1.5f, 2.5f, 0.25f, 0.5f, -1, 1)); // 7 + guns.add(new Gun("357_revolver", 1, 8, 5, 6, 45, 357, 14, 0, 0, 2, 4, 0.2f, 0.5f, -1, 1)); // 8 + guns.add(new Gun("awp", 1, 4, 20, 1, 75, 762, 20, 0, 0, 2f, 8, 0.3f, 0.6f, -1, 1)); // 4 + guns.add(new Gun("rpg9", 2, 4, 20, 1, 10, 999, 8, 20, 0, 3f, 0.5f, 1, 2, -1, 1)); // 8 - grenades.add(new GrenadeItem("grenade_an_m14", 1, 5f, 0.5f, 40, true, false, 0, 0, 0, 8, 0)); // AN-M14 Incendiary Grenade - grenades.add(new GrenadeItem("grenade_m34", 1, 10f, 0.5f, 60, true, true, 0, 0, 0, 10, 0)); // M34 White Phosphorus Incendiary Fragmentation Grenade - grenades.add(new GrenadeItem("grenade_m18", 1, 0.1f, 0.2f, 50, false, false, 0, 0, 100, 15, 30)); // M18 Smoke Grenade - grenades.add(new GrenadeItem("grenade_m84", 1, 0.5f, 0.2f, 120, false, false, 10, 10, 5, 12, 10)); // M84 Stun Grenade (Flashbang) - grenades.add(new GrenadeItem("grenade_rgd_5", 1, 6.5f, 0.5f, 60, false, true, 0, 0, 0, 10, 0)); // RGD-5 Fragmentation Grenade - grenades.add(new GrenadeItem("grenade_thermite", 1, 4f, 0.3f, 80, true, false, 0, 0, 0, 8, 15)); // Thermite Grenade - grenades.add(new GrenadeItem("grenade_f1", 1, 7f, 0.5f, 60, false, true, 0, 0, 0, 10, 0)); // F1 Soviet Fragmentation Grenade - grenades.add(new GrenadeItem("grenade_mk3a2", 1, 6f, 0.4f, 60, false, false, 0, 0, 0, 10, 0)); // Mk3A2 Offensive Grenade - grenades.add(new GrenadeItem("grenade_m7a3", 1, 0.1f, 0.2f, 90, false, false, 0, 0, 50, 8, 40)); // M7A3 CS Gas Grenade - grenades.add(new GrenadeItem("grenade_no_69", 1, 5.5f, 0.4f, 60, false, false, 0, 0, 0, 10, 0)); // No. 69 British Offensive Grenade - grenades.add(new GrenadeItem("grenade_rgo", 1, 6.5f, 0.5f, 90, false, true, 0, 0, 0, 10, 0)); // RGO Fragmentation Grenade - grenades.add(new GrenadeItem("grenade_k417", 1, 7f, 0.5f, 70, false, true, 0, 0, 0, 10, 0)); // K417 Fragmentation Grenade + grenades.add(new GrenadeItem("grenade_m18", 1, 0.1f, 0.2f, 50, false, false, 0, 0, 100, 15, 30)); // 0.02 + grenades.add(new GrenadeItem("grenade_m7a3", 1, 0.1f, 0.2f, 90, false, false, 80, 40, 50, 8, 40)); // 0.02 + grenades.add(new GrenadeItem("grenade_m84", 1, 0.5f, 0.2f, 120, false, false, 160, 160, 5, 12, 10)); // 0.1 + grenades.add(new GrenadeItem("grenade_no_69", 1, 5.5f, 0.4f, 60, false, false, 0, 0, 0, 10, 0)); // 2.2 + grenades.add(new GrenadeItem("grenade_an_m14", 1, 5f, 0.5f, 40, true, false, 0, 0, 0, 8, 0)); // 2.5 + grenades.add(new GrenadeItem("grenade_thermite", 1, 4f, 0.3f, 80, true, false, 0, 0, 0, 8, 15)); // 1.2 + grenades.add(new GrenadeItem("grenade_mk3a2", 1, 6f, 0.4f, 60, false, false, 0, 0, 0, 10, 0)); // 2.4 + grenades.add(new GrenadeItem("grenade_m34", 1, 10f, 0.5f, 60, true, true, 0, 0, 0, 10, 0)); // 5 + grenades.add(new GrenadeItem("grenade_f1", 1, 7f, 0.5f, 60, false, true, 0, 0, 0, 10, 0)); // 3.5 + grenades.add(new GrenadeItem("grenade_rgd_5", 1, 6.5f, 0.5f, 60, false, true, 0, 0, 0, 10, 0)); // 3.25 + grenades.add(new GrenadeItem("grenade_rgo", 1, 6.5f, 0.5f, 90, false, true, 0, 0, 0, 10, 0)); // 3.25 + grenades.add(new GrenadeItem("grenade_k417", 1, 7f, 0.5f, 70, false, true, 0, 0, 0, 10, 0)); // 3.5 + weaponArmors.add(new WeaponArmor("kevlar_vest", 200, 0.8, 0.8, 0.4, 0.3)); weaponArmors.add(new WeaponArmor("flak_vest", 500, 0.8, 0.4, 0.8, 0.2)); @@ -142,6 +150,10 @@ public class PlasticGun implements ModInitializer { craftingItems.add(new CraftingItem("titanium_alloy")); craftingItems.add(new CraftingItem("trigger_mechanism")); + itemGunMap = generateItemMap(guns); + itemBulletItemMap = generateItemMap(bullets); + itemGrenadeItemMap = generateItemMap(grenades); + itemShurikenItemMap = generateItemMap(shurikens); GRENADE_ENTITY_TYPE = Registry.register( Registries.ENTITY_TYPE, diff --git a/src/main/java/systems/brn/plasticgun/bullets/BulletEntity.java b/src/main/java/systems/brn/plasticgun/bullets/BulletEntity.java index 613ad26..7e84da5 100644 --- a/src/main/java/systems/brn/plasticgun/bullets/BulletEntity.java +++ b/src/main/java/systems/brn/plasticgun/bullets/BulletEntity.java @@ -33,10 +33,10 @@ public class BulletEntity extends PersistentProjectileEntity implements PolymerE private ItemStack itemStack = Items.ARROW.getDefaultStack(); private final float scale; - public BulletEntity(ServerPlayerEntity player, ItemStack stack, Hand hand, Gun gun, float scale, double damage, float speed, double explosionPower, double repulsionPower, boolean isIncendiary) { - super(BULLET_ENTITY_TYPE, player.getPos().x, player.getPos().y + 1.5d, player.getPos().z, player.getServerWorld(), stack, player.getStackInHand(hand)); - this.setOwner(player); - this.setVelocity(player, player.getPitch(), player.getYaw(), 0.0F, speed, 0); + public BulletEntity(LivingEntity livingEntity, ItemStack stack, Hand hand, Gun gun, float scale, double damage, float speed, double explosionPower, double repulsionPower, boolean isIncendiary) { + super(BULLET_ENTITY_TYPE, livingEntity.getPos().x, livingEntity.getPos().y + 1.75d, livingEntity.getPos().z, livingEntity.getEntityWorld(), stack, livingEntity.getStackInHand(hand)); + this.setOwner(livingEntity); + this.setVelocity(livingEntity, livingEntity.getPitch(), livingEntity.getYaw(), 0.0F, speed, 0); this.pickupType = PickupPermission.DISALLOWED; this.setDamage(damage); this.setSilent(true); diff --git a/src/main/java/systems/brn/plasticgun/grenades/GrenadeEntity.java b/src/main/java/systems/brn/plasticgun/grenades/GrenadeEntity.java index 9700e73..e6f0be1 100644 --- a/src/main/java/systems/brn/plasticgun/grenades/GrenadeEntity.java +++ b/src/main/java/systems/brn/plasticgun/grenades/GrenadeEntity.java @@ -128,7 +128,7 @@ public class GrenadeEntity extends ThrowableProjectile implements PolymerEntity if (flashBangDuration > 0) { for (Entity entity : nearbyEntities) { if (entity instanceof LivingEntity livingEntity) { - livingEntity.addStatusEffect(new StatusEffectInstance(StatusEffects.BLINDNESS, stunDuration, 255, true, false)); + livingEntity.addStatusEffect(new StatusEffectInstance(StatusEffects.BLINDNESS, flashBangDuration, 255, true, false)); } } flashBangDuration = 0; diff --git a/src/main/java/systems/brn/plasticgun/grenades/GrenadeItem.java b/src/main/java/systems/brn/plasticgun/grenades/GrenadeItem.java index 597e5e0..844c573 100644 --- a/src/main/java/systems/brn/plasticgun/grenades/GrenadeItem.java +++ b/src/main/java/systems/brn/plasticgun/grenades/GrenadeItem.java @@ -3,6 +3,7 @@ package systems.brn.plasticgun.grenades; import eu.pb4.polymer.core.api.item.PolymerItem; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.LoreComponent; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.*; import net.minecraft.registry.Registries; @@ -57,7 +58,7 @@ public class GrenadeItem extends SimpleItem implements PolymerItem { Text.translatable("gun.description.particle_count", smokeCount) ))) .maxDamage(explosionTarget + 1) - , id(path), Items.FLOWER_POT + , id(path), Items.STICK ); this.explosionTarget = explosionTarget; this.isIncendiary = isIncendiary; @@ -103,19 +104,25 @@ public class GrenadeItem extends SimpleItem implements PolymerItem { if (!player.isCreative()) { stack.decrement(1); } - if (!stack.isEmpty()) { + if (!stack.isEmpty()) { stack.setDamage(0); } } } } - private void turnIntoEntity(ServerPlayerEntity player, @Nullable ItemStack stack, int speed, int timer) { - GrenadeEntity grenadeEntity = new GrenadeEntity(player, stack, timer, 1f, speed, explosionPower, repulsionPower, isIncendiary, isFragmentation, flashBangDuration, stunDuration, smokeTicks, 8, smokeCount); + public void turnIntoEntity(ServerPlayerEntity player, @Nullable ItemStack stack, int speed, int timer) { + GrenadeEntity grenadeEntity = new GrenadeEntity(player, stack, timer, 0.5f, speed, explosionPower, repulsionPower, isIncendiary, isFragmentation, flashBangDuration, stunDuration, smokeTicks, 8, smokeCount); player.getServerWorld().spawnEntity(grenadeEntity); player.getServerWorld().playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.UI_BUTTON_CLICK.value(), SoundCategory.PLAYERS, 1.0f, 2.0f); } + public void turnIntoEntity(Entity entity, @Nullable ItemStack stack, int speed, int timer) { + GrenadeEntity grenadeEntity = new GrenadeEntity(entity.getEntityWorld(), entity.getPos(), stack, timer, 1f, explosionPower, repulsionPower, isIncendiary, isFragmentation, flashBangDuration, stunDuration, smokeTicks, 8, smokeCount); + entity.getEntityWorld().spawnEntity(grenadeEntity); + entity.getEntityWorld().playSound(null, entity.getX(), entity.getY(), entity.getZ(), SoundEvents.UI_BUTTON_CLICK.value(), SoundCategory.PLAYERS, 1.0f, 2.0f); + } + public void checkExplosions(ServerWorld world, PlayerEntity playerEntity, ItemStack stackInSlot) { if (playerEntity instanceof ServerPlayerEntity player && !world.isClient()) { int timer = stackInSlot.getOrDefault(GRENADE_TIMER_COMPONENT, -1); diff --git a/src/main/java/systems/brn/plasticgun/guns/Gun.java b/src/main/java/systems/brn/plasticgun/guns/Gun.java index 79d2164..31c703a 100644 --- a/src/main/java/systems/brn/plasticgun/guns/Gun.java +++ b/src/main/java/systems/brn/plasticgun/guns/Gun.java @@ -3,6 +3,9 @@ package systems.brn.plasticgun.guns; import eu.pb4.polymer.core.api.item.PolymerItem; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.LoreComponent; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.*; import net.minecraft.network.packet.s2c.play.PositionFlag; @@ -16,7 +19,9 @@ import net.minecraft.text.Text; import net.minecraft.util.Hand; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.random.Random; +import net.minecraft.world.LocalDifficulty; import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; import systems.brn.plasticgun.bullets.BulletEntity; import systems.brn.plasticgun.bullets.BulletItem; import systems.brn.plasticgun.lib.SimpleItem; @@ -25,6 +30,7 @@ import java.util.ArrayList; import java.util.List; import static systems.brn.plasticgun.PlasticGun.bullets; +import static systems.brn.plasticgun.PlasticGun.itemBulletItemMap; import static systems.brn.plasticgun.lib.GunComponents.*; import static systems.brn.plasticgun.lib.Util.*; @@ -112,6 +118,7 @@ public class Gun extends SimpleItem implements PolymerItem { stack.remove(GUN_AMMO_COMPONENT); } else { stack.set(GUN_AMMO_COMPONENT, chamber); + stack.set(GUN_LAST_LOADED_AMMO, bulletStack); world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.PLAYERS, 0.5f, 1.0f); } stack.set(GUN_LOADING_COMPONENT, 1); @@ -126,6 +133,7 @@ public class Gun extends SimpleItem implements PolymerItem { stack.remove(GUN_AMMO_COMPONENT); } else { stack.set(GUN_AMMO_COMPONENT, chamber); + stack.set(GUN_LAST_LOADED_AMMO, bulletStack); world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.PLAYERS, 1f, 2.5f); } bulletStack.decrement(targetCount); @@ -136,6 +144,7 @@ public class Gun extends SimpleItem implements PolymerItem { } if (player.isCreative()) { stack.set(GUN_AMMO_COMPONENT, new ItemStack(ammo.getFirst(), clipSize)); // Ensure ammo.get(0) is a valid item + stack.set(GUN_LAST_LOADED_AMMO, bulletStack); } updateDamage(stack); } else { @@ -144,6 +153,36 @@ public class Gun extends SimpleItem implements PolymerItem { } } + public void reload(World world, MobEntity mobEntity, Hand hand, Random random, LocalDifficulty localDifficulty) { + if (!world.isClient()) { + ItemStack stack = mobEntity.getStackInHand(hand); + int currentReloadCooldown = stack.getOrDefault(GUN_RELOAD_COOLDOWN_COMPONENT, 0); + ItemStack chamber = stack.getOrDefault(GUN_AMMO_COMPONENT, ItemStack.EMPTY); + int bulletsInChamber = chamber.getCount(); + int currentReload = stack.getOrDefault(GUN_LOADING_COMPONENT, 1); + if (currentReload == 1 && bulletsInChamber > 0) { + return; + } + if (currentReloadCooldown == 0) { + stack.set(GUN_RELOAD_COOLDOWN_COMPONENT, reloadTarget); + ItemStack oldChamber = stack.getOrDefault(GUN_LAST_LOADED_AMMO, ItemStack.EMPTY); + Item ammoItem = (oldChamber == null || oldChamber.isEmpty()) + ? ammo.get(selectWeaponIndex(random, localDifficulty, ammo.size())) + : oldChamber.getItem(); + ItemStack bulletStack = new ItemStack(ammoItem, world.getRandom().nextBetween(1, ammoItem.getMaxCount())); + if (!bulletStack.isEmpty()) { // we have ammo + if (currentReload < reloadCount) { // still reloading + stack.set(GUN_LOADING_COMPONENT, currentReload + 1); + } else if (currentReload == reloadCount) { // now reload + stack.set(GUN_AMMO_COMPONENT, bulletStack); + stack.set(GUN_LAST_LOADED_AMMO, bulletStack); + stack.set(GUN_LOADING_COMPONENT, 1); + } + } + } + } + } + public void updateDamage(ItemStack stack) { ItemStack chamber = stack.getOrDefault(GUN_AMMO_COMPONENT, ItemStack.EMPTY).copy(); BulletItem bulletItem = null; @@ -194,67 +233,70 @@ public class Gun extends SimpleItem implements PolymerItem { stack.set(DataComponentTypes.LORE, newLore); } - public void doRecoil(ServerPlayerEntity player) { - Random rng = player.getServerWorld().getRandom(); - // Get the player's current position and yaw - Vec3d pos = player.getPos(); - float yaw = player.getYaw(); - float newPitch = player.getPitch(); - Vec3d currentLook = player.getRotationVector().multiply(-1); - newPitch -= verticalRecoilMin + rng.nextFloat() * (verticalRecoilMax - verticalRecoilMin); - yaw -= (float) (horizontalRecoilMin + rng.nextFloat() * (horizontalRecoilMax - horizontalRecoilMin)); + public void doRecoil(LivingEntity entity) { + if (entity.getEntityWorld() instanceof ServerWorld serverWorld) { + Random rng = entity.getWorld().getRandom(); + // Get the entity's current position and yaw + Vec3d pos = entity.getPos(); + float yaw = entity.getYaw(); + float newPitch = entity.getPitch(); + Vec3d currentLook = entity.getRotationVector().multiply(-1); + newPitch -= verticalRecoilMin + rng.nextFloat() * (verticalRecoilMax - verticalRecoilMin); + yaw -= (float) (horizontalRecoilMin + rng.nextFloat() * (horizontalRecoilMax - horizontalRecoilMin)); - player.teleport(player.getServerWorld(), pos.x, pos.y, pos.z, PositionFlag.ROT, yaw, newPitch); - double velocityRecoil = rng.nextDouble() * (velocityRecoilMax - velocityRecoilMin); - if (velocityRecoil > 0) { - player.setVelocity(currentLook.multiply(velocityRecoil)); + entity.teleport(serverWorld, pos.x, pos.y, pos.z, PositionFlag.ROT, yaw, newPitch); + double velocityRecoil = rng.nextDouble() * (velocityRecoilMax - velocityRecoilMin); + if (velocityRecoil > 0) { + entity.setVelocity(currentLook.multiply(velocityRecoil)); + } } } - public void shoot(ServerWorld world, PlayerEntity user, Hand hand) { - if (user instanceof ServerPlayerEntity player && !world.isClient()) { + public void shoot(ServerWorld world, LivingEntity user, Hand hand) { + if (!world.isClient()) { ItemStack stack = user.getStackInHand(hand); int currentReload = stack.getOrDefault(GUN_LOADING_COMPONENT, 1); int currentCooldown = stack.getOrDefault(GUN_COOLDOWN_COMPONENT, 0); ItemStack chamber = stack.getOrDefault(GUN_AMMO_COMPONENT, ItemStack.EMPTY).copy(); BulletItem bullet = null; - for (BulletItem bulletTemp : bullets) { - if (bulletTemp == chamber.getItem()) { - bullet = bulletTemp; - break; - } + if (itemBulletItemMap.containsKey(chamber.getItem())) { + bullet = itemBulletItemMap.get(chamber.getItem()); } if (!chamber.isEmpty() && currentReload == 1 && currentCooldown == 0) { - boolean isIncendiary = false; - double explosionPower = explosionPowerGun; - double repulsionPower = repulsionPowerGun; - - if (bullet != null) { - isIncendiary = bullet.isIncendiary; - explosionPower *= bullet.explosionPowerCoefficient; - repulsionPower *= bullet.repulsionPowerCoefficient; - } - - BulletEntity bulletEntity = new BulletEntity(player, chamber, hand, this, 1f, damage, speed, explosionPower, repulsionPower, isIncendiary); + BulletEntity bulletEntity = getBulletEntity(user, hand, bullet, chamber); world.spawnEntity(bulletEntity); - world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.ENTITY_GENERIC_EXPLODE.value(), SoundCategory.PLAYERS, 0.1f, 1.2f); + world.playSound(null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENTITY_GENERIC_EXPLODE.value(), SoundCategory.PLAYERS, 0.1f, 1.2f); chamber.decrement(1); stack.set(GUN_COOLDOWN_COMPONENT, cooldownTarget); - doRecoil(player); + doRecoil(user); if (chamber.isEmpty()) { stack.remove(GUN_AMMO_COMPONENT); } else { stack.set(GUN_AMMO_COMPONENT, chamber); } } else if (cooldownTarget > 0) { - world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS, 1.0f, 2.0f); + world.playSound(null, user.getX(), user.getY(), user.getZ(), SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS, 1.0f, 2.0f); } else if (currentReload > 1) { - world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.UI_BUTTON_CLICK.value(), SoundCategory.PLAYERS, 1.0f, 2.0f); + world.playSound(null, user.getX(), user.getY(), user.getZ(), SoundEvents.UI_BUTTON_CLICK.value(), SoundCategory.PLAYERS, 1.0f, 2.0f); } updateDamage(stack); } } -} + + private @NotNull BulletEntity getBulletEntity(LivingEntity entity, Hand hand, BulletItem bullet, ItemStack chamber) { + boolean isIncendiary = false; + double explosionPower = explosionPowerGun; + double repulsionPower = repulsionPowerGun; + + if (bullet != null) { + isIncendiary = bullet.isIncendiary; + explosionPower *= bullet.explosionPowerCoefficient; + repulsionPower *= bullet.repulsionPowerCoefficient; + } + + return new BulletEntity(entity, chamber, hand, this, 0.25f, damage, speed, explosionPower, repulsionPower, isIncendiary); + } +} \ No newline at end of file diff --git a/src/main/java/systems/brn/plasticgun/guns/WeaponShootGoal.java b/src/main/java/systems/brn/plasticgun/guns/WeaponShootGoal.java new file mode 100644 index 0000000..fdc7515 --- /dev/null +++ b/src/main/java/systems/brn/plasticgun/guns/WeaponShootGoal.java @@ -0,0 +1,181 @@ +package systems.brn.plasticgun.guns; + +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +import java.util.EnumSet; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.RangedAttackMob; +import net.minecraft.entity.ai.goal.Goal; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.item.BowItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Hand; +import net.minecraft.util.math.MathHelper; + +import static systems.brn.plasticgun.PlasticGun.guns; +import static systems.brn.plasticgun.PlasticGun.itemGunMap; +import static systems.brn.plasticgun.lib.GunComponents.*; + +public class WeaponShootGoal extends Goal { + private final T actor; + private final double speed; + private final float squaredRange; + private int targetSeeingTicker; + private boolean movingToLeft; + private boolean backward; + private int combatTicks = -1; + private int lockedTicks = 0; + + public WeaponShootGoal(T actor, double speed, float range) { + this.actor = actor; + this.speed = speed; + this.squaredRange = range * range; + this.setControls(EnumSet.of(Control.MOVE, Control.LOOK)); + } + + public boolean canStart() { + return this.actor.getTarget() != null && this.isHoldingGun(); + } + + protected boolean isHoldingGun() { + ItemStack mainHandStack = this.actor.getMainHandStack(); + ItemStack offhandHandStack = this.actor.getOffHandStack(); + return itemGunMap.containsKey(mainHandStack.getItem()) || itemGunMap.containsKey(offhandHandStack.getItem()); + } + + public boolean shouldContinue() { + return (this.canStart() || !this.actor.getNavigation().isIdle()) && this.isHoldingGun(); + } + + public void start() { + super.start(); + this.actor.setAttacking(true); + } + + public void stop() { + super.stop(); + this.actor.setAttacking(false); + this.targetSeeingTicker = 0; + this.actor.clearActiveItem(); + } + + public boolean shouldRunEveryTick() { + return true; + } + + public boolean isLookingAtEntity(Entity targetEntity, float yawThreshold, float pitchThreshold) { + double deltaX = targetEntity.getX() - this.actor.getX(); + double deltaZ = targetEntity.getZ() - this.actor.getZ(); + double deltaY; + + if (targetEntity instanceof LivingEntity livingEntity) { + deltaY = livingEntity.getEyeY() - this.actor.getEyeY(); + } else { + deltaY = (targetEntity.getBoundingBox().minY + targetEntity.getBoundingBox().maxY) / 2.0 - this.actor.getEyeY(); + } + + double distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); + float targetYaw = (float) (MathHelper.atan2(deltaZ, deltaX) * (180 / Math.PI)) - 90.0F; + float targetPitch = (float) (-(MathHelper.atan2(deltaY, distance) * (180 / Math.PI))); + + float currentYaw = this.actor.getYaw(); + float currentPitch = this.actor.getPitch(); + + float yawDifference = MathHelper.wrapDegrees(currentYaw - targetYaw); + float pitchDifference = MathHelper.wrapDegrees(currentPitch - targetPitch); + + return Math.abs(yawDifference) < yawThreshold && Math.abs(pitchDifference) < pitchThreshold; + } + + public void tick() { + LivingEntity livingEntity = this.actor.getTarget(); + if (livingEntity != null) { + double d = this.actor.squaredDistanceTo(livingEntity.getX(), livingEntity.getY(), livingEntity.getZ()); + boolean canSeeTarget = this.actor.getVisibilityCache().canSee(livingEntity); + boolean wasSeeingTarget = this.targetSeeingTicker > 0; + + // Update target seeing ticker based on visibility + if (canSeeTarget != wasSeeingTarget) { + this.targetSeeingTicker = 0; + } + this.targetSeeingTicker = canSeeTarget ? this.targetSeeingTicker + 1 : this.targetSeeingTicker - 1; + + // Manage navigation and combat ticks + if (d <= this.squaredRange && this.targetSeeingTicker >= 20) { + this.actor.getNavigation().stop(); + this.combatTicks++; + } else { + this.actor.getNavigation().startMovingTo(livingEntity, this.speed); + this.combatTicks = -1; + } + + // Randomize movement directions + if (this.combatTicks >= 20) { + if (this.actor.getRandom().nextFloat() < 0.3) { + this.movingToLeft = !this.movingToLeft; + } + if (this.actor.getRandom().nextFloat() < 0.3) { + this.backward = !this.backward; + } + this.combatTicks = 0; + } + + // Update strafing behavior + if (this.combatTicks > -1) { + if (d > this.squaredRange * 0.75) { + this.backward = false; + } else if (d < this.squaredRange * 0.25) { + this.backward = true; + } + this.actor.getMoveControl().strafeTo(this.backward ? -0.5F : 0.5F, this.movingToLeft ? 0.5F : -0.5F); + } + + // Simplified looking at the target + this.actor.lookAtEntity(livingEntity, 30.0F, 30.0F); + if (isLookingAtEntity(livingEntity, 1, 1)) { + lockedTicks++; + } else { + lockedTicks = 0; + } + + Hand gunHand = this.actor.getActiveHand(); + ItemStack gunStack = this.actor.getStackInHand(gunHand); + if (itemGunMap.containsKey(gunStack.getItem())) { + Gun gun = itemGunMap.get(gunStack.getItem()); + // Handle item usage + gun.reload(this.actor.getEntityWorld(), this.actor, gunHand, this.actor.getRandom(), this.actor.getWorld().getLocalDifficulty(this.actor.getBlockPos())); + if (!canSeeTarget && this.targetSeeingTicker < -60) { + this.actor.clearActiveItem(); + } else if (canSeeTarget) { + this.actor.clearActiveItem(); + int currentReload = gunStack.getOrDefault(GUN_LOADING_COMPONENT, 1); + int currentCooldown = gunStack.getOrDefault(GUN_COOLDOWN_COMPONENT, 1); + ItemStack chamber = gunStack.getOrDefault(GUN_AMMO_COMPONENT, ItemStack.EMPTY).copy(); + if (!chamber.isEmpty() && currentReload == 1 && currentCooldown == 0 && lockedTicks >= 10) { + if (this.actor.getEntityWorld() instanceof ServerWorld serverWorld) { + gun.shoot(serverWorld, this.actor, gunHand); + } + } + } + } else if (this.targetSeeingTicker >= -60) { + ItemStack mainHand = this.actor.getStackInHand(Hand.MAIN_HAND); + ItemStack offHand = this.actor.getStackInHand(Hand.OFF_HAND); + if (itemGunMap.containsKey(mainHand.getItem())) { + this.actor.setCurrentHand(Hand.MAIN_HAND); + } + if (itemGunMap.containsKey(offHand.getItem())) { + this.actor.setCurrentHand(Hand.OFF_HAND); + } + } + } + } +} diff --git a/src/main/java/systems/brn/plasticgun/lib/EventHandler.java b/src/main/java/systems/brn/plasticgun/lib/EventHandler.java index ec03994..79957e7 100644 --- a/src/main/java/systems/brn/plasticgun/lib/EventHandler.java +++ b/src/main/java/systems/brn/plasticgun/lib/EventHandler.java @@ -1,7 +1,9 @@ package systems.brn.plasticgun.lib; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.mob.SkeletonEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.Item; @@ -26,24 +28,14 @@ public class EventHandler { ItemStack stack = playerEntity.getStackInHand(hand); if (!world.isClient) { Item stackInHand = playerEntity.getStackInHand(hand).getItem(); - for (Gun gun : guns) { - if (gun == stackInHand) { - gun.reload(world, playerEntity, hand); - break; - } + if (itemGunMap.containsKey(stackInHand)) { + itemGunMap.get(stackInHand).reload(world, playerEntity, hand); } - for (GrenadeItem grenade : grenades) { - if (grenade != null && grenade == stackInHand) { - grenade.unpin(world, playerEntity, hand); - break; - } + if (itemGrenadeItemMap.containsKey(stackInHand)) { + itemGrenadeItemMap.get(stackInHand).unpin(world, playerEntity, hand); } - - for (ShurikenItem shuriken : shurikens) { - if (shuriken != null && shuriken == stackInHand) { - shuriken.chuck(world, playerEntity, hand); - break; - } + if (itemShurikenItemMap.containsKey(stackInHand)) { + itemShurikenItemMap.get(stackInHand).chuck(world, playerEntity, hand); } } @@ -58,75 +50,64 @@ public class EventHandler { Hand hand = player.getActiveHand(); ItemStack stackInHand = player.getStackInHand(hand); Item itemInHand = stackInHand.getItem(); - for (Gun gun : guns) { - if (gun == itemInHand) { - decrementComponent(GUN_COOLDOWN_COMPONENT, stackInHand); - decrementComponent(GUN_RELOAD_COOLDOWN_COMPONENT, stackInHand); - if (player.handSwinging && player.handSwingTicks == -1) { - gun.shoot(world, player, hand); - } - break; + if (itemGunMap.containsKey(itemInHand)) { + decrementComponent(GUN_COOLDOWN_COMPONENT, stackInHand); + decrementComponent(GUN_RELOAD_COOLDOWN_COMPONENT, stackInHand); + if (player.handSwinging && player.handSwingTicks == -1) { + itemGunMap.get(itemInHand).shoot(world, player, hand); } } - - - for (GrenadeItem grenade : grenades) { - if (grenade != null && grenade == itemInHand) { - if (player.handSwinging && player.handSwingTicks == -1) { - grenade.chuck(world, player, hand); - } - break; - } + if (itemGrenadeItemMap.containsKey(itemInHand)) { + itemGrenadeItemMap.get(itemInHand).chuck(world, player, hand); } - - for (ShurikenItem shuriken : shurikens) { - if (shuriken != null && shuriken == itemInHand) { - if (player.handSwinging && player.handSwingTicks == -1) { - shuriken.chuck(world, player, hand); - } - break; - } + if (itemShurikenItemMap.containsKey(itemInHand)) { + itemShurikenItemMap.get(itemInHand).chuck(world, player, hand); } - PlayerInventory playerInventory = player.getInventory(); for (int i = 1; i < playerInventory.main.size(); i++) { ItemStack stackInSlot = playerInventory.main.get(i); Item itemInSlot = stackInSlot.getItem(); - for (GrenadeItem grenadeItem : grenades) { - if (grenadeItem == itemInSlot) { - decrementComponent(GRENADE_TIMER_COMPONENT, stackInSlot); - GrenadeItem.updateDamage(stackInSlot, grenadeItem); - grenadeItem.checkExplosions(world, player, stackInSlot); - } + if (itemGrenadeItemMap.containsKey(itemInSlot)) { + decrementComponent(GRENADE_TIMER_COMPONENT, stackInSlot); + GrenadeItem grenadeItem = itemGrenadeItemMap.get(itemInHand); + GrenadeItem.updateDamage(stackInSlot, grenadeItem); + grenadeItem.checkExplosions(world, player, stackInSlot); } } } } + for (SkeletonEntity skeletonEntity : world.getEntitiesByType(EntityType.SKELETON, entity -> true)) { + for (ItemStack itemStack : skeletonEntity.getEquippedItems()) { + if (itemGunMap.containsKey(itemStack.getItem())) { + decrementComponent(GUN_COOLDOWN_COMPONENT, itemStack); + decrementComponent(GUN_RELOAD_COOLDOWN_COMPONENT, itemStack); + } + } + } } public static void onEntityLoad(Entity entity, ServerWorld world) { if (entity instanceof ItemEntity itemEntity) { ItemStack entityStack = itemEntity.getStack(); - for (GrenadeItem grenadeItem : grenades) { - if (entityStack.getItem() == grenadeItem) { - Entity owner = itemEntity.getOwner(); - int timer = (1 + grenadeItem.explosionTarget) - entityStack.getDamage(); - if (timer <= grenadeItem.explosionTarget) { - if (owner instanceof ServerPlayerEntity player) { - GrenadeEntity grenadeEntity = new GrenadeEntity(player, entityStack, timer, 1f, 0, grenadeItem.explosionPower, grenadeItem.repulsionPower, grenadeItem.isIncendiary, grenadeItem.isFragmentation, grenadeItem.flashBangDuration, grenadeItem.stunDuration, grenadeItem.smokeTicks, grenadeItem.smokeRadius, grenadeItem.smokeCount); - grenadeEntity.setVelocity(entity.getVelocity()); - world.spawnEntity(grenadeEntity); - world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.UI_BUTTON_CLICK.value(), SoundCategory.PLAYERS, 1.0f, 2.0f); - } else { - GrenadeEntity grenadeEntity = new GrenadeEntity(world, entity.getPos(), entityStack, timer, 1f, grenadeItem.explosionPower, grenadeItem.repulsionPower, grenadeItem.isIncendiary, grenadeItem.isFragmentation, grenadeItem.flashBangDuration, grenadeItem.stunDuration, grenadeItem.smokeTicks, grenadeItem.smokeRadius, grenadeItem.smokeCount); - grenadeEntity.setVelocity(entity.getVelocity()); - world.spawnEntity(grenadeEntity); - } - entity.discard(); + Item item = entityStack.getItem(); + if (itemGrenadeItemMap.containsKey(item)) { + GrenadeItem grenadeItem = itemGrenadeItemMap.get(item); + Entity owner = itemEntity.getOwner(); + int timer = (1 + grenadeItem.explosionTarget) - entityStack.getDamage(); + if (timer <= grenadeItem.explosionTarget) { + if (owner instanceof ServerPlayerEntity player) { + GrenadeEntity grenadeEntity = new GrenadeEntity(player, entityStack, timer, 0.5f, 0, grenadeItem.explosionPower, grenadeItem.repulsionPower, grenadeItem.isIncendiary, grenadeItem.isFragmentation, grenadeItem.flashBangDuration, grenadeItem.stunDuration, grenadeItem.smokeTicks, grenadeItem.smokeRadius, grenadeItem.smokeCount); + grenadeEntity.setVelocity(entity.getVelocity()); + world.spawnEntity(grenadeEntity); + world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.UI_BUTTON_CLICK.value(), SoundCategory.PLAYERS, 1.0f, 2.0f); + } else { + GrenadeEntity grenadeEntity = new GrenadeEntity(world, entity.getPos(), entityStack, timer, 1f, grenadeItem.explosionPower, grenadeItem.repulsionPower, grenadeItem.isIncendiary, grenadeItem.isFragmentation, grenadeItem.flashBangDuration, grenadeItem.stunDuration, grenadeItem.smokeTicks, grenadeItem.smokeRadius, grenadeItem.smokeCount); + grenadeEntity.setVelocity(entity.getVelocity()); + world.spawnEntity(grenadeEntity); } - break; + entity.discard(); } } } diff --git a/src/main/java/systems/brn/plasticgun/lib/GunComponents.java b/src/main/java/systems/brn/plasticgun/lib/GunComponents.java index e40fe78..45e4a55 100644 --- a/src/main/java/systems/brn/plasticgun/lib/GunComponents.java +++ b/src/main/java/systems/brn/plasticgun/lib/GunComponents.java @@ -3,6 +3,7 @@ package systems.brn.plasticgun.lib; import com.mojang.serialization.Codec; import eu.pb4.polymer.core.api.other.PolymerComponent; import net.minecraft.component.ComponentType; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; @@ -15,6 +16,7 @@ public class GunComponents { public static final ComponentType GUN_LOADING_COMPONENT = register("gun_load", builder -> builder.codec(Codec.INT)); public static final ComponentType GUN_COOLDOWN_COMPONENT = register("gun_cooldown", builder -> builder.codec(Codec.INT)); public static final ComponentType GUN_RELOAD_COOLDOWN_COMPONENT = register("gun_reload_cooldown", builder -> builder.codec(Codec.INT)); + public static final ComponentType GUN_LAST_LOADED_AMMO = register("gun_last_load", builder -> builder.codec(ItemStack.CODEC)); public static final ComponentType GRENADE_TIMER_COMPONENT = register("grenade_tuner", builder -> builder.codec(Codec.INT)); private static ComponentType register(String id, UnaryOperator> builderOperator) { diff --git a/src/main/java/systems/brn/plasticgun/lib/Util.java b/src/main/java/systems/brn/plasticgun/lib/Util.java index c9c2fa1..843056c 100644 --- a/src/main/java/systems/brn/plasticgun/lib/Util.java +++ b/src/main/java/systems/brn/plasticgun/lib/Util.java @@ -22,16 +22,18 @@ import net.minecraft.util.Pair; import net.minecraft.util.math.Box; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.Difficulty; +import net.minecraft.world.LocalDifficulty; import net.minecraft.world.World; import net.minecraft.world.explosion.Explosion; import net.minecraft.world.explosion.ExplosionBehavior; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; +import systems.brn.plasticgun.PlasticGun; import systems.brn.plasticgun.defence.WeaponArmor; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import static net.minecraft.world.explosion.Explosion.getExposure; import static systems.brn.plasticgun.PlasticGun.MOD_ID; @@ -220,4 +222,43 @@ public class Util { } return damage; } + + public static Map generateItemMap(List extendedItems) { + Map itemMap = new HashMap<>(); + for (T item : extendedItems) { + itemMap.put(item, item); + } + return itemMap; + } + + public static int getDifficultyAdjustedChance(LocalDifficulty localDifficulty, World world) { + Difficulty worldDifficulty = world.getDifficulty(); + + // Calculate chance modifiers based on difficulties + float localDifficultyFactor = localDifficulty.getLocalDifficulty(); + int worldDifficultyFactor = switch (worldDifficulty) { + case PEACEFUL -> 0; + case EASY -> 1; + case NORMAL -> 2; + case HARD -> 3; + default -> 1; + }; + + // Determine the chance to equip a gun + int baseChance = 20; // Base chance denominator + return (int) (baseChance / (localDifficultyFactor * worldDifficultyFactor)); + } + + + public static int selectWeaponIndex(Random random, LocalDifficulty localDifficulty, int size) { + double difficultyFactor = localDifficulty.getClampedLocalDifficulty(); + double biasFactor = 1.0 - difficultyFactor; // Higher difficulty means lower index less likely + + // Bias towards lower indices + double r = random.nextDouble() * biasFactor; + int index = (int) (r * size * size); + + // Ensure index is within bounds + return Math.min(index, size - 1); + } } diff --git a/src/main/java/systems/brn/plasticgun/mixins/AbstractSkeletonEntityGunMixin.java b/src/main/java/systems/brn/plasticgun/mixins/AbstractSkeletonEntityGunMixin.java new file mode 100644 index 0000000..86c1518 --- /dev/null +++ b/src/main/java/systems/brn/plasticgun/mixins/AbstractSkeletonEntityGunMixin.java @@ -0,0 +1,107 @@ +package systems.brn.plasticgun.mixins; + +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.ai.goal.BowAttackGoal; +import net.minecraft.entity.ai.goal.GoalSelector; +import net.minecraft.entity.ai.goal.MeleeAttackGoal; +import net.minecraft.entity.mob.AbstractSkeletonEntity; +import net.minecraft.entity.projectile.ProjectileUtil; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.RangedWeaponItem; +import net.minecraft.util.Hand; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.Difficulty; +import net.minecraft.world.LocalDifficulty; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import systems.brn.plasticgun.PlasticGun; +import systems.brn.plasticgun.guns.Gun; +import systems.brn.plasticgun.guns.WeaponShootGoal; + +import static systems.brn.plasticgun.PlasticGun.guns; +import static systems.brn.plasticgun.PlasticGun.itemGunMap; +import static systems.brn.plasticgun.lib.Util.getDifficultyAdjustedChance; + +@Mixin(AbstractSkeletonEntity.class) +public class AbstractSkeletonEntityGunMixin { + @Unique + protected boolean isHoldingGun() { + AbstractSkeletonEntity thisObject = (AbstractSkeletonEntity) (Object) this; + ItemStack mainHandStack = thisObject.getMainHandStack(); + ItemStack offhandHandStack = thisObject.getOffHandStack(); + return itemGunMap.containsKey(mainHandStack.getItem()) || itemGunMap.containsKey(offhandHandStack.getItem()); + } + + @Inject(method = "canUseRangedWeapon", at = @At("HEAD"), cancellable = true) + public void canUseRangedWeapon(RangedWeaponItem weapon, CallbackInfoReturnable cir) { + cir.setReturnValue(weapon == Items.BOW || itemGunMap.containsKey(weapon)); + } + + @Inject(method = "updateAttackType", at = @At("HEAD"), cancellable = true) + public void updateAttackType(CallbackInfo ci) { + AbstractSkeletonEntity thisObject = (AbstractSkeletonEntity) (Object) this; + + // Remove existing attack goals + thisObject.goalSelector.remove(thisObject.bowAttackGoal); + thisObject.goalSelector.remove(thisObject.meleeAttackGoal); + + // Add WeaponShootGoal if holding a gun + if (isHoldingGun()) { + thisObject.goalSelector.add(4, new WeaponShootGoal<>(thisObject, 1.0, 15.0F)); + } else { + // Keep existing logic for bows + ItemStack itemStack = thisObject.getStackInHand(ProjectileUtil.getHandPossiblyHolding(thisObject, Items.BOW)); + if (itemStack.isOf(Items.BOW)) { + int attackInterval = thisObject.getWorld().getDifficulty() == Difficulty.HARD ? 20 : 40; + BowAttackGoal bowAttackGoal = new BowAttackGoal<>(thisObject, 1.0, attackInterval, 15.0F); + thisObject.goalSelector.add(4, bowAttackGoal); + } else { + // Default to melee attack if no bow + MeleeAttackGoal meleeAttackGoal = new MeleeAttackGoal(thisObject, 1.2, false) { + @Override + public void stop() { + super.stop(); + thisObject.setAttacking(false); + } + + @Override + public void start() { + super.start(); + thisObject.setAttacking(true); + } + }; + thisObject.goalSelector.add(4, meleeAttackGoal); + } + } + + ci.cancel(); + } + + @Inject(method = "initEquipment", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/AbstractSkeletonEntity;equipStack(Lnet/minecraft/entity/EquipmentSlot;Lnet/minecraft/item/ItemStack;)V"), cancellable = true) + protected void initEquipment(Random random, LocalDifficulty localDifficulty, CallbackInfo ci) { + AbstractSkeletonEntity thisObject = (AbstractSkeletonEntity) (Object) this; + + // Get world difficulty + World world = thisObject.getWorld(); + int difficultyAdjustedChance = getDifficultyAdjustedChance(localDifficulty, world); + + // Equip item based on adjusted chance + if (random.nextInt(difficultyAdjustedChance) == 0) { + thisObject.equipStack(EquipmentSlot.MAINHAND, new ItemStack(guns.get(random.nextInt(guns.size())))); + } else { + thisObject.equipStack(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + } + + ci.cancel(); + } + +} diff --git a/src/main/java/systems/brn/plasticgun/mixins/ZombieGrenadeMixin.java b/src/main/java/systems/brn/plasticgun/mixins/ZombieGrenadeMixin.java new file mode 100644 index 0000000..216ccc0 --- /dev/null +++ b/src/main/java/systems/brn/plasticgun/mixins/ZombieGrenadeMixin.java @@ -0,0 +1,83 @@ +package systems.brn.plasticgun.mixins; + +import net.minecraft.entity.EntityType; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.ZombieEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.Difficulty; +import net.minecraft.world.LocalDifficulty; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import systems.brn.plasticgun.PlasticGun; +import systems.brn.plasticgun.grenades.GrenadeItem; + +import java.util.Arrays; + +import static systems.brn.plasticgun.PlasticGun.grenades; +import static systems.brn.plasticgun.lib.Util.getDifficultyAdjustedChance; +import static systems.brn.plasticgun.lib.Util.selectWeaponIndex; + +@Mixin(ZombieEntity.class) +public abstract class ZombieGrenadeMixin extends MobEntity { + + @Shadow + public abstract void tick(); + + // Constructor required for the Mixin + protected ZombieGrenadeMixin(EntityType entityType, World world) { + super(entityType, world); + } + + @Inject(method = "initEquipment", at = @At("HEAD"), cancellable = true) + protected void initEquipment(Random random, LocalDifficulty localDifficulty, CallbackInfo ci) { + super.initEquipment(random, localDifficulty); + + World world = this.getWorld(); + int difficultyAdjustedChance = getDifficultyAdjustedChance(localDifficulty, world); + + // Equip item based on adjusted chance + if (random.nextInt(difficultyAdjustedChance) == 0) { + int i = random.nextInt(20); + ItemStack stackToEquip; + + if (i < 8) { + stackToEquip = new ItemStack(Items.IRON_SWORD); + } else if (i < 14) { + int grenadeIndex = selectWeaponIndex(random, localDifficulty, grenades.size()); + stackToEquip = new ItemStack(grenades.get(grenadeIndex)); + Arrays.fill(this.handDropChances, 0F); + } else { + stackToEquip = new ItemStack(Items.IRON_SHOVEL); + } + + this.equipStack(EquipmentSlot.MAINHAND, stackToEquip); + } + ci.cancel(); + } + + @Inject(method = "dropEquipment", at = @At("RETURN")) + protected void dropEquipment(ServerWorld world, DamageSource source, boolean causedByPlayer, CallbackInfo ci) { + ItemStack mainHandItem = this.getEquippedStack(EquipmentSlot.MAINHAND); + ItemStack offHandItem = this.getEquippedStack(EquipmentSlot.OFFHAND); + for (GrenadeItem grenadeItem : grenades) { + if (mainHandItem.getItem() == grenadeItem) { + grenadeItem.turnIntoEntity(this, mainHandItem, 0, grenadeItem.explosionTarget); + break; + } + if (offHandItem.getItem() == grenadeItem) { + grenadeItem.turnIntoEntity(this, offHandItem, 0, grenadeItem.explosionTarget); + break; + } + } + } +} diff --git a/src/main/java/systems/brn/plasticgun/shurikens/ShurikenEntity.java b/src/main/java/systems/brn/plasticgun/shurikens/ShurikenEntity.java index 4614ff0..f097a7a 100644 --- a/src/main/java/systems/brn/plasticgun/shurikens/ShurikenEntity.java +++ b/src/main/java/systems/brn/plasticgun/shurikens/ShurikenEntity.java @@ -20,7 +20,7 @@ import static systems.brn.plasticgun.lib.Util.getFinalDamage; public class ShurikenEntity extends ThrowableProjectile implements PolymerEntity { public ShurikenEntity(ServerPlayerEntity player, ItemStack itemStack, float speed, double damage) { - super(SHURIKEN_ENTITY_TYPE, player, itemStack, 1f, speed, damage, PickupPermission.ALLOWED, (byte) 0); + super(SHURIKEN_ENTITY_TYPE, player, itemStack, 0.5f, speed, damage, PickupPermission.ALLOWED, (byte) 0); } public ShurikenEntity(EntityType bulletEntityEntityType, World world) { diff --git a/src/main/java/systems/brn/plasticgun/throwables/ThrowableProjectile.java b/src/main/java/systems/brn/plasticgun/throwables/ThrowableProjectile.java index f6e5685..46827f4 100644 --- a/src/main/java/systems/brn/plasticgun/throwables/ThrowableProjectile.java +++ b/src/main/java/systems/brn/plasticgun/throwables/ThrowableProjectile.java @@ -20,7 +20,7 @@ public class ThrowableProjectile extends PersistentProjectileEntity implements P private final float scale; public ThrowableProjectile(EntityType entityType, World world, Vec3d pos, ItemStack itemStack, float scale, double damage, PickupPermission pickupPermission, byte penetration) { - super(entityType, pos.getX(), pos.getY() + 1.5d, pos.getZ(), world, itemStack, itemStack); + super(entityType, pos.getX(), pos.getY() + 1.5d, pos.getZ(), world, itemStack, null); this.pickupType = pickupPermission; this.setDamage(damage); this.setSilent(true); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 0a3a559..b25fc1c 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -8,6 +8,10 @@ "contact": {}, "license": "MIT", "icon": "assets/plasticgun/icon.png", + "accessWidener" : "plasticgun.accesswidener", + "mixins": [ + "plasticgun.mixins.json" + ], "environment": "*", "entrypoints": { "main": [ diff --git a/src/main/resources/plasticgun.accesswidener b/src/main/resources/plasticgun.accesswidener new file mode 100644 index 0000000..a00957c --- /dev/null +++ b/src/main/resources/plasticgun.accesswidener @@ -0,0 +1,5 @@ +accessWidener v2 named +accessible field net/minecraft/entity/mob/MobEntity goalSelector Lnet/minecraft/entity/ai/goal/GoalSelector; +accessible field net/minecraft/entity/mob/AbstractSkeletonEntity bowAttackGoal Lnet/minecraft/entity/ai/goal/BowAttackGoal; +accessible field net/minecraft/entity/mob/AbstractSkeletonEntity meleeAttackGoal Lnet/minecraft/entity/ai/goal/MeleeAttackGoal; +accessible method net/minecraft/entity/mob/MobEntity initEquipment (Lnet/minecraft/util/math/random/Random;Lnet/minecraft/world/LocalDifficulty;)V \ No newline at end of file diff --git a/src/main/resources/plasticgun.mixins.json b/src/main/resources/plasticgun.mixins.json new file mode 100644 index 0000000..865f5a4 --- /dev/null +++ b/src/main/resources/plasticgun.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "systems.brn.plasticgun.mixins", + "compatibilityLevel": "JAVA_21", + "mixins": [ + "AbstractSkeletonEntityGunMixin", + "ZombieGrenadeMixin" + ], + "client": [], + "server": [], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file