diff --git a/src/main/kotlin/be4rjp/sclat/weapon/ArmorStandManager.kt b/src/main/kotlin/be4rjp/sclat/weapon/ArmorStandManager.kt new file mode 100644 index 0000000..fbaac34 --- /dev/null +++ b/src/main/kotlin/be4rjp/sclat/weapon/ArmorStandManager.kt @@ -0,0 +1,123 @@ +package be4rjp.sclat.weapon + +import be4rjp.sclat.api.GlowingAPI +import be4rjp.sclat.api.player.PlayerData +import be4rjp.sclat.api.team.Team +import be4rjp.sclat.data.DataMgr.setKasaDataWithARmorStand +import be4rjp.sclat.data.KasaData +import be4rjp.sclat.plugin +import net.minecraft.server.v1_14_R1.EnumItemSlot +import net.minecraft.server.v1_14_R1.PacketPlayOutEntityEquipment +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack +import org.bukkit.entity.ArmorStand +import org.bukkit.entity.EntityType +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.util.EulerAngle + +object ArmorStandManager { + val hashPlayer: HashMap = HashMap() + val hashArmorstand: HashMap = HashMap() + + /** + * Spawns and configures a group of 3 armor stands (main + 2 wings) for a funnel. + * All stands are set up as small, invisible, gravity-less "Kasa" entities. + * + * @param player The owning player (used for glowing setup and world context). + * @param data The player's data (main stand is registered here). + * @param kdata The kasa data associated with this group. + * @param mainLoc Spawn location for the main (central) stand. + * @param wingsLoc Spawn location for the two wing stands (defaults to [mainLoc]). + * @param applyGlowing Whether to explicitly disable glowing on the main stand. + */ + fun spawnFunnelGroup( + player: Player, + data: PlayerData, + kdata: KasaData, + mainLoc: Location, + wingsLoc: Location = mainLoc, + applyGlowing: Boolean = false, + ): MutableList { + val main = player.world.spawnEntity(mainLoc.clone(), EntityType.ARMOR_STAND) as ArmorStand + val wing1 = player.world.spawnEntity(wingsLoc.clone(), EntityType.ARMOR_STAND) as ArmorStand + val wing2 = player.world.spawnEntity(wingsLoc.clone(), EntityType.ARMOR_STAND) as ArmorStand + + wing1.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(-40.0)) + wing2.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(40.0)) + + val list = mutableListOf(main, wing1, wing2) + data.setArmorlist(main) + if (applyGlowing) GlowingAPI.setGlowing(main, player, false) + + for (armorStand in list) { + armorStand.isSmall = true + armorStand.setBasePlate(false) + armorStand.isVisible = false + armorStand.setGravity(false) + armorStand.customName = "Kasa" + setKasaDataWithARmorStand(armorStand, kdata) + } + + return list + } + + /** + * Sends equipment packets for a funnel group to all online players. + * list[0] (main) wears the team wool; list[1] and list[2] (wings) wear the team glass pane. + */ + fun sendEquipmentPackets( + list: List, + team: Team, + ) { + val glassPaneMaterial = + Material.getMaterial(team.teamColor!!.glass.toString() + "_PANE")!! + val woolItem = ItemStack(team.teamColor!!.wool!!) + val glassPaneItem = ItemStack(glassPaneMaterial) + + for (onlinePlayer in plugin.server.onlinePlayers) { + (onlinePlayer as CraftPlayer).handle.playerConnection.sendPacket( + PacketPlayOutEntityEquipment( + list[1].entityId, + EnumItemSlot.HEAD, + CraftItemStack.asNMSCopy(glassPaneItem), + ), + ) + onlinePlayer.handle.playerConnection.sendPacket( + PacketPlayOutEntityEquipment( + list[2].entityId, + EnumItemSlot.HEAD, + CraftItemStack.asNMSCopy(glassPaneItem), + ), + ) + onlinePlayer.handle.playerConnection.sendPacket( + PacketPlayOutEntityEquipment( + list[0].entityId, + EnumItemSlot.HEAD, + CraftItemStack.asNMSCopy(woolItem), + ), + ) + } + } + + /** + * Removes a funnel group: cleans up map entries, unregisters the main stand from player data, + * removes all entities, and clears the list. + */ + fun cleanupFunnelGroup( + list: MutableList, + data: PlayerData, + ) { + if (list.isEmpty()) return + val kasaStand = list[0] + hashPlayer.remove(kasaStand) + hashArmorstand.remove(kasaStand) + data.subArmorlist(kasaStand) + for (armorStand in list) { + armorStand.remove() + } + list.clear() + } +} diff --git a/src/main/kotlin/be4rjp/sclat/weapon/Funnel.kt b/src/main/kotlin/be4rjp/sclat/weapon/Funnel.kt index f12f703..1d9c6cd 100644 --- a/src/main/kotlin/be4rjp/sclat/weapon/Funnel.kt +++ b/src/main/kotlin/be4rjp/sclat/weapon/Funnel.kt @@ -1,1431 +1,50 @@ package be4rjp.sclat.weapon import be4rjp.sclat.Sclat -import be4rjp.sclat.api.GlowingAPI -import be4rjp.sclat.api.SclatUtil -import be4rjp.sclat.api.SclatUtil.giveDamage import be4rjp.sclat.api.player.PlayerData -import be4rjp.sclat.api.raytrace.BoundingBox import be4rjp.sclat.api.raytrace.RayTrace -import be4rjp.sclat.data.DataMgr.getKasaDataFromArmorStand import be4rjp.sclat.data.DataMgr.getPlayerData -import be4rjp.sclat.data.DataMgr.getSplashShieldDataFromArmorStand -import be4rjp.sclat.data.DataMgr.setKasaDataWithARmorStand -import be4rjp.sclat.data.DataMgr.setKasaDataWithPlayer -import be4rjp.sclat.data.KasaData -import be4rjp.sclat.manager.ArmorStandMgr import be4rjp.sclat.plugin -import net.minecraft.server.v1_14_R1.EnumItemSlot -import net.minecraft.server.v1_14_R1.PacketPlayOutEntityEquipment -import org.bukkit.GameMode +import be4rjp.sclat.sclatLogger import org.bukkit.Location -import org.bukkit.Material import org.bukkit.Particle import org.bukkit.Sound -import org.bukkit.block.data.BlockData -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack import org.bukkit.entity.ArmorStand -import org.bukkit.entity.Entity -import org.bukkit.entity.EntityType import org.bukkit.entity.Player -import org.bukkit.inventory.ItemStack import org.bukkit.scheduler.BukkitRunnable -import org.bukkit.util.EulerAngle import org.bukkit.util.Vector +import kotlin.math.min object Funnel { - var hashPlayer: HashMap = HashMap() - var hashArmorstand: HashMap = HashMap() var funnelMaxHP: Int = 10 var funnelMaxHP2: Int = 3 var funnelSpeed: Double = 1.0 + /** + * Fires a ray from [funnel] towards [targetLoc] and applies damage to the first target hit. + * Delegates all ray tracing logic to [RayTraceHandler]. + * + * @param player The player who fired the funnel shot. + * @param funnel The armor stand representing the funnel being fired. + * @param targetLoc The location the funnel is being fired towards. + */ fun funnelShot( player: Player, funnel: ArmorStand, - taegetloc: Location, + targetLoc: Location, ) { - val damage = 3.0 - val funloc = funnel.eyeLocation - if (player.gameMode == GameMode.SPECTATOR) return + if (player.gameMode == org.bukkit.GameMode.SPECTATOR) return player.world.playSound(player.location, Sound.ENTITY_FIREWORK_ROCKET_BLAST, 0.4f, 5f) - val rayTrace = - RayTrace( - funloc.toVector(), - Vector( - taegetloc.x - funloc.x, - taegetloc.y - funloc.y, - taegetloc.z - funloc.z, - ).normalize(), - ) - val positions = rayTrace.traverse(4.0, 0.2) - - loop@ for (vector in positions) { - val position = vector.toLocation(player.location.world!!) - val block = player.location.world!!.getBlockAt(position) - - if (block.type != Material.AIR) { - break - } - for (target in plugin.server.onlinePlayers) { - if (getPlayerData(target)!!.settings!!.showEffectMainWeaponInk()) { - if (target.world === position.world) { - if (target.location.distanceSquared(position) < Sclat.particleRenderDistanceSquared) { - val bd = - getPlayerData(player)!! - .team!! - .teamColor!! - .wool!! - .createBlockData() - target.spawnParticle(Particle.BLOCK_DUST, position, 1, 0.0, 0.0, 0.0, 1.0, bd) - } - } - } - } - - val maxDistSquad = 4.0 // 2*2 - for (target in plugin.server.onlinePlayers) { - if (!getPlayerData(target)!!.isInMatch) continue - if (getPlayerData(player)!!.team != getPlayerData(target)!!.team && - target.gameMode == GameMode.ADVENTURE - ) { - if (target.location.distanceSquared(position) <= maxDistSquad) { - if (rayTrace.intersects(BoundingBox(target as Entity), 4.0, 0.05)) { - giveDamage(player, target, damage, "killed") - player.playSound(player.location, Sound.ENTITY_PLAYER_HURT, 1.2f, 1.3f) - - // AntiNoDamageTime - val task: BukkitRunnable = - object : BukkitRunnable() { - var p: Player = target - - override fun run() { - target.noDamageTicks = 0 - } - } - task.runTaskLater(plugin, 1) - break@loop - } - } - } - } - - for (armorStand in player.world.entities) { - if (armorStand is ArmorStand) { - if (armorStand.location.distanceSquared(position) <= maxDistSquad) { - if (rayTrace.intersects(BoundingBox(armorStand as Entity), 4.0, 0.05)) { - if (armorStand.customName != null) { - if (armorStand.customName == "SplashShield") { - val ssdata = getSplashShieldDataFromArmorStand(armorStand) - if (getPlayerData(ssdata!!.player)!!.team != - getPlayerData(player)!! - .team - ) { - ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) - armorStand - .world - .playSound(armorStand.location, Sound.ENTITY_PLAYER_HURT, 0.8f, 1.2f) - break@loop - } - } else if (armorStand.customName == "Kasa") { - val ssdata = getKasaDataFromArmorStand(armorStand) - if (getPlayerData(ssdata!!.player)!!.team != - getPlayerData(player)!! - .team - ) { - ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) - armorStand - .world - .playSound(armorStand.location, Sound.ENTITY_PLAYER_HURT, 0.8f, 1.2f) - break@loop - } - } else { - if (SclatUtil.isNumber(armorStand.customName!!)) { - if (armorStand.customName != "21" && - armorStand.customName != "100" - ) { - if (armorStand.isVisible) { - player.playSound( - player.location, - Sound.ENTITY_ARROW_HIT_PLAYER, - 1.2f, - 1.3f, - ) - } - } - } - ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) - break@loop - } - } - ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) - } - } - } - } - } + RayTraceHandler.performShot(player, funnel, targetLoc) } + /** + * Spawns the three funnel groups for [player] and starts the floating/targeting task. + * Delegates lifecycle management to [FunnelTask]. + */ @JvmStatic fun funnelFloat(player: Player) { - val kdata = KasaData(player) - val kdata1 = KasaData(player) - val kdata2 = KasaData(player) - setKasaDataWithPlayer(player, kdata) - setKasaDataWithPlayer(player, kdata1) - setKasaDataWithPlayer(player, kdata2) - - val task: BukkitRunnable = - object : BukkitRunnable() { - var p: Player = player - - var data: PlayerData? = getPlayerData(p) - var i: Int = 0 - - var list: MutableList = ArrayList() - var list1: MutableList = ArrayList() - var list2: MutableList = ArrayList() - var list5: MutableList> = mutableListOf() - var list6: MutableList = mutableListOf() - - lateinit var as1: ArmorStand - lateinit var as2: ArmorStand - lateinit var as3: ArmorStand - - lateinit var as11: ArmorStand - lateinit var as12: ArmorStand - lateinit var as13: ArmorStand - - lateinit var as21: ArmorStand - lateinit var as22: ArmorStand - lateinit var as23: ArmorStand - lateinit var las: ArmorStand - var check: Boolean = false - var kdataReset: Int = -1 - var kdataReset1: Int = -1 - var kdataReset2: Int = -1 - - override fun run() { - try { - // Location loc = p.getLocation().add(0, -1.7, 0); - val locp = p.location - var pv = p.eyeLocation.direction.normalize() - val vec = Vector(pv.getX(), 0.0, pv.getZ()).normalize() - var vec1: Vector? - var vec2: Vector? - val l1 = Vector(vec.clone().getZ() * -1, 0.0, vec.clone().getX()) - val r1 = Vector(vec.clone().getZ(), 0.0, vec.clone().getX() * -1) - val taskcheck: BukkitRunnable = - object : BukkitRunnable() { - override fun run() { - check = true - } - } - val listremove: BukkitRunnable = - object : BukkitRunnable() { - override fun run() { - try { - hashPlayer.remove(list[0]) - hashArmorstand.remove(list[0]) - data!!.subArmorlist(list[0]) - for (`as` in list) { - `as`.remove() - } - } catch (e: Exception) { - } - list.clear() - as3 = - p.world.spawnEntity( - locp.clone().add(0.0, 2.5, 0.0), - EntityType.ARMOR_STAND, - ) as ArmorStand - as1 = - p.world.spawnEntity( - locp.clone().add(0.0, 2.8, 0.0), - EntityType.ARMOR_STAND, - ) as ArmorStand - as2 = - p.world.spawnEntity( - locp.clone().add(0.0, 2.8, 0.0), - EntityType.ARMOR_STAND, - ) as ArmorStand - as1.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(-40.0)) - as2.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(40.0)) - list.add(as3) - list.add(as1) - list.add(as2) - data!!.setArmorlist(as3) - GlowingAPI.setGlowing(as3, player, false) - for (`as` in list) { - `as`.isSmall = true - `as`.setBasePlate(false) - `as`.isVisible = false - `as`.setGravity(false) - `as`.customName = "Kasa" - setKasaDataWithARmorStand(`as`, kdata) - } - val team = data!!.team - for (o_player in plugin.server.onlinePlayers) { - (o_player as CraftPlayer).handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list[2].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team!!.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list[1].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list[0].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy(ItemStack(team.teamColor!!.wool!!)), - ), - ) - } - list6.add(as3) - kdata.damage = 0.0 - kdata.armorStandList = list - cancel() - } - } - val listremove1: BukkitRunnable = - object : BukkitRunnable() { - override fun run() { - try { - hashPlayer.remove(list1[0]) - hashArmorstand.remove(list1[0]) - data!!.subArmorlist(list1[0]) - for (`as` in list1) { - `as`.remove() - } - } catch (e: Exception) { - } - list1.clear() - as13 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as11 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as12 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as11.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(-40.0)) - as12.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(40.0)) - list1.add(as13) - list1.add(as11) - list1.add(as12) - data!!.setArmorlist(as13) - GlowingAPI.setGlowing(as13, player, false) - for (`as` in list1) { - `as`.isSmall = true - `as`.setBasePlate(false) - `as`.isVisible = false - `as`.setGravity(false) - `as`.customName = "Kasa" - setKasaDataWithARmorStand(`as`, kdata1) - } - val team = data!!.team - for (o_player in plugin.server.onlinePlayers) { - (o_player as CraftPlayer).handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list1[2].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team?.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list1[1].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list1[0].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy(ItemStack(team.teamColor!!.wool!!)), - ), - ) - } - list6.add(as13) - kdata1.damage = 0.0 - kdata1.armorStandList = list1 - cancel() - } - } - val listremove2: BukkitRunnable = - object : BukkitRunnable() { - override fun run() { - try { - hashPlayer.remove(list2[0]) - hashArmorstand.remove(list2[0]) - data!!.subArmorlist(list2[0]) - for (`as` in list2) { - `as`.remove() - } - } catch (e: Exception) { - } - list2.clear() - as23 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as21 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as22 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as21.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(-40.0)) - as22.headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(40.0)) - list2.add(as23) - list2.add(as21) - list2.add(as22) - data!!.setArmorlist(as23) - GlowingAPI.setGlowing(as23, player, false) - for (`as` in list2) { - `as`.isSmall = true - `as`.setBasePlate(false) - `as`.isVisible = false - `as`.setGravity(false) - `as`.customName = "Kasa" - setKasaDataWithARmorStand(`as`, kdata2) - } - val team = data!!.team - for (o_player in plugin.server.onlinePlayers) { - (o_player as CraftPlayer).handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list2[2].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team!!.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list2[1].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - list2[0].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy(ItemStack(team.teamColor!!.wool!!)), - ), - ) - } - list6.add(as23) - kdata2.damage = 0.0 - kdata2.armorStandList = list2 - cancel() - } - } - if (i == 0) { - as3 = - p.world.spawnEntity( - locp.clone().add(0.0, 2.5, 0.0), - EntityType.ARMOR_STAND, - ) as ArmorStand - pv = as3.eyeLocation.direction.normalize() - vec1 = - Vector( - pv.clone().getX() * 0.707 - pv.clone().getZ() * 0.707, - 0.0, - pv.clone().getX() * 0.707 + pv.clone().getZ() * 0.707, - ).normalize() - vec2 = - Vector( - pv.clone().getX() * 0.707 + pv.clone().getZ() * 0.707, - 0.0, - -pv.clone().getX() * 0.707 + pv.clone().getZ() * 0.707, - ).normalize() - as1 = - p.world.spawnEntity( - locp.clone().add(0.0, 2.8, 0.0).add(vec1.clone().multiply(0.3)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as2 = - p.world.spawnEntity( - locp.clone().add(0.0, 2.8, 0.0).add(vec2.clone().multiply(0.3)), - EntityType.ARMOR_STAND, - ) as ArmorStand - list.add(as3) - list.add(as1) - list.add(as2) - as13 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as11 = - p.world.spawnEntity( - locp - .clone() - .add(0.0, 1.3, 0.0) - .add(vec1.clone().multiply(0.3)) - .add(l1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as12 = - p.world.spawnEntity( - locp - .clone() - .add(0.0, 1.3, 0.0) - .add(vec2.clone().multiply(0.3)) - .add(l1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - list1.add(as13) - list1.add(as11) - list1.add(as12) - as23 = - p.world.spawnEntity( - locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as21 = - p.world.spawnEntity( - locp - .clone() - .add(0.0, 1.3, 0.0) - .add(vec1.clone().multiply(0.3)) - .add(r1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - as22 = - p.world.spawnEntity( - locp - .clone() - .add(0.0, 1.3, 0.0) - .add(vec2.clone().multiply(0.3)) - .add(r1.clone().multiply(1.5)), - EntityType.ARMOR_STAND, - ) as ArmorStand - list2.add(as23) - list2.add(as21) - list2.add(as22) - - list5.add(list) - list5.add(list1) - list5.add(list2) - data!!.setArmorlist(as3) - data!!.setArmorlist(as13) - data!!.setArmorlist(as23) - list6.add(as3) - list6.add(as13) - list6.add(as23) - kdata.armorStandList = list - kdata1.armorStandList = list1 - kdata2.armorStandList = list2 - kdata.damage = 0.0 - kdata1.damage = 0.0 - kdata2.damage = 0.0 - for (`as` in list) { - setKasaDataWithARmorStand(`as`, kdata) - } - for (`as` in list1) { - setKasaDataWithARmorStand(`as`, kdata1) - } - for (`as` in list2) { - setKasaDataWithARmorStand(`as`, kdata2) - } - for (aslist in list5) { - aslist[1].headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(-40.0)) - aslist[2].headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(40.0)) - for (`as` in aslist) { - `as`.isSmall = true - `as`.setBasePlate(false) - `as`.isVisible = false - `as`.setGravity(false) - `as`.customName = "Kasa" - } - } - val team = data!!.team - for (o_player in plugin.server.onlinePlayers) { - for (aslist in list5) { - (o_player as CraftPlayer).handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[1].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team!!.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[2].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[0].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy(ItemStack(team.teamColor!!.wool!!)), - ), - ) - } - } - taskcheck.runTaskLater(plugin, 20) - } - if (i >= 0) { - // ファンネル破壊時の復活処理 - if (p.gameMode == GameMode.SPECTATOR) { - if (kdata.damage <= funnelMaxHP) { - kdata.damage = 1024.0 - } - if (kdata1.damage <= funnelMaxHP) { - kdata1.damage = 1024.0 - } - if (kdata2.damage <= funnelMaxHP) { - kdata2.damage = 1024.0 - } - } - if (kdata.damage > funnelMaxHP && kdata.damage < 9999) { - val kasaStand = kdata.armorStandList[0] - data!!.subArmorlist(kasaStand) - if (hashPlayer.containsKey(kasaStand)) { - if (hashPlayer.get(kasaStand)!!.gameMode != GameMode.SPECTATOR) { - kdataReset += 60 - } - hashPlayer.remove(kasaStand) - } else if (hashArmorstand.containsKey(kasaStand)) { - kdataReset += 60 - hashArmorstand.remove(kasaStand) - } else { - list6.remove(kasaStand) - if (kdata.damage == 1024.0) { - listremove.runTaskLater(plugin, 110) - } else { - listremove.runTaskLater(plugin, 160) - } - } - kdata.damage = 10000.0 - for (`as` in kdata.armorStandList) { - `as`.remove() - } - } - if (kdata1.damage > funnelMaxHP && kdata1.damage < 9999) { - val kasaStand1 = kdata1.armorStandList[0] - data!!.subArmorlist(kasaStand1) - if (hashPlayer.containsKey(kasaStand1)) { - if (hashPlayer.get(kasaStand1)!!.gameMode != GameMode.SPECTATOR) { - kdataReset1 += 60 - } - hashPlayer.remove(kasaStand1) - } else if (hashArmorstand.containsKey(kasaStand1)) { - kdataReset1 += 60 - hashArmorstand.remove(kasaStand1) - } else { - list6.remove(kasaStand1) - if (kdata1.damage == 1024.0) { - listremove1.runTaskLater(plugin, 110) - } else { - listremove1.runTaskLater(plugin, 160) - } - } - kdata1.damage = 10000.0 - for (`as` in kdata1.armorStandList) { - `as`.remove() - } - } - if (kdata2.damage > funnelMaxHP && kdata2.damage < 9999) { - val kasaStand2 = kdata2.armorStandList[0] - data!!.subArmorlist(kasaStand2) - if (hashPlayer.containsKey(kasaStand2)) { - if (hashPlayer.get(kasaStand2)!!.gameMode != GameMode.SPECTATOR) { - kdataReset2 += 60 - } - hashPlayer.remove(kasaStand2) - } else if (hashArmorstand.containsKey(kasaStand2)) { - kdataReset2 += 60 - hashArmorstand.remove(kasaStand2) - } else { - list6.remove(kasaStand2) - if (kdata2.damage == 1024.0) { - listremove2.runTaskLater(plugin, 110) - } else { - listremove2.runTaskLater(plugin, 160) - } - } - kdata2.damage = 10000.0 - for (`as` in kdata2.armorStandList) { - `as`.remove() - } - } - if (i == kdataReset) { - listremove.runTaskLater(plugin, 1) - kdataReset = -1 - } - if (i == kdataReset1) { - listremove1.runTaskLater(plugin, 1) - kdataReset1 = -1 - } - if (i == kdataReset2) { - listremove2.runTaskLater(plugin, 1) - kdataReset2 = -1 - } - // ファンネル破壊時の復活処理了 - pv = - Vector( - p - .eyeLocation - .direction - .normalize() - .getX(), - 0.0, - p - .eyeLocation - .direction - .normalize() - .getZ(), - ) - var io = 0 - for (aslist in list5) { - val aslistget0: ArmorStand = aslist[0] - if (io == 0) { - if (!hashPlayer.containsKey(aslistget0) && - !hashArmorstand.containsKey(aslistget0) - ) { - aslistget0.teleport(locp.clone().add(0.0, 2.5, 0.0)) - } else if (hashPlayer.containsKey(aslistget0)) { - val las = aslistget0.location - val lpl = - hashPlayer - .get(aslistget0)!! - .location - .add(pv.clone().multiply(2).add(Vector(0.0, 1.4, 0.0))) - pv = - Vector( - lpl.x - las.x, - lpl.y - las.y, - lpl.z - las.z, - ) - if (i % 48 == 0) { - if (!getPlayerData(hashPlayer.get(aslistget0))!!.isUsingSP) { - funnelShot( - p, - aslistget0, - hashPlayer.get(aslistget0)!!.eyeLocation, - ) - } - } - if (pv.length() > 1) { - if (!aslistget0.hasGravity()) { - aslistget0.setGravity(true) - } - aslistget0.velocity = pv.normalize().multiply(funnelSpeed) - } else { - if (aslistget0.hasGravity()) { - aslistget0.setGravity(false) - } - aslistget0.teleport(lpl) - } - if (( - hashPlayer - .get(aslistget0)!! - .gameMode == GameMode.SPECTATOR || - !getPlayerData( - hashPlayer.get(aslistget0), - )!!.isInMatch || - !hashPlayer.get(aslistget0)!!.isOnline - ) && - kdata.damage < funnelMaxHP - ) { - kdata.damage = (funnelMaxHP + 1).toDouble() - kdataReset = i + 3 - } - } else if (hashArmorstand.containsKey(aslistget0)) { - val las = aslistget0.location - val lpl = - hashArmorstand - .get(aslistget0)!! - .location - .add(pv.clone().multiply(2).add(Vector(0.0, 1.4, 0.0))) - pv = - Vector( - lpl.x - las.x, - lpl.y - las.y, - lpl.z - las.z, - ) - if (i % 48 == 0) { - funnelShot( - p, - aslistget0, - hashArmorstand.get(aslistget0)!!.eyeLocation, - ) - } - if (pv.length() > 1) { - if (!aslistget0.hasGravity()) { - aslistget0.setGravity(true) - } - aslistget0.velocity = pv.normalize().multiply(funnelSpeed) - } else { - if (aslistget0.hasGravity()) { - aslistget0.setGravity(false) - } - aslistget0.teleport(lpl) - } - if (!hashArmorstand.get(aslistget0)!!.isVisible) { - kdataReset = i + 3 - kdata.damage = (funnelMaxHP + 1).toDouble() - } - } - if (i % 20 == 0) { - val team = data!!.team - for (o_player in plugin.server.onlinePlayers) { - (o_player as CraftPlayer).handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[2].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team!!.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[1].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[0].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack(team.teamColor!!.wool!!), - ), - ), - ) - } - } - } - if (io == 1) { - if (!hashPlayer.containsKey(aslistget0) && - !hashArmorstand.containsKey(aslistget0) - ) { - aslistget0.teleport( - locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)), - ) - } else if (hashPlayer.containsKey(aslistget0)) { - val las = aslistget0.location - val lpl = - hashPlayer - .get(aslistget0)!! - .location - .add(l1.clone().multiply(2).add(Vector(0.0, 1.4, 0.0))) - pv = - Vector( - lpl.x - las.x, - lpl.y - las.y, - lpl.z - las.z, - ) - if (i % 48 == 16) { - if (!getPlayerData(hashPlayer.get(aslistget0))!!.isUsingSP) { - funnelShot( - p, - aslistget0, - hashPlayer.get(aslistget0)!!.eyeLocation, - ) - } - } - if (pv.length() > 1) { - if (!aslistget0.hasGravity()) { - aslistget0.setGravity(true) - } - aslistget0.velocity = pv.normalize().multiply(funnelSpeed) - } else { - if (aslistget0.hasGravity()) { - aslistget0.setGravity(false) - } - aslistget0.teleport(lpl) - } - if (( - hashPlayer - .get(aslistget0)!! - .gameMode == GameMode.SPECTATOR || - !getPlayerData( - hashPlayer.get(aslistget0), - )!!.isInMatch || - !hashPlayer.get(aslistget0)!!.isOnline - ) && - kdata1.damage < funnelMaxHP - ) { - kdataReset1 = i + 3 - kdata1.damage = (funnelMaxHP + 1).toDouble() - } - } else if (hashArmorstand.containsKey(aslistget0)) { - val las = aslistget0.location - val lpl = - hashArmorstand - .get(aslistget0)!! - .location - .add(l1.clone().multiply(2).add(Vector(0.0, 1.4, 0.0))) - pv = - Vector( - lpl.x - las.x, - lpl.y - las.y, - lpl.z - las.z, - ) - if (i % 48 == 16) { - funnelShot( - p, - aslistget0, - hashArmorstand.get(aslist[0])!!.eyeLocation, - ) - } - if (pv.length() > 1) { - if (!aslistget0.hasGravity()) { - aslistget0.setGravity(true) - } - aslistget0.velocity = pv.normalize().multiply(funnelSpeed) - } else { - if (aslistget0.hasGravity()) { - aslistget0.setGravity(false) - } - aslistget0.teleport(lpl) - } - if (!hashArmorstand.get(aslistget0)!!.isVisible) { - kdataReset1 = i + 3 - kdata1.damage = (funnelMaxHP + 1).toDouble() - } - } - if (i % 20 == 0) { - val team = data!!.team - for (o_player in plugin.server.onlinePlayers) { - (o_player as CraftPlayer).handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[2].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team!!.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[1].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[0].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack(team.teamColor!!.wool!!), - ), - ), - ) - } - } - } - if (io == 2) { - if (!hashPlayer.containsKey(aslistget0) && - !hashArmorstand.containsKey(aslist[0]) - ) { - aslistget0.teleport( - locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)), - ) - } else if (hashPlayer.containsKey(aslistget0)) { - val las = aslistget0.location - val lpl = - hashPlayer - .get(aslistget0)!! - .location - .add(r1.clone().multiply(2).add(Vector(0.0, 1.4, 0.0))) - pv = - Vector( - lpl.x - las.x, - lpl.y - las.y, - lpl.z - las.z, - ) - if (i % 48 == 32) { - if (!getPlayerData(hashPlayer.get(aslistget0))!!.isUsingSP) { - funnelShot( - p, - aslistget0, - hashPlayer.get(aslistget0)!!.eyeLocation, - ) - } - } - if (pv.length() > 1) { - if (!aslistget0.hasGravity()) { - aslistget0.setGravity(true) - } - aslistget0.velocity = pv.normalize().multiply(funnelSpeed) - } else { - if (aslistget0.hasGravity()) { - aslistget0.setGravity(false) - } - aslistget0.teleport(lpl) - } - if (( - hashPlayer - .get(aslistget0)!! - .gameMode == GameMode.SPECTATOR || - !getPlayerData( - hashPlayer.get(aslistget0), - )!!.isInMatch || - !hashPlayer.get(aslistget0)!!.isOnline - ) && - kdata2.damage < funnelMaxHP - ) { - kdataReset2 = i + 3 - kdata2.damage = (funnelMaxHP + 1).toDouble() - } - } else if (hashArmorstand.containsKey(aslistget0)) { - val las = aslistget0.location - val lpl = - hashArmorstand - .get(aslistget0)!! - .location - .add(r1.clone().multiply(2).add(Vector(0.0, 1.4, 0.0))) - pv = - Vector( - lpl.x - las.x, - lpl.y - las.y, - lpl.z - las.z, - ) - if (i % 48 == 32) { - funnelShot( - p, - aslistget0, - hashArmorstand.get(aslistget0)!!.eyeLocation, - ) - } - if (pv.length() > 1) { - if (!aslistget0.hasGravity()) { - aslistget0.setGravity(true) - } - aslistget0.velocity = pv.normalize().multiply(funnelSpeed) - } else { - if (aslistget0.hasGravity()) { - aslistget0.setGravity(false) - } - aslistget0.teleport(lpl) - } - if (!hashArmorstand.get(aslistget0)!!.isVisible) { - kdataReset2 = i + 3 - kdata2.damage = (funnelMaxHP + 1).toDouble() - } - } - // 残数表記 - if (i % 20 == 0) { - if (p.gameMode != GameMode.SPECTATOR) { - val funnelamo = funnelamount(player) - val nuget: ItemStack? - nuget = - if (funnelamo > 0) { - ItemStack(Material.GOLD_NUGGET, funnelamo) - } else { - ItemStack(Material.AIR) - } - player.inventory.setItem(8, nuget) - } - // 残数表記了 - val team = data!!.team - for (o_player in plugin.server.onlinePlayers) { - (o_player as CraftPlayer).handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[2].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team!!.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[1].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack( - Material.getMaterial( - team.teamColor!!.glass.toString() + "_PANE", - )!!, - ), - ), - ), - ) - o_player.handle.playerConnection.sendPacket( - PacketPlayOutEntityEquipment( - aslist[0].entityId, - EnumItemSlot.HEAD, - CraftItemStack.asNMSCopy( - ItemStack(team.teamColor!!.wool!!), - ), - ), - ) - } - } - } - io++ - } - for (aslist in list5) { - pv = - aslist[0] - .eyeLocation - .direction - .normalize() - vec1 = - Vector( - pv.clone().getX() * 0.707 - pv.clone().getZ() * 0.707, - 0.0, - pv.clone().getX() * 0.707 + pv.clone().getZ() * 0.707, - ).normalize() - vec2 = - Vector( - pv.clone().getX() * 0.707 + pv.clone().getZ() * 0.707, - 0.0, - -pv.clone().getX() * 0.707 + pv.clone().getZ() * 0.707, - ).normalize() - val floc2 = aslist[0].location.clone() - aslist[1].teleport(floc2.clone().add(0.0, 0.3, 0.0).add(vec1.clone().multiply(0.3))) - aslist[2].teleport(floc2.clone().add(0.0, 0.3, 0.0).add(vec2.clone().multiply(0.3))) - } - } - if (check && p.isSneaking && p.gameMode != GameMode.SPECTATOR) { - check = false - taskcheck.runTaskLater(plugin, 18) - player.world.playSound(player.location, Sound.ENTITY_FIREWORK_ROCKET_BLAST, 0.4f, 5f) - val rayTrace = - RayTrace( - player.eyeLocation.toVector(), - player.eyeLocation.direction, - ) - val positions = rayTrace.traverse(55.0, 0.3) - - loop@ for (it in positions.indices) { - val position = positions[it].toLocation(player.location.world!!) - val block = player.location.world!!.getBlockAt(position) - - if (block.type != Material.AIR) { - break - } - if (getPlayerData(player)!!.settings!!.showEffectMainWeaponInk()) { - if (it < 10) { - if (player.world === position.world) { - if (player - .location - .distanceSquared(position) < Sclat.particleRenderDistanceSquared - ) { - val bd = - getPlayerData(player)!! - .team!! - .teamColor!! - .wool!! - .createBlockData() - player.spawnParticle( - Particle.BLOCK_DUST, - position, - 1, - 0.0, - 0.0, - 0.0, - 1.0, - bd, - ) - } - } - } - } - - val maxDistSquad = 20.0 // 2*2 - for (target in plugin.server.onlinePlayers) { - if (!getPlayerData(target)!!.isInMatch) continue - if (getPlayerData(player)!!.team != getPlayerData(target)!!.team && - target.gameMode == GameMode.ADVENTURE - ) { - if (target.location.distanceSquared(position) <= maxDistSquad) { - // if(rayTrace.intersects(new BoundingBox((Entity)target), (30), 0.2)){ - player.world.playSound( - player.location, - Sound.BLOCK_NOTE_BLOCK_BIT, - 1.0f, - 5f, - ) - if (!list6.isEmpty()) { - if (list6[list6.size - 1] == as3 && funAmoP(target)) { - player.world.playSound( - target.location, - Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, - 1.0f, - 2f, - ) - hashPlayer[as3] = target - GlowingAPI.setGlowing(as3, player, true) - GlowingAPI.setGlowing(as3, target, true) - if (kdata.damage < funnelMaxHP2) { - kdata.damage = funnelMaxHP2.toDouble() - } - as3.setGravity(true) - kdataReset = i + 210 - // listremove.runTaskLater(Main.getPlugin(), 140); - list6.removeAt(list6.size - 1) - } else if (list6[list6.size - 1] == as13 && funAmoP(target)) { - player.world.playSound( - target.location, - Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, - 1.0f, - 2f, - ) - hashPlayer[as13] = target - GlowingAPI.setGlowing(as13, player, true) - GlowingAPI.setGlowing(as13, target, true) - if (kdata1.damage < funnelMaxHP2) { - kdata1.damage = funnelMaxHP2.toDouble() - } - as13.setGravity(true) - kdataReset1 = i + 210 - // listremove1.runTaskLater(Main.getPlugin(), 140); - list6.removeAt(list6.size - 1) - } else if (list6[list6.size - 1] == as23 && funAmoP(target)) { - player.world.playSound( - target.location, - Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, - 1.0f, - 2f, - ) - hashPlayer[as23] = target - GlowingAPI.setGlowing(as23, player, true) - GlowingAPI.setGlowing(as23, target, true) - if (kdata2.damage < funnelMaxHP2) { - kdata2.damage = funnelMaxHP2.toDouble() - } - as23.setGravity(true) - kdataReset2 = i + 210 - // listremove2.runTaskLater(Main.getPlugin(), 140); - list6.removeAt(list6.size - 1) - } - } - break@loop - // } - } - } - } - - for (`as` in player.world.entities) { - if (`as` is ArmorStand) { - if (`as`.location.distanceSquared(position) <= maxDistSquad) { - // if(rayTrace.intersects(new BoundingBox((Entity)as), (int)(30), 0.2)){ - if (`as`.customName != null) { - if (`as`.customName == "SplashShield") { - // SplashShieldData ssdata = - // DataMgr.getSplashShieldDataFromArmorStand((ArmorStand)as); - // if(DataMgr.getPlayerData(ssdata.player).getTeam() != - // DataMgr.getPlayerData(player).getTeam()){ - // break loop; - // } - } else if (`as`.customName == "Kasa") { - // KasaData ssdata = DataMgr.getKasaDataFromArmorStand((ArmorStand)as); - // if(DataMgr.getPlayerData(ssdata.player).getTeam() != - // DataMgr.getPlayerData(player).getTeam()){ - // break loop; - // } - } else { - if (SclatUtil.isNumber(`as`.customName!!)) { - if (`as`.customName != "21" && - `as`.customName != "100" - ) { - if (`as`.isVisible) { - // player.playSound(player.getLocation(), - // Sound.ENTITY_ARROW_HIT_PLAYER, 1.2F, 1.3F); - player.world.playSound( - player.location, - Sound.BLOCK_NOTE_BLOCK_BIT, - 1.0f, - 5f, - ) - } - } - } - if (!list6.isEmpty()) { - if (list6[list6.size - 1] == as3 && - funAmoA(`as`) - ) { - player.world.playSound( - `as`.location, - Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, - 1.0f, - 2f, - ) - hashArmorstand[as3] = `as` - GlowingAPI.setGlowing(as3, player, true) - if (kdata.damage < funnelMaxHP2) { - kdata.damage = funnelMaxHP2.toDouble() - } - as3.setGravity(true) - kdataReset = i + 210 - // listremove.runTaskLater(Main.getPlugin(), 140); - list6.removeAt(list6.size - 1) - } else if (list6[list6.size - 1] == as13 && - funAmoA(`as`) - ) { - player.world.playSound( - `as`.location, - Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, - 1.0f, - 2f, - ) - hashArmorstand[as13] = `as` - GlowingAPI.setGlowing(as13, player, true) - if (kdata1.damage < funnelMaxHP2) { - kdata1.damage = funnelMaxHP2.toDouble() - } - as13.setGravity(true) - kdataReset1 = i + 210 - // listremove1.runTaskLater(Main.getPlugin(), 140); - list6.removeAt(list6.size - 1) - } else if (list6[list6.size - 1] == as23 && - funAmoA(`as`) - ) { - player.world.playSound( - `as`.location, - Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, - 1.0f, - 2f, - ) - hashArmorstand[as23] = `as` - GlowingAPI.setGlowing(as23, player, true) - if (kdata2.damage < funnelMaxHP2) { - kdata2.damage = funnelMaxHP2.toDouble() - } - as23.setGravity(true) - kdataReset2 = i + 210 - // listremove2.runTaskLater(Main.getPlugin(), 140); - list6.removeAt(list6.size - 1) - } - } - break@loop - } - } - // ArmorStandMgr.giveDamageArmorStand((ArmorStand) as, damage, player); - // } - } - } - } - } - } - if (!p.isOnline || !data!!.isInMatch) { - if (getPlayerData(p)!!.isInMatch) { - as1 - .world - .playSound(as1.location, Sound.ENTITY_ITEM_BREAK, 0.8f, 0.8f) - } - - for (aslist in list5) { - for (`as` in aslist) { - `as`.remove() - } - } - las.remove() - cancel() - } - - i++ - } catch (e: Exception) { - cancel() - } - } - } - task.runTaskTimer(plugin, 0, 1) + FunnelTask(player).start() } fun funnelPursuit( @@ -1435,94 +54,37 @@ object Funnel { var rate = 0.0 for (ai in 0..2) { try { - if (hashArmorstand.containsKey(getPlayerData(player)!!.getArmorlist(ai))) { - if (hashArmorstand.get(getPlayerData(player)!!.getArmorlist(ai)) == target) { - rate += 1.5 - } + if (ArmorStandManager.hashArmorstand[getPlayerData(player)!!.getArmorlist(ai)] == target) { + rate += 1.5 } - } catch (e: Exception) { + } catch (_: Exception) { rate -= 0.7 } } val task: BukkitRunnable = object : BukkitRunnable() { - var p: Player = player - var data: PlayerData? = getPlayerData(player) - var loct: Location = target.location - var locd: Location? = null + val loct: Location = target.location override fun run() { + val playerData = getPlayerData(player)!! for (ai in 0..2) { try { - locd = data!!.getArmorlist(ai)!!.eyeLocation + val locd = playerData.getArmorlist(ai)!!.eyeLocation val vec = Vector( - loct.x - locd!!.x, - loct.y - locd!!.y + 1.5, - loct.z - locd!!.z, + loct.x - locd.x, + loct.y - locd.y + 1.5, + loct.z - locd.z, ) - val rayTrace = RayTrace(locd!!.toVector(), vec) + val rayTrace = RayTrace(locd.toVector(), vec) val positions = rayTrace.traverse(vec.length(), 0.4) - var veclength = vec.length() / 2 - if (veclength > 12) { - veclength = 12.0 - } - var i = 0 - while (i < veclength) { - val position = positions[i].toLocation(p.location.world!!) - if (player.world === position.world) { - if (player - .location - .distanceSquared(position) < Sclat.particleRenderDistanceSquared - ) { - val dustOptions = - Particle.DustOptions( - data!!.team!!.teamColor!!.bukkitColor!!, - 1f, - ) - player.spawnParticle( - Particle.REDSTONE, - position, - 1, - 0.0, - 0.0, - 0.0, - 3.0, - dustOptions, - ) - } - } - for (target in plugin.server.onlinePlayers) { - if (target == p || - getPlayerData(target)!!.settings!!.showEffectChargerLine() - ) { - if (target.world === p.world) { - if (target - .location - .distanceSquared(position) < Sclat.particleRenderDistanceSquared - ) { - val dustOptions = - Particle.DustOptions( - data!!.team!!.teamColor!!.bukkitColor!!, - 1f, - ) - target.spawnParticle( - Particle.REDSTONE, - position, - 1, - 0.0, - 0.0, - 0.0, - 3.0, - dustOptions, - ) - } - } - } - } - i++ + val veclength = min(vec.length() / 2, 12.0) + for (i in 0 until veclength.toInt()) { + val position = positions[i].toLocation(player.location.world!!) + spawnPursuitParticle(player, playerData, position) } } catch (e: Exception) { + sclatLogger.error("Failed to perform funnel pursuit ray trace for player", e) } } } @@ -1538,94 +100,42 @@ object Funnel { var rate = 0.0 for (ai in 0..2) { try { - if (hashPlayer.containsKey(getPlayerData(player)!!.getArmorlist(ai))) { - if (hashPlayer.get(getPlayerData(player)!!.getArmorlist(ai)) == target) { - rate += 1.5 - } + if (ArmorStandManager.hashPlayer[getPlayerData(player)!!.getArmorlist(ai)] == target) { + rate += 1.5 } - } catch (e: Exception) { + } catch (_: Exception) { rate -= 0.7 } } val task: BukkitRunnable = object : BukkitRunnable() { - var p: Player = player - var data: PlayerData? = getPlayerData(player) - var loct: Location = target.location - var locd: Location? = null + val loct: Location = target.location override fun run() { + val data: PlayerData = + getPlayerData(player) ?: run { + sclatLogger.warn("Failed to get player data for funnel pursuit particle spawning task, aborting task") + return + } + for (ai in 0..2) { try { - locd = data!!.getArmorlist(ai)!!.eyeLocation + val locd = data.getArmorlist(ai)!!.eyeLocation val vec = Vector( - loct.x - locd!!.x, - loct.y - locd!!.y + 1.5, - loct.z - locd!!.z, + loct.x - locd.x, + loct.y - locd.y + 1.5, + loct.z - locd.z, ) - val rayTrace = RayTrace(locd!!.toVector(), vec) + val rayTrace = RayTrace(locd.toVector(), vec) val positions = rayTrace.traverse(vec.length().toInt().toDouble(), 0.4) - var veclength = vec.length() / 2 - if (veclength > 12) { - veclength = 12.0 - } - var i = 0 - while (i < veclength) { - val position = positions[i].toLocation(p.location.world!!) - if (player.world === position.world) { - if (player - .location - .distanceSquared(position) < Sclat.particleRenderDistanceSquared - ) { - val dustOptions = - Particle.DustOptions( - data!!.team!!.teamColor!!.bukkitColor!!, - 1f, - ) - player.spawnParticle( - Particle.REDSTONE, - position, - 1, - 0.0, - 0.0, - 0.0, - 3.0, - dustOptions, - ) - } - } - for (target in plugin.server.onlinePlayers) { - if (target == p || - getPlayerData(target)!!.settings!!.showEffectChargerLine() - ) { - if (target.world === p.world) { - if (target - .location - .distanceSquared(position) < Sclat.particleRenderDistanceSquared - ) { - val dustOptions = - Particle.DustOptions( - data!!.team!!.teamColor!!.bukkitColor!!, - 1f, - ) - target.spawnParticle( - Particle.REDSTONE, - position, - 1, - 0.0, - 0.0, - 0.0, - 3.0, - dustOptions, - ) - } - } - } - } - i++ + val veclength = min(vec.length() / 2, 12.0) + for (i in 0 until veclength.toInt()) { + val position = positions[i].toLocation(player.location.world!!) + spawnPursuitParticle(player, data, position) } } catch (e: Exception) { + sclatLogger.error("Failed to perform funnel pursuit ray trace for player", e) } } } @@ -1638,22 +148,23 @@ object Funnel { var rate = 3 for (ai in 0..2) { try { - if (hashPlayer.containsKey(getPlayerData(player)!!.getArmorlist(ai))) { + val armorList = getPlayerData(player)!!.getArmorlist(ai) + if (ArmorStandManager.hashPlayer.containsKey(armorList)) { rate -= 1 } - if (hashArmorstand.containsKey(getPlayerData(player)!!.getArmorlist(ai))) { + if (ArmorStandManager.hashArmorstand.containsKey(armorList)) { rate -= 1 } - } catch (e: Exception) { + } catch (_: Exception) { rate -= 1 } } return rate } - private fun funAmoP(player: Player?): Boolean { + internal fun funAmoP(player: Player?): Boolean { var count = 0 - for (entry in hashPlayer.entries) { + for (entry in ArmorStandManager.hashPlayer.entries) { if (entry.value === player) { count++ } @@ -1661,13 +172,46 @@ object Funnel { return count < 3 } - private fun funAmoA(stand: ArmorStand?): Boolean { - var count = 0 - for (entry in hashArmorstand.entries) { - if (entry.value === stand) { - count++ + internal fun funAmoA(stand: ArmorStand?): Boolean = ArmorStandManager.hashArmorstand.count { it.value === stand } < 3 + + /** Spawns a pursuit-line particle at [position] for the player and nearby observers. */ + private fun spawnPursuitParticle( + player: Player, + data: PlayerData, + position: Location, + ) { + val dustOptions = Particle.DustOptions(data.team!!.teamColor!!.bukkitColor!!, 1f) + if (player.world === position.world && + player.location.distanceSquared(position) < Sclat.particleRenderDistanceSquared + ) { + player.spawnParticle( + Particle.REDSTONE, + position, + 1, + 0.0, + 0.0, + 0.0, + 3.0, + dustOptions, + ) + } + for (target in plugin.server.onlinePlayers) { + if (target == player || getPlayerData(target)!!.settings!!.showEffectChargerLine()) { + if (target.world === player.world && + target.location.distanceSquared(position) < Sclat.particleRenderDistanceSquared + ) { + target.spawnParticle( + Particle.REDSTONE, + position, + 1, + 0.0, + 0.0, + 0.0, + 3.0, + dustOptions, + ) + } } } - return count < 3 } } diff --git a/src/main/kotlin/be4rjp/sclat/weapon/FunnelTask.kt b/src/main/kotlin/be4rjp/sclat/weapon/FunnelTask.kt new file mode 100644 index 0000000..26999de --- /dev/null +++ b/src/main/kotlin/be4rjp/sclat/weapon/FunnelTask.kt @@ -0,0 +1,675 @@ +package be4rjp.sclat.weapon + +import be4rjp.sclat.Sclat +import be4rjp.sclat.api.GlowingAPI +import be4rjp.sclat.api.SclatUtil +import be4rjp.sclat.api.player.PlayerData +import be4rjp.sclat.api.raytrace.RayTrace +import be4rjp.sclat.data.DataMgr.getPlayerData +import be4rjp.sclat.data.DataMgr.setKasaDataWithARmorStand +import be4rjp.sclat.data.DataMgr.setKasaDataWithPlayer +import be4rjp.sclat.data.KasaData +import be4rjp.sclat.plugin +import org.bukkit.GameMode +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.Sound +import org.bukkit.block.data.BlockData +import org.bukkit.entity.ArmorStand +import org.bukkit.entity.EntityType +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.scheduler.BukkitRunnable +import org.bukkit.util.EulerAngle +import org.bukkit.util.Vector + +/** + * Manages the lifecycle and per-tick behavior of the three funnel (kasa) groups for a player. + * Handles spawning, movement, targeting, lock-on, and respawn scheduling. + */ +class FunnelTask( + private val player: Player, +) : BukkitRunnable() { + private val kdata = KasaData(player) + private val kdata1 = KasaData(player) + private val kdata2 = KasaData(player) + private val data: PlayerData? = getPlayerData(player) + + private val list: MutableList = ArrayList() + private val list1: MutableList = ArrayList() + private val list2: MutableList = ArrayList() + private val list5: MutableList> = mutableListOf() + private val list6: MutableList = mutableListOf() + + private lateinit var as3: ArmorStand + private lateinit var as13: ArmorStand + private lateinit var as23: ArmorStand + private lateinit var las: ArmorStand + + private var i: Int = 0 + private var check: Boolean = false + private var kdataReset: Int = -1 + private var kdataReset1: Int = -1 + private var kdataReset2: Int = -1 + + /** Registers kasa data and starts the repeating task. */ + fun start() { + setKasaDataWithPlayer(player, kdata) + setKasaDataWithPlayer(player, kdata1) + setKasaDataWithPlayer(player, kdata2) + runTaskTimer(plugin, 0, 1) + } + + override fun run() { + try { + val locp = player.location + val pv = player.eyeLocation.direction.normalize() + val vec = Vector(pv.x, 0.0, pv.z).normalize() + val l1 = Vector(-vec.z, 0.0, vec.x) + val r1 = Vector(vec.z, 0.0, -vec.x) + + val taskcheck: BukkitRunnable = + object : BukkitRunnable() { + override fun run() { + check = true + } + } + + if (i == 0) { + initializeFunnelGroups(locp, pv, l1, r1) + taskcheck.runTaskLater(plugin, 20) + } + + if (i >= 0) { + handleSpectatorDestruction() + handleDestroyedGroups(locp, l1, r1) + + var io = 0 + for (aslist in list5) { + val aslistget0: ArmorStand = aslist[0] + val kdataForGroup = + when (io) { + 0 -> kdata + 1 -> kdata1 + else -> kdata2 + } + val idleTarget = + when (io) { + 0 -> locp.clone().add(0.0, 2.5, 0.0) + 1 -> locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)) + else -> locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)) + } + if (!ArmorStandManager.hashPlayer.containsKey(aslistget0) && + !ArmorStandManager.hashArmorstand.containsKey(aslistget0) + ) { + aslistget0.teleport(idleTarget) + } else { + moveFunnelToTarget(aslistget0, io, pv, l1, r1, kdataForGroup, i) + } + + if (i % 20 == 0) { + val team = data!!.team + if (team != null) { + ArmorStandManager.sendEquipmentPackets(aslist, team) + } + if (io == 2 && player.gameMode != GameMode.SPECTATOR) { + val funnelamo = Funnel.funnelamount(player) + val nuget = + if (funnelamo > 0) { + ItemStack(Material.GOLD_NUGGET, funnelamo) + } else { + ItemStack(Material.AIR) + } + player.inventory.setItem(8, nuget) + } + } + + io++ + } + + for (aslist in list5) { + val dir = aslist[0].eyeLocation.direction.normalize() + val vec1 = + Vector( + dir.x * 0.707 - dir.z * 0.707, + 0.0, + dir.x * 0.707 + dir.z * 0.707, + ).normalize() + val vec2 = + Vector( + dir.x * 0.707 + dir.z * 0.707, + 0.0, + -dir.x * 0.707 + dir.z * 0.707, + ).normalize() + val floc2 = aslist[0].location.clone() + aslist[1].teleport(floc2.clone().add(0.0, 0.3, 0.0).add(vec1.clone().multiply(0.3))) + aslist[2].teleport(floc2.clone().add(0.0, 0.3, 0.0).add(vec2.clone().multiply(0.3))) + } + } + + if (check && player.isSneaking && player.gameMode != GameMode.SPECTATOR) { + check = false + taskcheck.runTaskLater(plugin, 18) + performLockOnScan(l1, r1) + } + + if (!player.isOnline || !data!!.isInMatch) { + if (getPlayerData(player)!!.isInMatch) { + list[1].world.playSound(list[1].location, Sound.ENTITY_ITEM_BREAK, 0.8f, 0.8f) + } + for (aslist in list5) { + for (armorStand in aslist) { + armorStand.remove() + } + } + las.remove() + cancel() + } + + i++ + } catch (e: Exception) { + cancel() + } + } + + // ── Initialization ──────────────────────────────────────────────────────── + + /** + * Spawns all three funnel groups and configures them on the very first tick (i == 0). + */ + private fun initializeFunnelGroups( + locp: Location, + pv: Vector, + l1: Vector, + r1: Vector, + ) { + val vec1 = + Vector( + pv.x * 0.707 - pv.z * 0.707, + 0.0, + pv.x * 0.707 + pv.z * 0.707, + ).normalize() + val vec2 = + Vector( + pv.x * 0.707 + pv.z * 0.707, + 0.0, + -pv.x * 0.707 + pv.z * 0.707, + ).normalize() + + // Group 0 – above the player + as3 = player.world.spawnEntity(locp.clone().add(0.0, 2.5, 0.0), EntityType.ARMOR_STAND) as ArmorStand + val as1 = + player.world.spawnEntity( + locp.clone().add(0.0, 2.8, 0.0).add(vec1.clone().multiply(0.3)), + EntityType.ARMOR_STAND, + ) as ArmorStand + val as2 = + player.world.spawnEntity( + locp.clone().add(0.0, 2.8, 0.0).add(vec2.clone().multiply(0.3)), + EntityType.ARMOR_STAND, + ) as ArmorStand + list.add(as3) + list.add(as1) + list.add(as2) + + // Group 1 – left side + as13 = player.world.spawnEntity(locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)), EntityType.ARMOR_STAND) as ArmorStand + val as11 = + player.world.spawnEntity( + locp + .clone() + .add(0.0, 1.3, 0.0) + .add(vec1.clone().multiply(0.3)) + .add(l1.clone().multiply(1.5)), + EntityType.ARMOR_STAND, + ) as ArmorStand + val as12 = + player.world.spawnEntity( + locp + .clone() + .add(0.0, 1.3, 0.0) + .add(vec2.clone().multiply(0.3)) + .add(l1.clone().multiply(1.5)), + EntityType.ARMOR_STAND, + ) as ArmorStand + list1.add(as13) + list1.add(as11) + list1.add(as12) + + // Group 2 – right side + as23 = player.world.spawnEntity(locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)), EntityType.ARMOR_STAND) as ArmorStand + val as21 = + player.world.spawnEntity( + locp + .clone() + .add(0.0, 1.3, 0.0) + .add(vec1.clone().multiply(0.3)) + .add(r1.clone().multiply(1.5)), + EntityType.ARMOR_STAND, + ) as ArmorStand + val as22 = + player.world.spawnEntity( + locp + .clone() + .add(0.0, 1.3, 0.0) + .add(vec2.clone().multiply(0.3)) + .add(r1.clone().multiply(1.5)), + EntityType.ARMOR_STAND, + ) as ArmorStand + list2.add(as23) + list2.add(as21) + list2.add(as22) + + list5.add(list) + list5.add(list1) + list5.add(list2) + data!!.setArmorlist(as3) + data.setArmorlist(as13) + data.setArmorlist(as23) + list6.add(as3) + list6.add(as13) + list6.add(as23) + kdata.armorStandList = list + kdata1.armorStandList = list1 + kdata2.armorStandList = list2 + kdata.damage = 0.0 + kdata1.damage = 0.0 + kdata2.damage = 0.0 + + for (armorStand in list) setKasaDataWithARmorStand(armorStand, kdata) + for (armorStand in list1) setKasaDataWithARmorStand(armorStand, kdata1) + for (armorStand in list2) setKasaDataWithARmorStand(armorStand, kdata2) + + for (aslist in list5) { + aslist[1].headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(-40.0)) + aslist[2].headPose = EulerAngle(Math.toRadians(-45.0), 0.0, Math.toRadians(40.0)) + for (armorStand in aslist) { + armorStand.isSmall = true + armorStand.setBasePlate(false) + armorStand.isVisible = false + armorStand.setGravity(false) + armorStand.customName = "Kasa" + } + } + + val team = data.team + if (team != null) { + for (aslist in list5) { + ArmorStandManager.sendEquipmentPackets(aslist, team) + } + } + } + + // ── Destruction / revival ───────────────────────────────────────────────── + + /** Forces all funnel groups into the destroyed state when the player is in spectator. */ + private fun handleSpectatorDestruction() { + if (player.gameMode == GameMode.SPECTATOR) { + if (kdata.damage <= Funnel.funnelMaxHP) kdata.damage = 1024.0 + if (kdata1.damage <= Funnel.funnelMaxHP) kdata1.damage = 1024.0 + if (kdata2.damage <= Funnel.funnelMaxHP) kdata2.damage = 1024.0 + } + } + + /** + * Checks each group's damage; if a group has been destroyed, removes its entities, + * updates state, and schedules a rebuild runnable. + */ + private fun handleDestroyedGroups( + locp: Location, + l1: Vector, + r1: Vector, + ) { + checkGroupDestroyed(kdata, list, 0, locp, l1, r1) + checkGroupDestroyed(kdata1, list1, 1, locp, l1, r1) + checkGroupDestroyed(kdata2, list2, 2, locp, l1, r1) + + if (i == kdataReset) { + scheduleGroupRebuild(0, locp, l1, r1, 1) + kdataReset = -1 + } + if (i == kdataReset1) { + scheduleGroupRebuild(1, locp, l1, r1, 1) + kdataReset1 = -1 + } + if (i == kdataReset2) { + scheduleGroupRebuild(2, locp, l1, r1, 1) + kdataReset2 = -1 + } + } + + private fun checkGroupDestroyed( + kd: KasaData, + groupList: MutableList, + groupIndex: Int, + locp: Location, + l1: Vector, + r1: Vector, + ) { + if (kd.damage <= Funnel.funnelMaxHP || kd.damage >= 9999) return + val kasaStand = kd.armorStandList[0] + data!!.subArmorlist(kasaStand) + + when { + ArmorStandManager.hashPlayer.containsKey(kasaStand) -> { + if (ArmorStandManager.hashPlayer[kasaStand]!!.gameMode != GameMode.SPECTATOR) { + incrementReset(groupIndex) + } + ArmorStandManager.hashPlayer.remove(kasaStand) + } + ArmorStandManager.hashArmorstand.containsKey(kasaStand) -> { + incrementReset(groupIndex) + ArmorStandManager.hashArmorstand.remove(kasaStand) + } + else -> { + list6.remove(kasaStand) + val delay = if (kd.damage == 1024.0) 110L else 160L + scheduleGroupRebuild(groupIndex, locp, l1, r1, delay) + } + } + kd.damage = 10000.0 + for (armorStand in kd.armorStandList) { + armorStand.remove() + } + } + + private fun incrementReset(groupIndex: Int) { + when (groupIndex) { + 0 -> kdataReset += 60 + 1 -> kdataReset1 += 60 + 2 -> kdataReset2 += 60 + } + } + + /** + * Schedules a BukkitRunnable that cleans up and respawns the specified funnel group. + * + * @param groupIndex 0 = above, 1 = left, 2 = right + * @param locp Player location captured at the tick when the rebuild is scheduled + * @param l1 Left-perpendicular vector captured at that tick + * @param r1 Right-perpendicular vector captured at that tick + * @param delay Delay in ticks before the rebuild executes + */ + private fun scheduleGroupRebuild( + groupIndex: Int, + locp: Location, + l1: Vector, + r1: Vector, + delay: Long, + ) { + val groupList = + when (groupIndex) { + 0 -> list + 1 -> list1 + else -> list2 + } + val kd = + when (groupIndex) { + 0 -> kdata + 1 -> kdata1 + else -> kdata2 + } + + object : BukkitRunnable() { + override fun run() { + try { + ArmorStandManager.cleanupFunnelGroup(groupList, data!!) + } catch (e: Exception) { + } + + val mainLoc: Location + val wingsLoc: Location + when (groupIndex) { + 0 -> { + mainLoc = locp.clone().add(0.0, 2.5, 0.0) + wingsLoc = locp.clone().add(0.0, 2.8, 0.0) + } + 1 -> { + mainLoc = locp.clone().add(0.0, 1.0, 0.0).add(l1.clone().multiply(1.5)) + wingsLoc = mainLoc + } + else -> { + mainLoc = locp.clone().add(0.0, 1.0, 0.0).add(r1.clone().multiply(1.5)) + wingsLoc = mainLoc + } + } + + val newGroup = + ArmorStandManager.spawnFunnelGroup( + player, + data!!, + kd, + mainLoc, + wingsLoc, + applyGlowing = true, + ) + groupList.addAll(newGroup) + + when (groupIndex) { + 0 -> as3 = groupList[0] + 1 -> as13 = groupList[0] + 2 -> as23 = groupList[0] + } + + val team = data.team + if (team != null) { + ArmorStandManager.sendEquipmentPackets(groupList, team) + } + list6.add(groupList[0]) + kd.damage = 0.0 + kd.armorStandList = groupList + cancel() + } + }.runTaskLater(plugin, delay) + } + + // ── Movement ────────────────────────────────────────────────────────────── + + /** + * Moves the given funnel stand towards its locked-on target (player or armor stand), + * firing a shot at the appropriate interval. + * Updates kdataReset if the target is gone. + */ + private fun moveFunnelToTarget( + stand: ArmorStand, + io: Int, + pv: Vector, + l1: Vector, + r1: Vector, + kd: KasaData, + tick: Int, + ) { + val shotInterval = 48 + val shotOffset = + when (io) { + 0 -> 0 + 1 -> 16 + else -> 32 + } + val dirOffset = + when (io) { + 0 -> pv.clone().multiply(2).add(Vector(0.0, 1.4, 0.0)) + 1 -> l1.clone().multiply(2).add(Vector(0.0, 1.4, 0.0)) + else -> r1.clone().multiply(2).add(Vector(0.0, 1.4, 0.0)) + } + + if (ArmorStandManager.hashPlayer.containsKey(stand)) { + val target = ArmorStandManager.hashPlayer[stand]!! + val lpl = target.location.add(dirOffset) + moveStandTowards(stand, lpl) + if (tick % shotInterval == shotOffset) { + if (!getPlayerData(target)!!.isUsingSP) { + Funnel.funnelShot(player, stand, target.eyeLocation) + } + } + if (( + target.gameMode == GameMode.SPECTATOR || + !getPlayerData(target)!!.isInMatch || + !target.isOnline + ) && + kd.damage < Funnel.funnelMaxHP + ) { + kd.damage = (Funnel.funnelMaxHP + 1).toDouble() + setResetForGroup(io, tick + 3) + } + } else if (ArmorStandManager.hashArmorstand.containsKey(stand)) { + val targetStand = ArmorStandManager.hashArmorstand[stand]!! + val lpl = targetStand.location.add(dirOffset) + moveStandTowards(stand, lpl) + if (tick % shotInterval == shotOffset) { + Funnel.funnelShot(player, stand, targetStand.eyeLocation) + } + if (!targetStand.isVisible) { + setResetForGroup(io, tick + 3) + kd.damage = (Funnel.funnelMaxHP + 1).toDouble() + } + } + } + + /** Moves a funnel stand towards [target] using velocity or teleport depending on distance. */ + private fun moveStandTowards( + stand: ArmorStand, + target: Location, + ) { + val las = stand.location + val moveVec = Vector(target.x - las.x, target.y - las.y, target.z - las.z) + if (moveVec.length() > 1) { + if (!stand.hasGravity()) stand.setGravity(true) + stand.velocity = moveVec.normalize().multiply(Funnel.funnelSpeed) + } else { + if (stand.hasGravity()) stand.setGravity(false) + stand.teleport(target) + } + } + + private fun setResetForGroup( + io: Int, + value: Int, + ) { + when (io) { + 0 -> kdataReset = value + 1 -> kdataReset1 = value + 2 -> kdataReset2 = value + } + } + + // ── Lock-on scan ────────────────────────────────────────────────────────── + + /** + * Performs the lock-on ray trace triggered by the player sneaking. + * Attempts to assign the next available funnel from [list6] to the first nearby target found. + */ + private fun performLockOnScan( + l1: Vector, + r1: Vector, + ) { + player.world.playSound(player.location, Sound.ENTITY_FIREWORK_ROCKET_BLAST, 0.4f, 5f) + val rayTrace = RayTrace(player.eyeLocation.toVector(), player.eyeLocation.direction) + val positions = rayTrace.traverse(55.0, 0.3) + + loop@ for (idx in positions.indices) { + val position = positions[idx].toLocation(player.location.world!!) + if (player.location.world!! + .getBlockAt(position) + .type != Material.AIR + ) { + break + } + + if (getPlayerData(player)!!.settings!!.showEffectMainWeaponInk() && idx < 10) { + if (player.world === position.world && + player.location.distanceSquared(position) < Sclat.particleRenderDistanceSquared + ) { + val bd = + getPlayerData(player)!! + .team!! + .teamColor!! + .wool!! + .createBlockData() + player.spawnParticle(Particle.BLOCK_DUST, position, 1, 0.0, 0.0, 0.0, 1.0, bd) + } + } + + val maxDistSquared = 20.0 + for (target in plugin.server.onlinePlayers) { + if (!getPlayerData(target)!!.isInMatch) continue + if (getPlayerData(player)!!.team != getPlayerData(target)!!.team && + target.gameMode == GameMode.ADVENTURE + ) { + if (target.location.distanceSquared(position) <= maxDistSquared) { + player.world.playSound(player.location, Sound.BLOCK_NOTE_BLOCK_BIT, 1.0f, 5f) + if (list6.isNotEmpty()) tryLockOnPlayer(target) + break@loop + } + } + } + + for (entity in player.world.entities) { + if (entity !is ArmorStand) continue + if (entity.location.distanceSquared(position) > maxDistSquared) continue + if (entity.customName == null) continue + if (entity.customName == "SplashShield" || entity.customName == "Kasa") continue + + if (SclatUtil.isNumber(entity.customName!!)) { + if (entity.customName != "21" && entity.customName != "100" && entity.isVisible) { + player.world.playSound(player.location, Sound.BLOCK_NOTE_BLOCK_BIT, 1.0f, 5f) + } + } + if (list6.isNotEmpty()) tryLockOnArmorStand(entity) + break@loop + } + } + } + + /** Attempts to lock the last available funnel in [list6] onto a player target. */ + private fun tryLockOnPlayer(target: Player) { + if (list6.isEmpty()) return + when (list6.last()) { + as3 -> if (Funnel.funAmoP(target)) lockFunnelOnPlayer(as3, target, kdata, 0) + as13 -> if (Funnel.funAmoP(target)) lockFunnelOnPlayer(as13, target, kdata1, 1) + as23 -> if (Funnel.funAmoP(target)) lockFunnelOnPlayer(as23, target, kdata2, 2) + } + } + + private fun lockFunnelOnPlayer( + stand: ArmorStand, + target: Player, + kd: KasaData, + groupIndex: Int, + ) { + player.world.playSound(target.location, Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, 1.0f, 2f) + ArmorStandManager.hashPlayer[stand] = target + GlowingAPI.setGlowing(stand, player, true) + GlowingAPI.setGlowing(stand, target, true) + if (kd.damage < Funnel.funnelMaxHP2) kd.damage = Funnel.funnelMaxHP2.toDouble() + stand.setGravity(true) + setResetForGroup(groupIndex, i + 210) + list6.removeAt(list6.size - 1) + } + + /** Attempts to lock the last available funnel in [list6] onto an armor stand target. */ + private fun tryLockOnArmorStand(target: ArmorStand) { + if (list6.isEmpty()) return + when (list6.last()) { + as3 -> if (Funnel.funAmoA(target)) lockFunnelOnArmorStand(as3, target, kdata, 0) + as13 -> if (Funnel.funAmoA(target)) lockFunnelOnArmorStand(as13, target, kdata1, 1) + as23 -> if (Funnel.funAmoA(target)) lockFunnelOnArmorStand(as23, target, kdata2, 2) + } + } + + private fun lockFunnelOnArmorStand( + stand: ArmorStand, + target: ArmorStand, + kd: KasaData, + groupIndex: Int, + ) { + player.world.playSound(target.location, Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, 1.0f, 2f) + ArmorStandManager.hashArmorstand[stand] = target + GlowingAPI.setGlowing(stand, player, true) + if (kd.damage < Funnel.funnelMaxHP2) kd.damage = Funnel.funnelMaxHP2.toDouble() + stand.setGravity(true) + setResetForGroup(groupIndex, i + 210) + list6.removeAt(list6.size - 1) + } +} diff --git a/src/main/kotlin/be4rjp/sclat/weapon/RayTraceHandler.kt b/src/main/kotlin/be4rjp/sclat/weapon/RayTraceHandler.kt new file mode 100644 index 0000000..8ba8cbd --- /dev/null +++ b/src/main/kotlin/be4rjp/sclat/weapon/RayTraceHandler.kt @@ -0,0 +1,152 @@ +package be4rjp.sclat.weapon + +import be4rjp.sclat.Sclat +import be4rjp.sclat.api.SclatUtil +import be4rjp.sclat.api.SclatUtil.giveDamage +import be4rjp.sclat.api.raytrace.BoundingBox +import be4rjp.sclat.api.raytrace.RayTrace +import be4rjp.sclat.data.DataMgr.getKasaDataFromArmorStand +import be4rjp.sclat.data.DataMgr.getPlayerData +import be4rjp.sclat.data.DataMgr.getSplashShieldDataFromArmorStand +import be4rjp.sclat.manager.ArmorStandMgr +import be4rjp.sclat.plugin +import org.bukkit.GameMode +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.Sound +import org.bukkit.block.data.BlockData +import org.bukkit.entity.ArmorStand +import org.bukkit.entity.Entity +import org.bukkit.entity.Player +import org.bukkit.scheduler.BukkitRunnable +import org.bukkit.util.Vector + +object RayTraceHandler { + /** + * Performs a funnel shot ray trace from [funnel] towards [targetLoc], applying damage to + * the first hit player or armor stand. + */ + fun performShot( + player: Player, + funnel: ArmorStand, + targetLoc: Location, + ) { + val damage = 3.0 + val funloc = funnel.eyeLocation + val direction = + Vector( + targetLoc.x - funloc.x, + targetLoc.y - funloc.y, + targetLoc.z - funloc.z, + ).normalize() + val rayTrace = RayTrace(funloc.toVector(), direction) + val positions = rayTrace.traverse(4.0, 0.2) + val maxDistSquared = 4.0 + + loop@ for (vector in positions) { + val position = vector.toLocation(player.location.world!!) + if (player.location.world!! + .getBlockAt(position) + .type != Material.AIR + ) { + break + } + + spawnInkParticles(player, position) + + for (target in plugin.server.onlinePlayers) { + if (!getPlayerData(target)!!.isInMatch) continue + if (getPlayerData(player)!!.team != getPlayerData(target)!!.team && + target.gameMode == GameMode.ADVENTURE + ) { + if (target.location.distanceSquared(position) <= maxDistSquared) { + if (rayTrace.intersects(BoundingBox(target as Entity), 4.0, 0.05)) { + giveDamage(player, target, damage, "killed") + player.playSound(player.location, Sound.ENTITY_PLAYER_HURT, 1.2f, 1.3f) + object : BukkitRunnable() { + override fun run() { + target.noDamageTicks = 0 + } + }.runTaskLater(plugin, 1) + break@loop + } + } + } + } + + for (entity in player.world.entities) { + if (entity !is ArmorStand) continue + if (entity.location.distanceSquared(position) > maxDistSquared) continue + if (!rayTrace.intersects(BoundingBox(entity as Entity), 4.0, 0.05)) continue + if (applyDamageToArmorStand(entity, player, damage)) break@loop + } + } + } + + private fun spawnInkParticles( + player: Player, + position: Location, + ) { + for (target in plugin.server.onlinePlayers) { + if (getPlayerData(target)!!.settings!!.showEffectMainWeaponInk()) { + if (target.world === position.world) { + if (target.location.distanceSquared(position) < Sclat.particleRenderDistanceSquared) { + val bd = + getPlayerData(player)!! + .team!! + .teamColor!! + .wool!! + .createBlockData() + target.spawnParticle(Particle.BLOCK_DUST, position, 1, 0.0, 0.0, 0.0, 1.0, bd) + } + } + } + } + } + + /** + * Applies damage to an armor stand hit during a funnel shot. + * @return true if the outer ray traversal loop should break + */ + private fun applyDamageToArmorStand( + armorStand: ArmorStand, + player: Player, + damage: Double, + ): Boolean { + val name = armorStand.customName + if (name == null) { + ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) + return false + } + return when (name) { + "SplashShield" -> { + val ssdata = getSplashShieldDataFromArmorStand(armorStand) ?: return false + if (getPlayerData(ssdata.player)!!.team != getPlayerData(player)!!.team) { + ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) + armorStand.world.playSound(armorStand.location, Sound.ENTITY_PLAYER_HURT, 0.8f, 1.2f) + true + } else { + false + } + } + "Kasa" -> { + val ssdata = getKasaDataFromArmorStand(armorStand) ?: return false + if (getPlayerData(ssdata.player)!!.team != getPlayerData(player)!!.team) { + ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) + armorStand.world.playSound(armorStand.location, Sound.ENTITY_PLAYER_HURT, 0.8f, 1.2f) + true + } else { + false + } + } + else -> { + if (SclatUtil.isNumber(name) && name != "21" && name != "100" && armorStand.isVisible) { + player.playSound(player.location, Sound.ENTITY_ARROW_HIT_PLAYER, 1.2f, 1.3f) + } + ArmorStandMgr.giveDamageArmorStand(armorStand, damage, player) + true + } + } + } +}