new system ; now you can cast in the air! and also pull towards it!
This commit is contained in:
@@ -26,6 +26,7 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
private val grapplingHookKey = NamespacedKey(this, "grappling_hook")
|
private val grapplingHookKey = NamespacedKey(this, "grappling_hook")
|
||||||
private val cooldowns = HashMap<UUID, Long>()
|
private val cooldowns = HashMap<UUID, Long>()
|
||||||
private val recentlyGrappled = HashSet<UUID>()
|
private val recentlyGrappled = HashSet<UUID>()
|
||||||
|
private val activeHooks = HashMap<UUID, FishHook>()
|
||||||
|
|
||||||
private var cooldownSeconds = 2
|
private var cooldownSeconds = 2
|
||||||
private var pullStrength = 1.5
|
private var pullStrength = 1.5
|
||||||
@@ -33,6 +34,8 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
private var maxDistance = 30.0
|
private var maxDistance = 30.0
|
||||||
private var showParticles = true
|
private var showParticles = true
|
||||||
private var hookSpeed = 2.5
|
private var hookSpeed = 2.5
|
||||||
|
private var airGrappleStrength = 1.2
|
||||||
|
private var airGrappleEnabled = true
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
server.pluginManager.registerEvents(this, this)
|
server.pluginManager.registerEvents(this, this)
|
||||||
@@ -58,6 +61,8 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
writer.println("max-distance: 30.0")
|
writer.println("max-distance: 30.0")
|
||||||
writer.println("show-particles: true")
|
writer.println("show-particles: true")
|
||||||
writer.println("hook-speed: 2.5")
|
writer.println("hook-speed: 2.5")
|
||||||
|
writer.println("air-grapple-enabled: true")
|
||||||
|
writer.println("air-grapple-strength: 1.2")
|
||||||
writer.close()
|
writer.close()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.severe("Could not create config.yml: ${e.message}")
|
logger.severe("Could not create config.yml: ${e.message}")
|
||||||
@@ -77,6 +82,8 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
maxDistance = config.getDouble("max-distance", 30.0)
|
maxDistance = config.getDouble("max-distance", 30.0)
|
||||||
showParticles = config.getBoolean("show-particles", true)
|
showParticles = config.getBoolean("show-particles", true)
|
||||||
hookSpeed = config.getDouble("hook-speed", 2.5)
|
hookSpeed = config.getDouble("hook-speed", 2.5)
|
||||||
|
airGrappleEnabled = config.getBoolean("air-grapple-enabled", true)
|
||||||
|
airGrappleStrength = config.getDouble("air-grapple-strength", 1.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
||||||
@@ -139,6 +146,7 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
val lore = ArrayList<String>()
|
val lore = ArrayList<String>()
|
||||||
lore.add("${ChatColor.GRAY}Right-click to launch the hook")
|
lore.add("${ChatColor.GRAY}Right-click to launch the hook")
|
||||||
lore.add("${ChatColor.GRAY}Pull yourself to the hook location")
|
lore.add("${ChatColor.GRAY}Pull yourself to the hook location")
|
||||||
|
lore.add("${ChatColor.YELLOW}Right-click while hook is in air for a quick boost!")
|
||||||
meta?.lore = lore
|
meta?.lore = lore
|
||||||
|
|
||||||
meta?.isUnbreakable = true
|
meta?.isUnbreakable = true
|
||||||
@@ -167,7 +175,7 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
val player = event.player
|
val player = event.player
|
||||||
val item = player.inventory.itemInMainHand
|
val item = player.inventory.itemInMainHand
|
||||||
|
|
||||||
if (!isGrapplingHook(item)) return
|
if (!isGrapplingHook(item) && !isGrapplingHook(player.inventory.itemInOffHand)) return
|
||||||
|
|
||||||
when (event.state) {
|
when (event.state) {
|
||||||
PlayerFishEvent.State.FISHING -> {
|
PlayerFishEvent.State.FISHING -> {
|
||||||
@@ -185,6 +193,9 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
cooldowns[uuid] = System.currentTimeMillis()
|
cooldowns[uuid] = System.currentTimeMillis()
|
||||||
event.hook.velocity = event.hook.velocity.multiply(hookSpeed)
|
event.hook.velocity = event.hook.velocity.multiply(hookSpeed)
|
||||||
player.world.playSound(player.location, Sound.ENTITY_ARROW_SHOOT, 1.0f, 1.0f)
|
player.world.playSound(player.location, Sound.ENTITY_ARROW_SHOOT, 1.0f, 1.0f)
|
||||||
|
|
||||||
|
// Store the active hook for air grapples
|
||||||
|
activeHooks[uuid] = event.hook
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerFishEvent.State.IN_GROUND, PlayerFishEvent.State.CAUGHT_ENTITY -> {
|
PlayerFishEvent.State.IN_GROUND, PlayerFishEvent.State.CAUGHT_ENTITY -> {
|
||||||
@@ -196,29 +207,47 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val direction = hook.location.toVector().subtract(player.location.toVector())
|
// Standard grapple when the hook hits something
|
||||||
var adjustedPullStrength = pullStrength
|
pullPlayerTowards(player, hook.location.toVector(), pullStrength, distance)
|
||||||
if (distance > 15.0) {
|
|
||||||
adjustedPullStrength *= 1.0 + ((distance - 15.0) / 15.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
direction.normalize().multiply(adjustedPullStrength)
|
// Clean up
|
||||||
direction.y = Math.max(0.4, direction.y)
|
activeHooks.remove(player.uniqueId)
|
||||||
player.velocity = direction
|
}
|
||||||
|
|
||||||
if (preventFallDamage) {
|
PlayerFishEvent.State.REEL_IN -> {
|
||||||
recentlyGrappled.add(player.uniqueId)
|
// This is triggered when player right-clicks while the hook is in the air
|
||||||
object : BukkitRunnable() {
|
if (airGrappleEnabled && activeHooks.containsKey(player.uniqueId)) {
|
||||||
override fun run() {
|
val hook = activeHooks[player.uniqueId]
|
||||||
recentlyGrappled.remove(player.uniqueId)
|
if (hook != null && !hook.isDead) {
|
||||||
|
// Air grapple logic - boost in the direction the player is looking
|
||||||
|
val lookDirection = player.location.direction
|
||||||
|
val boost = lookDirection.normalize().multiply(airGrappleStrength)
|
||||||
|
|
||||||
|
// Add a slight upward boost to prevent players from smashing into the ground
|
||||||
|
if (boost.y < 0.2) {
|
||||||
|
boost.y = 0.2
|
||||||
}
|
}
|
||||||
}.runTaskLater(this, 100L)
|
|
||||||
}
|
|
||||||
|
|
||||||
player.world.playSound(player.location, Sound.ENTITY_ZOMBIE_INFECT, 1.0f, 2.0f)
|
player.velocity = boost
|
||||||
|
|
||||||
if (showParticles) {
|
if (preventFallDamage) {
|
||||||
showGrapplingParticles(player, hook)
|
recentlyGrappled.add(player.uniqueId)
|
||||||
|
object : BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
recentlyGrappled.remove(player.uniqueId)
|
||||||
|
}
|
||||||
|
}.runTaskLater(this, 100L)
|
||||||
|
}
|
||||||
|
|
||||||
|
player.world.playSound(player.location, Sound.ENTITY_BAT_TAKEOFF, 1.0f, 1.5f)
|
||||||
|
|
||||||
|
if (showParticles) {
|
||||||
|
showAirGrappleParticles(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
activeHooks.remove(player.uniqueId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +255,34 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun pullPlayerTowards(player: Player, targetLocation: Vector, strength: Double, distance: Double) {
|
||||||
|
val direction = targetLocation.subtract(player.location.toVector())
|
||||||
|
var adjustedPullStrength = strength
|
||||||
|
|
||||||
|
if (distance > 15.0) {
|
||||||
|
adjustedPullStrength *= 1.0 + ((distance - 15.0) / 15.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
direction.normalize().multiply(adjustedPullStrength)
|
||||||
|
direction.y = Math.max(0.4, direction.y)
|
||||||
|
player.velocity = direction
|
||||||
|
|
||||||
|
if (preventFallDamage) {
|
||||||
|
recentlyGrappled.add(player.uniqueId)
|
||||||
|
object : BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
recentlyGrappled.remove(player.uniqueId)
|
||||||
|
}
|
||||||
|
}.runTaskLater(this, 100L)
|
||||||
|
}
|
||||||
|
|
||||||
|
player.world.playSound(player.location, Sound.ENTITY_ZOMBIE_INFECT, 1.0f, 2.0f)
|
||||||
|
|
||||||
|
if (showParticles) {
|
||||||
|
showGrapplingParticles(player, targetLocation.toLocation(player.world))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
fun onEntityDamage(event: EntityDamageEvent) {
|
fun onEntityDamage(event: EntityDamageEvent) {
|
||||||
if (event.entity !is Player) return
|
if (event.entity !is Player) return
|
||||||
@@ -241,25 +298,25 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isGrapplingHook(item: ItemStack): Boolean {
|
private fun isGrapplingHook(item: ItemStack?): Boolean {
|
||||||
if (item.type != Material.FISHING_ROD) return false
|
if (item == null || item.type != Material.FISHING_ROD) return false
|
||||||
val meta = item.itemMeta ?: return false
|
val meta = item.itemMeta ?: return false
|
||||||
return meta.persistentDataContainer.has(grapplingHookKey, PersistentDataType.BYTE)
|
return meta.persistentDataContainer.has(grapplingHookKey, PersistentDataType.BYTE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showGrapplingParticles(player: Player, hook: FishHook) {
|
private fun showGrapplingParticles(player: Player, targetLocation: org.bukkit.Location) {
|
||||||
object : BukkitRunnable() {
|
object : BukkitRunnable() {
|
||||||
var distance = player.location.distance(hook.location)
|
var distance = player.location.distance(targetLocation)
|
||||||
var traveled = 0.0
|
var traveled = 0.0
|
||||||
val particleSpacing = 0.3
|
val particleSpacing = 0.3
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
if (traveled >= distance || !player.isOnline || hook.isDead) {
|
if (traveled >= distance || !player.isOnline) {
|
||||||
cancel()
|
cancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val direction = hook.location.toVector().subtract(player.location.toVector()).normalize()
|
val direction = targetLocation.toVector().subtract(player.location.toVector()).normalize()
|
||||||
val particleLocation = player.location.clone().add(direction.multiply(traveled))
|
val particleLocation = player.location.clone().add(direction.multiply(traveled))
|
||||||
|
|
||||||
player.world.spawnParticle(Particle.CRIT, particleLocation, 2, 0.05, 0.05, 0.05, 0.01)
|
player.world.spawnParticle(Particle.CRIT, particleLocation, 2, 0.05, 0.05, 0.05, 0.01)
|
||||||
@@ -271,4 +328,33 @@ class GrapplingHook : JavaPlugin(), Listener {
|
|||||||
}
|
}
|
||||||
}.runTaskTimer(this, 0L, 1L)
|
}.runTaskTimer(this, 0L, 1L)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private fun showAirGrappleParticles(player: Player) {
|
||||||
|
// Create a swirl of particles around the player
|
||||||
|
object : BukkitRunnable() {
|
||||||
|
var counter = 0
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
if (counter >= 10 || !player.isOnline) {
|
||||||
|
cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a circular pattern
|
||||||
|
for (i in 0 until 8) {
|
||||||
|
val angle = i * Math.PI / 4
|
||||||
|
val x = Math.cos(angle) * 0.7
|
||||||
|
val z = Math.sin(angle) * 0.7
|
||||||
|
val particleLocation = player.location.clone().add(x, counter * 0.1, z)
|
||||||
|
|
||||||
|
player.world.spawnParticle(Particle.CLOUD, particleLocation, 1, 0.05, 0.05, 0.05, 0.01)
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
player.world.spawnParticle(Particle.CRIT, particleLocation, 1, 0.05, 0.05, 0.05, 0.01)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}.runTaskTimer(this, 0L, 2L)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user