initial
This commit is contained in:
113
.gitignore
vendored
Normal file
113
.gitignore
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# User-specific stuff
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
target/
|
||||||
|
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
.flattened-pom.xml
|
||||||
|
|
||||||
|
# Common working directory
|
||||||
|
run/
|
||||||
87
pom.xml
Normal file
87
pom.xml
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.CrossBowShoot</groupId>
|
||||||
|
<artifactId>crossbowshoot</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>crossbowshoot</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>21</java.version>
|
||||||
|
<kotlin.version>2.2.0-Beta2</kotlin.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<defaultGoal>clean package</defaultGoal>
|
||||||
|
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-maven-plugin</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>compile</id>
|
||||||
|
<phase>compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<jvmTarget>${java.version}</jvmTarget>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.3</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>papermc-repo</id>
|
||||||
|
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>sonatype</id>
|
||||||
|
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.papermc.paper</groupId>
|
||||||
|
<artifactId>paper-api</artifactId>
|
||||||
|
<version>1.21.5-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
371
src/main/kotlin/org/CrossBowShoot/crossBowShoot/CrossBowShoot.kt
Normal file
371
src/main/kotlin/org/CrossBowShoot/crossBowShoot/CrossBowShoot.kt
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
package org.CrossBowShoot.crossBowShoot
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.Sound
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.entity.Projectile
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.entity.EntityDamageEvent
|
||||||
|
import org.bukkit.event.entity.ProjectileHitEvent
|
||||||
|
import org.bukkit.event.entity.ProjectileLaunchEvent
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.metadata.FixedMetadataValue
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import org.bukkit.util.Vector
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class CrossBowShoot : JavaPlugin(), Listener {
|
||||||
|
|
||||||
|
private val rocketJumpEnabled = mutableMapOf<String, Boolean>()
|
||||||
|
private val configFilename = "config.yml"
|
||||||
|
|
||||||
|
override fun onEnable() {
|
||||||
|
// Register event listener
|
||||||
|
server.pluginManager.registerEvents(this, this)
|
||||||
|
|
||||||
|
// Set up config
|
||||||
|
setupConfig()
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
registerCommands()
|
||||||
|
|
||||||
|
// Register a repeating task for particle trails
|
||||||
|
server.scheduler.scheduleSyncRepeatingTask(this, Runnable {
|
||||||
|
handlePlayerTrails()
|
||||||
|
}, 1L, 1L) // Run every tick
|
||||||
|
|
||||||
|
logger.info("CrossBowShoot plugin has been enabled!")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisable() {
|
||||||
|
logger.info("CrossBowShoot plugin has been disabled!")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupConfig() {
|
||||||
|
// Create default config if it doesn't exist
|
||||||
|
saveDefaultConfig()
|
||||||
|
|
||||||
|
// Load config settings
|
||||||
|
val config = config
|
||||||
|
if (!config.contains("settings.default-enabled")) {
|
||||||
|
config.set("settings.default-enabled", true)
|
||||||
|
config.set("settings.knockback-base", 1.5)
|
||||||
|
config.set("settings.knockback-max", 3.2)
|
||||||
|
config.set("settings.immunity-duration", 10)
|
||||||
|
config.set("settings.messages.enabled", "${ChatColor.GREEN}Rocket jump enabled!")
|
||||||
|
config.set("settings.messages.disabled", "${ChatColor.RED}Rocket jump disabled!")
|
||||||
|
config.set("settings.messages.welcome", "${ChatColor.GOLD}Use /rocketjump to toggle CrossbBow Jump.")
|
||||||
|
saveConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerCommands() {
|
||||||
|
// Register the rocketjump command
|
||||||
|
getCommand("rocketjump")?.setExecutor { sender, _, _, args ->
|
||||||
|
if (sender is Player) {
|
||||||
|
val uuid = sender.uniqueId.toString()
|
||||||
|
val isEnabled = rocketJumpEnabled.getOrDefault(uuid, config.getBoolean("settings.default-enabled"))
|
||||||
|
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
// Toggle rocket jump
|
||||||
|
rocketJumpEnabled[uuid] = !isEnabled
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
sender.sendMessage(config.getString("settings.messages.enabled") ?: "${ChatColor.GREEN}Rocket jump enabled!")
|
||||||
|
sender.playSound(sender.location, Sound.BLOCK_NOTE_BLOCK_PLING, 0.5f, 2.0f)
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(config.getString("settings.messages.disabled") ?: "${ChatColor.RED}Rocket jump disabled!")
|
||||||
|
sender.playSound(sender.location, Sound.BLOCK_NOTE_BLOCK_BASS, 0.5f, 0.8f)
|
||||||
|
}
|
||||||
|
} else if (args[0].equals("status", ignoreCase = true)) {
|
||||||
|
// Check status
|
||||||
|
val status = if (isEnabled)
|
||||||
|
"${ChatColor.GREEN}ENABLED"
|
||||||
|
else
|
||||||
|
"${ChatColor.RED}DISABLED"
|
||||||
|
sender.sendMessage("${ChatColor.GOLD}Your rocket jump is currently: $status")
|
||||||
|
}
|
||||||
|
|
||||||
|
return@setExecutor true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||||
|
val player = event.player
|
||||||
|
|
||||||
|
// Send welcome message if it's their first time with the plugin
|
||||||
|
if (!player.hasPlayedBefore() || !player.hasMetadata("crossbow_welcomed")) {
|
||||||
|
// Send welcome message after a short delay
|
||||||
|
server.scheduler.runTaskLater(this, Runnable {
|
||||||
|
player.sendMessage(config.getString("settings.messages.welcome")
|
||||||
|
?: "${ChatColor.GOLD} Use /rocketjump to toggle Crossbow Jump.")
|
||||||
|
|
||||||
|
// Mark as welcomed
|
||||||
|
player.setMetadata("crossbow_welcomed", FixedMetadataValue(this, true))
|
||||||
|
}, 40L) // 2 second delay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePlayerTrails() {
|
||||||
|
// Find players who have recently been launched
|
||||||
|
for (player in server.onlinePlayers) {
|
||||||
|
// Check if player is moving fast enough (probably from knockback)
|
||||||
|
val velocity = player.velocity
|
||||||
|
val speed = velocity.length()
|
||||||
|
|
||||||
|
if (speed > 0.8 && player.hasMetadata("crossbow_launched")) {
|
||||||
|
// How long ago they were launched
|
||||||
|
val launchTime = player.getMetadata("crossbow_launched")[0].asLong()
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
val timeSinceLaunch = currentTime - launchTime
|
||||||
|
|
||||||
|
// Only create trails for 1.5 seconds after launch
|
||||||
|
if (timeSinceLaunch < 1500) {
|
||||||
|
// Add particles behind the player based on their movement direction
|
||||||
|
val oppositeDir = velocity.clone().multiply(-1).normalize()
|
||||||
|
val particleLocation = player.location.clone().add(
|
||||||
|
oppositeDir.x * 0.5,
|
||||||
|
0.0,
|
||||||
|
oppositeDir.z * 0.5
|
||||||
|
)
|
||||||
|
|
||||||
|
// Diminishing particle count over time
|
||||||
|
val particleCount = max(1, (10 * (1500 - timeSinceLaunch) / 1500)).toInt()
|
||||||
|
|
||||||
|
// Smoke trail
|
||||||
|
player.world.spawnParticle(
|
||||||
|
org.bukkit.Particle.SMOKE,
|
||||||
|
particleLocation,
|
||||||
|
particleCount, 0.1, 0.1, 0.1, 0.01
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Clear metadata after effect duration ends
|
||||||
|
player.removeMetadata("crossbow_launched", this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onEntityDamage(event: EntityDamageEvent) {
|
||||||
|
// Check if the entity is a player
|
||||||
|
if (event.entity is Player) {
|
||||||
|
val player = event.entity as Player
|
||||||
|
|
||||||
|
// Check if damage is from fall damage
|
||||||
|
if (event.cause == EntityDamageEvent.DamageCause.FALL) {
|
||||||
|
// Check if player has fall immunity metadata
|
||||||
|
if (player.hasMetadata("fall_immune")) {
|
||||||
|
val immuneUntil = player.getMetadata("fall_immune")[0].asLong()
|
||||||
|
|
||||||
|
// If current time is before immunity expiration
|
||||||
|
if (System.currentTimeMillis() < immuneUntil) {
|
||||||
|
// Cancel the damage
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
// Create a small dust effect to indicate fall damage prevention
|
||||||
|
player.world.spawnParticle(
|
||||||
|
org.bukkit.Particle.CLOUD,
|
||||||
|
player.location.clone().add(0.0, 0.1, 0.0),
|
||||||
|
8, 0.3, 0.0, 0.3, 0.05
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onProjectileLaunch(event: ProjectileLaunchEvent) {
|
||||||
|
val projectile = event.entity
|
||||||
|
val shooter = projectile.shooter
|
||||||
|
|
||||||
|
// Check if shooter is a player
|
||||||
|
if (shooter is Player) {
|
||||||
|
val player = shooter
|
||||||
|
val uuid = player.uniqueId.toString()
|
||||||
|
|
||||||
|
// Check if rocket jumps are enabled for this player
|
||||||
|
val isEnabled = rocketJumpEnabled.getOrDefault(uuid, config.getBoolean("settings.default-enabled"))
|
||||||
|
if (!isEnabled) return
|
||||||
|
|
||||||
|
// Check if player is using a crossbow with firework rockets
|
||||||
|
val mainHand = player.inventory.itemInMainHand
|
||||||
|
val offHand = player.inventory.itemInOffHand
|
||||||
|
|
||||||
|
if (isHoldingCrossbowWithRockets(mainHand, offHand)) {
|
||||||
|
// Store the launch location and direction for later use
|
||||||
|
projectile.setMetadata("crossbowshoot", FixedMetadataValue(this, true))
|
||||||
|
projectile.setMetadata("shooterLocation", FixedMetadataValue(this, player.location.clone()))
|
||||||
|
projectile.setMetadata("shooterDirection", FixedMetadataValue(this, player.location.direction.clone()))
|
||||||
|
|
||||||
|
// Visual indicator that rocket jump is active
|
||||||
|
player.world.spawnParticle(
|
||||||
|
org.bukkit.Particle.FLAME,
|
||||||
|
player.location.clone().add(0.0, 1.8, 0.0),
|
||||||
|
5, 0.1, 0.1, 0.1, 0.02
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onProjectileHit(event: ProjectileHitEvent) {
|
||||||
|
val projectile = event.entity
|
||||||
|
|
||||||
|
// Check if the projectile was fired by our plugin
|
||||||
|
if (projectile.hasMetadata("crossbowshoot") && !projectile.hasMetadata("processed")) {
|
||||||
|
val shooter = projectile.shooter
|
||||||
|
|
||||||
|
// Make sure shooter is still a player
|
||||||
|
if (shooter is Player) {
|
||||||
|
val player = shooter
|
||||||
|
|
||||||
|
// Only apply knockback if the projectile hit something
|
||||||
|
if (event.hitBlock != null || event.hitEntity != null) {
|
||||||
|
// Mark as processed to prevent multiple triggers
|
||||||
|
projectile.setMetadata("processed", FixedMetadataValue(this, true))
|
||||||
|
applyKnockback(player, projectile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyKnockback(player: Player, projectile: Projectile) {
|
||||||
|
// Get config values
|
||||||
|
val baseKnockback = config.getDouble("settings.knockback-base", 1.5)
|
||||||
|
val maxKnockback = config.getDouble("settings.knockback-max", 3.2)
|
||||||
|
val immunityDuration = config.getInt("settings.immunity-duration", 10) * 1000L
|
||||||
|
|
||||||
|
// Get the original shooting location and direction
|
||||||
|
val shooterLocation = projectile.getMetadata("shooterLocation")[0].value() as org.bukkit.Location
|
||||||
|
val shooterDirection = projectile.getMetadata("shooterDirection")[0].value() as Vector
|
||||||
|
|
||||||
|
// Calculate distance between shot location and hit location
|
||||||
|
val distance = shooterLocation.distance(projectile.location)
|
||||||
|
|
||||||
|
// Calculate the knockback strength (higher when shooting close to self)
|
||||||
|
// The closer the shot (smaller distance), the stronger the knockback
|
||||||
|
val knockbackStrength = max(baseKnockback, maxKnockback - min(distance / 10.0, maxKnockback - baseKnockback))
|
||||||
|
|
||||||
|
// Calculate the knockback vector (opposite to shooting direction)
|
||||||
|
val knockbackVector = shooterDirection.clone().multiply(-knockbackStrength)
|
||||||
|
|
||||||
|
// Apply smoother upward force for a more fluid motion
|
||||||
|
// Scale the upward boost based on horizontal motion for natural arc
|
||||||
|
val horizontalSpeed = Math.sqrt(knockbackVector.x * knockbackVector.x + knockbackVector.z * knockbackVector.z)
|
||||||
|
knockbackVector.y = max(0.3, knockbackVector.y + (horizontalSpeed * 0.15))
|
||||||
|
|
||||||
|
// Apply the knockback to the player with a more fluid approach
|
||||||
|
// We'll use a scheduler to apply it after a tiny delay for better feel
|
||||||
|
server.scheduler.runTaskLater(this, Runnable {
|
||||||
|
// Apply velocity
|
||||||
|
player.velocity = knockbackVector
|
||||||
|
|
||||||
|
// Set metadata for player to track trail effect and fall immunity
|
||||||
|
player.setMetadata("crossbow_launched", FixedMetadataValue(this, System.currentTimeMillis()))
|
||||||
|
player.setMetadata("fall_immune", FixedMetadataValue(this, System.currentTimeMillis() + immunityDuration))
|
||||||
|
|
||||||
|
// Create visual explosion effect at player's feet
|
||||||
|
player.world.spawnParticle(
|
||||||
|
org.bukkit.Particle.EXPLOSION,
|
||||||
|
player.location.clone().add(0.0, 0.5, 0.0),
|
||||||
|
1, 0.0, 0.0, 0.0, 0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add flame particles for rocket effect
|
||||||
|
player.world.spawnParticle(
|
||||||
|
org.bukkit.Particle.FLAME,
|
||||||
|
player.location.clone().add(0.0, 1.0, 0.0),
|
||||||
|
15, 0.2, 0.2, 0.2, 0.05
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add smoke trail
|
||||||
|
player.world.spawnParticle(
|
||||||
|
org.bukkit.Particle.SMOKE,
|
||||||
|
player.location.clone().add(0.0, 1.0, 0.0),
|
||||||
|
10, 0.2, 0.2, 0.2, 0.03
|
||||||
|
)
|
||||||
|
|
||||||
|
// Play sound effect (only once)
|
||||||
|
player.world.playSound(player.location, org.bukkit.Sound.ENTITY_GENERIC_EXPLODE, 0.7f, 1.3f)
|
||||||
|
|
||||||
|
// Reset fall distance immediately and periodically
|
||||||
|
player.fallDistance = 0f
|
||||||
|
|
||||||
|
// Create a colored particle ring showing the effect is active
|
||||||
|
createParticleRing(player.location.clone().add(0.0, 0.1, 0.0), 1.0, 16)
|
||||||
|
|
||||||
|
// Schedule periodic fall distance resets
|
||||||
|
var taskId = -1
|
||||||
|
taskId = server.scheduler.scheduleSyncRepeatingTask(this, Runnable {
|
||||||
|
if (player.isOnline && player.hasMetadata("fall_immune")) {
|
||||||
|
val endTime = player.getMetadata("fall_immune")[0].asLong()
|
||||||
|
if (System.currentTimeMillis() < endTime) {
|
||||||
|
// Reset fall distance every tick while immunity is active
|
||||||
|
player.fallDistance = 0f
|
||||||
|
} else {
|
||||||
|
// Immunity expired, remove metadata and cancel task
|
||||||
|
player.removeMetadata("fall_immune", this)
|
||||||
|
if (taskId != -1) {
|
||||||
|
server.scheduler.cancelTask(taskId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (taskId != -1) {
|
||||||
|
// Player offline or metadata removed, cancel task
|
||||||
|
server.scheduler.cancelTask(taskId)
|
||||||
|
}
|
||||||
|
}, 1L, 1L)
|
||||||
|
}, 1L) // 1 tick delay for more natural feel
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createParticleRing(location: org.bukkit.Location, radius: Double, count: Int) {
|
||||||
|
for (i in 0 until count) {
|
||||||
|
val angle = 2 * Math.PI * i / count
|
||||||
|
val x = radius * Math.cos(angle)
|
||||||
|
val z = radius * Math.sin(angle)
|
||||||
|
location.world.spawnParticle(
|
||||||
|
org.bukkit.Particle.FLAME,
|
||||||
|
location.clone().add(x, 0.0, z),
|
||||||
|
1, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
org.bukkit.Particle.DustOptions(org.bukkit.Color.fromRGB(255, 100, 0), 1.0f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isHoldingCrossbowWithRockets(mainHand: ItemStack, offHand: ItemStack): Boolean {
|
||||||
|
// Check if player is holding a crossbow
|
||||||
|
val holdingCrossbow = mainHand.type == Material.CROSSBOW || offHand.type == Material.CROSSBOW
|
||||||
|
|
||||||
|
// Check if player has firework rockets in inventory
|
||||||
|
val hasRockets = mainHand.type == Material.FIREWORK_ROCKET ||
|
||||||
|
offHand.type == Material.FIREWORK_ROCKET ||
|
||||||
|
isCrossbowLoadedWithRocket(mainHand) ||
|
||||||
|
isCrossbowLoadedWithRocket(offHand)
|
||||||
|
|
||||||
|
return holdingCrossbow && hasRockets
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isCrossbowLoadedWithRocket(item: ItemStack): Boolean {
|
||||||
|
if (item.type != Material.CROSSBOW) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check crossbow metadata for loaded projectiles
|
||||||
|
val meta = item.itemMeta
|
||||||
|
if (meta is org.bukkit.inventory.meta.CrossbowMeta) {
|
||||||
|
// Check if crossbow is loaded (has charged projectiles)
|
||||||
|
return meta.hasChargedProjectiles() &&
|
||||||
|
meta.chargedProjectiles.any { it.type == Material.FIREWORK_ROCKET }
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/resources/config.yml
Normal file
18
src/main/resources/config.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# CrossBowShoot Configuration
|
||||||
|
|
||||||
|
settings:
|
||||||
|
# Whether rocket jumps are enabled by default for new players
|
||||||
|
default-enabled: true
|
||||||
|
|
||||||
|
# Knockback values
|
||||||
|
knockback-base: 1.5 # Minimum knockback strength
|
||||||
|
knockback-max: 3.2 # Maximum knockback strength
|
||||||
|
|
||||||
|
# Fall immunity duration in seconds
|
||||||
|
immunity-duration: 10
|
||||||
|
|
||||||
|
# Messages
|
||||||
|
messages:
|
||||||
|
enabled: "&aRocket jump enabled!"
|
||||||
|
disabled: "&cRocket jump disabled!"
|
||||||
|
welcome: "&6Use /rocketjump to toggle Crossbow Jumping!."
|
||||||
16
src/main/resources/plugin.yml
Normal file
16
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: CrossBowShoot
|
||||||
|
version: 1.0-SNAPSHOT
|
||||||
|
main: org.CrossBowShoot.crossBowShoot.CrossBowShoot
|
||||||
|
api-version: 1.21.5
|
||||||
|
description: A plugin that makes players get knocked back when shooting a crossbow with rockets
|
||||||
|
author: YourName
|
||||||
|
commands:
|
||||||
|
rocketjump:
|
||||||
|
description: Toggle the rocket jump functionality
|
||||||
|
usage: /rocketjump [status]
|
||||||
|
aliases: [rj, rocket]
|
||||||
|
permission: crossbowshoot.use
|
||||||
|
permissions:
|
||||||
|
crossbowshoot.use:
|
||||||
|
description: Allows players to use the rocket jump feature
|
||||||
|
default: true
|
||||||
Reference in New Issue
Block a user