Made it follow combat and region restrictions
This commit is contained in:
@@ -84,6 +84,14 @@ public class AdminCommand implements QuickCommand {
|
||||
b.arg("test")
|
||||
.then(
|
||||
b.arg("vector")
|
||||
).then(
|
||||
b.arg("sphere")
|
||||
.then(
|
||||
b.argPosDecimal("radius")
|
||||
.then(
|
||||
b.argPosDecimal("pointDistance")
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -97,7 +105,7 @@ public class AdminCommand implements QuickCommand {
|
||||
|
||||
return false;
|
||||
},(point,block) -> {
|
||||
DisplayUtils.sphere(point.getLoc(),0.3,0.1,0.1,loc -> {
|
||||
DisplayUtils.sphere(point.getLoc(),0.3,0.1,loc -> {
|
||||
DisplayUtils.DUST_PARTICLE_FACTORY.apply(Color.RED,1F).accept(loc);
|
||||
});
|
||||
|
||||
@@ -106,6 +114,16 @@ public class AdminCommand implements QuickCommand {
|
||||
return !p.equals(entity);
|
||||
});
|
||||
}
|
||||
case "sphere" -> {
|
||||
if (!(commandSender instanceof Player p)) return;
|
||||
|
||||
double radius = args.get(2).toDouble();
|
||||
double pointDistance = args.get(3).toDouble();
|
||||
|
||||
DisplayUtils.sphere(p.getLocation(),radius,pointDistance,loc->{
|
||||
DisplayUtils.DUST_PARTICLE_FACTORY.apply(Color.AQUA,1F).accept(loc);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,9 @@ public class AbilityBackend implements Main {
|
||||
}
|
||||
|
||||
public AbstractAbility getAbility(Player player) {
|
||||
return getArmorInfo(player).getAbility();
|
||||
ArmorInfo info = getArmorInfo(player);
|
||||
if (info == null) return null;
|
||||
return info.getAbility();
|
||||
}
|
||||
|
||||
public TrimMaterial getMaterial(Player player) {
|
||||
@@ -249,7 +251,7 @@ public class AbilityBackend implements Main {
|
||||
}
|
||||
|
||||
/**
|
||||
* Force-removes the cooldown for a player's specific trim ability.
|
||||
* Force removes the cooldown for a player's specific trim ability.
|
||||
* Useful for admin commands or testing.
|
||||
*
|
||||
* @param player The player whose cooldown to reset
|
||||
@@ -270,7 +272,7 @@ public class AbilityBackend implements Main {
|
||||
* Checks the ability flag at the useLocation
|
||||
* @param player the player to check
|
||||
* @param useLocation the Bukkit location to check at
|
||||
* @return
|
||||
* @return true if the ability is allowed, false otherwise
|
||||
*/
|
||||
public boolean abilityAllowed(Player player, Location useLocation) {
|
||||
LocalPlayer user = WorldGuardPlugin.inst().wrapPlayer(player);
|
||||
|
||||
@@ -31,7 +31,9 @@ public class BoltAbility extends AbstractAbility implements Main {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
|
||||
return TargetingUtils.areaAffect(caster.getLocation(),range,target-> !main.man().trustBackend.trusts(caster,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()),(target) ->{
|
||||
return TargetingUtils.areaAffect(caster.getLocation(),range,target-> !main.man().trustBackend.trusts(caster,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(caster,target.getLocation()),(target) ->{
|
||||
drawLightning(caster.getEyeLocation(),target.getEyeLocation(),innerBlock,outerBlock);
|
||||
target.damage(damage,DamageSource.builder(DamageType.LIGHTNING_BOLT).withDamageLocation(caster.getEyeLocation()).withDirectEntity(caster).build());
|
||||
if (target instanceof Player t) Text.sendMessage(Text.Pallet.INFO,t,"You have been stunned by {0}'s Bolt!",caster.name());
|
||||
|
||||
@@ -68,6 +68,7 @@ public class CoastAbility extends AbstractAbility {
|
||||
}
|
||||
|
||||
Location centerLocation = TargetingUtils.findGroundLocation(castLocation);
|
||||
if (!main.man().abilityBackend.abilityAllowed(player,centerLocation)) return;
|
||||
World world = centerLocation.getWorld();
|
||||
|
||||
if (world == null) return;
|
||||
|
||||
@@ -5,6 +5,7 @@ import me.trouper.trimserver.server.systems.abilities.MaterialInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.AbstractAbility;
|
||||
import me.trouper.trimserver.server.systems.abilities.PatternInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.WormEvent;
|
||||
import me.trouper.trimserver.utils.PlayerUtils;
|
||||
import me.trouper.trimserver.utils.TargetingUtils;
|
||||
import me.trouper.trimserver.utils.Text;
|
||||
import me.trouper.trimserver.utils.visual.DisplayUtils;
|
||||
@@ -33,6 +34,7 @@ public class DuneAbility extends AbstractAbility {
|
||||
}
|
||||
|
||||
private void spawnWormSign(Player owner, Location loc) {
|
||||
if (!main.man().abilityBackend.abilityAllowed(owner,loc)) return;
|
||||
AtomicInteger t = new AtomicInteger(0);
|
||||
Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(wave)->{
|
||||
if (t.getAndIncrement() >= 25) {
|
||||
@@ -44,7 +46,10 @@ public class DuneAbility extends AbstractAbility {
|
||||
block.getWorld().playSound(block.getLocation(), Sound.BLOCK_ROOTED_DIRT_BREAK,0.1F,1);
|
||||
block.getWorld().spawnParticle(Particle.BLOCK_CRUMBLE,block.getLocation().add(0,1,0),1,0.5,0,0.5, data);
|
||||
|
||||
TargetingUtils.areaAffect(block.getLocation(),2, target -> !target.isDead() && !main.man().trustBackend.trusts(owner,target), liv->{
|
||||
TargetingUtils.areaAffect(block.getLocation(),2, target -> !target.isDead() &&
|
||||
!main.man().trustBackend.trusts(owner,target) &&
|
||||
main.man().abilityBackend.abilityAllowed(owner,target.getLocation()) &&
|
||||
PlayerUtils.combatAllowed(target,owner), liv->{
|
||||
liv.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS,20,4,true,false,false));
|
||||
Vector pullDirection = loc.toVector().subtract(liv.getLocation().toVector()).normalize();
|
||||
Vector pullVelocity = pullDirection.multiply(0.0015);
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.trimserver.server.systems.abilities.trims;
|
||||
import me.trouper.trimserver.server.systems.abilities.MaterialInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.AbstractAbility;
|
||||
import me.trouper.trimserver.server.systems.abilities.PatternInfo;
|
||||
import me.trouper.trimserver.utils.PlayerUtils;
|
||||
import me.trouper.trimserver.utils.SoundPlayer;
|
||||
import me.trouper.trimserver.utils.TargetingUtils;
|
||||
import me.trouper.trimserver.utils.visual.BlockDisplayRaytracer;
|
||||
@@ -58,9 +59,16 @@ public class EyeAbility extends AbstractAbility {
|
||||
public Location laser(Player owner, Location start, Vector direction, double distance) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
|
||||
return CustomDisplayRaytracer.trace(start,direction,distance,1,point ->{
|
||||
SoundPlayer hissSound = new SoundPlayer(point.getLoc(), Sound.BLOCK_LAVA_EXTINGUISH, 1, 1);
|
||||
List<Entity> targets = point.getNearbyEntities(owner,5,true,0.5, target -> target instanceof LivingEntity && !target.isDead() && !main.man().trustBackend.trusts(owner, (LivingEntity) target));
|
||||
List<Entity> targets = point.getNearbyEntities(owner,5,true,0.5, target ->
|
||||
target instanceof LivingEntity &&
|
||||
!target.isDead() &&
|
||||
!main.man().trustBackend.trusts(owner, (LivingEntity) target) &&
|
||||
PlayerUtils.combatAllowed(target, owner) &&
|
||||
main.man().abilityBackend.abilityAllowed(owner,target.getLocation())
|
||||
);
|
||||
targets.forEach(entity -> {
|
||||
if (!(entity instanceof LivingEntity liv) || shaper.activeShellTasks.containsKey(liv.getUniqueId())) return;
|
||||
hissSound.playWithin(30);
|
||||
|
||||
@@ -70,7 +70,7 @@ public class FlowAbility extends AbstractAbility {
|
||||
@Override
|
||||
public void run() {
|
||||
if (System.currentTimeMillis() > endTime || !player.isOnline() ||
|
||||
breeze.isDead() || breeze.getPassengers().isEmpty()) {
|
||||
breeze.isDead() || breeze.getPassengers().isEmpty() || !main.man().abilityBackend.abilityAllowed(player,player.getLocation())) {
|
||||
endBreezeRide(player, breeze);
|
||||
cancel();
|
||||
return;
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.trimserver.server.systems.abilities.trims;
|
||||
import me.trouper.trimserver.server.systems.abilities.MaterialInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.AbstractAbility;
|
||||
import me.trouper.trimserver.server.systems.abilities.PatternInfo;
|
||||
import me.trouper.trimserver.utils.PlayerUtils;
|
||||
import me.trouper.trimserver.utils.TargetingUtils;
|
||||
import me.trouper.trimserver.utils.Text;
|
||||
import me.trouper.trimserver.utils.visual.BlockDisplayRaytracer;
|
||||
@@ -30,8 +31,8 @@ public class RaiserAbility extends AbstractAbility {
|
||||
|
||||
private final Map<UUID, BukkitTask> levitationTasks = new HashMap<>();
|
||||
|
||||
public void raiseEntity(LivingEntity target) {
|
||||
if (target == null) return;
|
||||
public void raiseEntity(Player caster, LivingEntity target) {
|
||||
if (target == null || !PlayerUtils.combatAllowed(target,caster)) return;
|
||||
|
||||
UUID uuid = target.getUniqueId();
|
||||
|
||||
@@ -45,21 +46,17 @@ public class RaiserAbility extends AbstractAbility {
|
||||
int frequency = 2;
|
||||
int duration = 20 * 7;
|
||||
|
||||
// --- Start Up Effects ---
|
||||
target.getWorld().playSound(target.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.8f, 1.0f);
|
||||
BlockDisplay hook = BlockDisplayRaytracer.trace(Material.IRON_BLOCK,target.getEyeLocation(),target.getLocation().add(0,128,0),0.3,60);
|
||||
DisplayUtils.wave(target.getLocation(),2,Color.WHITE,1,0.1);
|
||||
// Give an initial upward push
|
||||
target.setVelocity(new Vector(0, 0.4, 0));
|
||||
|
||||
// --- The Main Repeating Task (Holding, Checking, Effects) ---
|
||||
BukkitTask task = new BukkitRunnable() {
|
||||
int ticksElapsed = 0;
|
||||
final int durationTicks = duration / frequency;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Check if player is still valid and online
|
||||
if (target.isDead()) {
|
||||
endLevitation(uuid);
|
||||
return;
|
||||
@@ -68,20 +65,17 @@ public class RaiserAbility extends AbstractAbility {
|
||||
Location currentLocation = target.getLocation();
|
||||
double currentHealth = target.getHealth();
|
||||
|
||||
// --- Check Conditions to End ---
|
||||
// 1. Health threshold reached
|
||||
|
||||
if (currentHealth <= healthThreshold) {
|
||||
endLevitation(uuid);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Time limit reached
|
||||
if (ticksElapsed >= durationTicks) {
|
||||
endLevitation(uuid);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Hold Player at Target Height ---
|
||||
if (currentLocation.getY() >= targetY) {
|
||||
Vector currentVelocity = target.getVelocity();
|
||||
target.setVelocity(new Vector(currentVelocity.getX(), 0, currentVelocity.getZ()));
|
||||
@@ -144,7 +138,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean amethystAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Copper ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -152,7 +151,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean copperAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Diamond ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -160,7 +164,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean diamondAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Emerald ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -168,7 +177,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean emeraldAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Gold ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -176,7 +190,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean goldAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Iron ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -184,7 +203,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean ironAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Lapis ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -192,13 +216,22 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean lapisAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Netherite ", description = "Pins your enemy 4 blocks into the air for 6 seconds. Can raise those with Shaper ability activated too.", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean netheriteAbility(Player player) {
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Quartz ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -206,7 +239,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean quartzAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Redstone ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -214,7 +252,12 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean redstoneAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Resin ", description = "Pins your enemy 4 blocks into the air for 6 seconds, or until they loose half their current health", cooldownTicks = 20 * 45)
|
||||
@@ -222,6 +265,11 @@ public class RaiserAbility extends AbstractAbility {
|
||||
public boolean resinAbility(Player player) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target -> !main.man().trustBackend.trusts(player,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()), this::raiseEntity);
|
||||
return TargetingUtils.areaAffect(player.getLocation(),15,target ->
|
||||
!main.man().trustBackend.trusts(player,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
main.man().abilityBackend.abilityAllowed(player,target.getLocation()), ent->{
|
||||
raiseEntity(player,ent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,11 @@ import org.bukkit.inventory.meta.trim.TrimPattern;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Transformation;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Quaternionf;
|
||||
|
||||
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@@ -36,7 +33,7 @@ public class RibAbility extends AbstractAbility {
|
||||
super(TrimPattern.RIB);
|
||||
}
|
||||
|
||||
private void executeResinEruption(Player caster, Material activatingTrimMaterial, int spikeCount, double areaRadius, int activeDurationTicks, Material groundParticleMaterial) {
|
||||
private void executeEruption(Player caster, Material activatingTrimMaterial, int spikeCount, double areaRadius, int activeDurationTicks, Material groundParticleMaterial) {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
|
||||
@@ -49,11 +46,16 @@ public class RibAbility extends AbstractAbility {
|
||||
caster.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH,10*20,1,true));
|
||||
caster.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION,5*20,2,true));
|
||||
|
||||
DisplayUtils.sphereWave(caster.getEyeLocation(),10,0.5,2,2,(point)->{
|
||||
DisplayUtils.sphereWave(caster.getEyeLocation(),10,0.5,2,(point)->{
|
||||
point.getWorld().spawnParticle(Particle.FLAME,point,1,0.1,0.1,0.1,0.01);
|
||||
point.getWorld().spawnParticle(Particle.SMOKE,point,1,0.1,0.1,0.1,0.1);
|
||||
point.getWorld().spawnParticle(Particle.CAMPFIRE_COSY_SMOKE,point,1,0.1,0.1,0.1,0.1);
|
||||
TargetingUtils.areaAffect(point,1, liv->!liv.equals(caster) && !liv.isDead() && !main.man().trustBackend.trusts(caster,liv) && !shaper.activeShellTasks.containsKey(liv.getUniqueId()), target->{
|
||||
TargetingUtils.areaAffect(point,1, liv-> !liv.equals(caster) &&
|
||||
!liv.isDead() &&
|
||||
!main.man().trustBackend.trusts(caster,liv) &&
|
||||
!shaper.activeShellTasks.containsKey(liv.getUniqueId())
|
||||
&& main.man().abilityBackend.abilityAllowed(caster, liv.getLocation()),
|
||||
target->{
|
||||
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS,20*20,1,true,false,false));
|
||||
target.setFireTicks(20*20);
|
||||
});
|
||||
@@ -103,8 +105,10 @@ public class RibAbility extends AbstractAbility {
|
||||
}
|
||||
|
||||
|
||||
final Location finalSpikeBase = groundSurfaceLoc.clone();
|
||||
final Location spikeTipLoc = finalSpikeBase.clone().add(0, spikeVisualHeight, 0);
|
||||
final Location finalSpikeBase = groundSurfaceLoc.clone(); // BOTTOM
|
||||
final Location spikeTipLoc = finalSpikeBase.clone().add(0, spikeVisualHeight, 0); // TOP
|
||||
|
||||
if (!main.man().abilityBackend.abilityAllowed(caster,finalSpikeBase)) continue;
|
||||
|
||||
BlockDisplay spikeDisplay = world.spawn(finalSpikeBase, BlockDisplay.class, bd -> {
|
||||
bd.setBlock(spikeBlockData);
|
||||
@@ -204,77 +208,77 @@ public class RibAbility extends AbstractAbility {
|
||||
@MaterialInfo(name = "Eruption (Amethyst)", description = "Erupts spikes of amethyst", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean amethystAbility(Player player) {
|
||||
executeResinEruption(player, Material.AMETHYST_BLOCK, 10, 5.5, 20 * 5, Material.SMOOTH_BASALT);
|
||||
executeEruption(player, Material.AMETHYST_BLOCK, 10, 5.5, 20 * 5, Material.SMOOTH_BASALT);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Copper)", description = "Erupts spikes of oxidized copper", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean copperAbility(Player player) {
|
||||
executeResinEruption(player, Material.RAW_COPPER, 10, 5.0, 20 * 5, Material.TUFF);
|
||||
executeEruption(player, Material.RAW_COPPER, 10, 5.0, 20 * 5, Material.TUFF);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Diamond)", description = "Erupts spikes of diamond", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean diamondAbility(Player player) {
|
||||
executeResinEruption(player, Material.DEEPSLATE_DIAMOND_ORE, 12, 6.0, 20 * 6, Material.DEEPSLATE);
|
||||
executeEruption(player, Material.DEEPSLATE_DIAMOND_ORE, 12, 6.0, 20 * 6, Material.DEEPSLATE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Emerald)", description = "Erupts spikes of emerald", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean emeraldAbility(Player player) {
|
||||
executeResinEruption(player, Material.DEEPSLATE_EMERALD_ORE, 12, 6.0, 20 * 6, Material.MOSS_BLOCK);
|
||||
executeEruption(player, Material.DEEPSLATE_EMERALD_ORE, 12, 6.0, 20 * 6, Material.MOSS_BLOCK);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Gold)", description = "Erupts spikes of gold", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean goldAbility(Player player) {
|
||||
executeResinEruption(player, Material.DEEPSLATE_GOLD_ORE, 10, 5.0, 20 * 5, Material.NETHER_GOLD_ORE);
|
||||
executeEruption(player, Material.DEEPSLATE_GOLD_ORE, 10, 5.0, 20 * 5, Material.NETHER_GOLD_ORE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Iron)", description = "Erupts spikes of iron", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean ironAbility(Player player) {
|
||||
executeResinEruption(player, Material.RAW_IRON_BLOCK, 11, 5.5, 20 * 5, Material.RAW_IRON_BLOCK);
|
||||
executeEruption(player, Material.RAW_IRON_BLOCK, 11, 5.5, 20 * 5, Material.RAW_IRON_BLOCK);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Lapis)", description = "Erupts spikes of lapis", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean lapisAbility(Player player) {
|
||||
executeResinEruption(player, Material.LAPIS_BLOCK, 10, 5.0, 20 * 5, Material.CLAY);
|
||||
executeEruption(player, Material.LAPIS_BLOCK, 10, 5.0, 20 * 5, Material.CLAY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Netherite)", description = "Erupts deadly blackstone spikes", cooldownTicks = 20 * 20)
|
||||
@Override
|
||||
public boolean netheriteAbility(Player player) {
|
||||
executeResinEruption(player, Material.BLACKSTONE, 20, 10.0, 20 * 8, Material.BLACKSTONE);
|
||||
executeEruption(player, Material.BLACKSTONE, 20, 10.0, 20 * 8, Material.BLACKSTONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Quartz)", description = "Erupts spikes of quartz", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean quartzAbility(Player player) {
|
||||
executeResinEruption(player, Material.QUARTZ_BLOCK, 11, 5.5, 20 * 5, Material.NETHER_QUARTZ_ORE);
|
||||
executeEruption(player, Material.QUARTZ_BLOCK, 11, 5.5, 20 * 5, Material.NETHER_QUARTZ_ORE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Redstone)", description = "Erupts energized redstone spikes", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean redstoneAbility(Player player) {
|
||||
executeResinEruption(player, Material.REDSTONE_BLOCK, 11, 5.5, 20 * 5, Material.REDSTONE_ORE);
|
||||
executeEruption(player, Material.REDSTONE_BLOCK, 11, 5.5, 20 * 5, Material.REDSTONE_ORE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Eruption (Resin)", description = "Erupts spikes of hardened resin from the ground", cooldownTicks = 20 * 30)
|
||||
@Override
|
||||
public boolean resinAbility(Player player) {
|
||||
executeResinEruption(player, Material.RESIN_BLOCK, 12, 6.0, 20 * 6, Material.NETHERRACK);
|
||||
executeEruption(player, Material.RESIN_BLOCK, 12, 6.0, 20 * 6, Material.NETHERRACK);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package me.trouper.trimserver.server.systems.abilities.trims;
|
||||
import me.trouper.trimserver.server.systems.abilities.MaterialInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.AbstractAbility;
|
||||
import me.trouper.trimserver.server.systems.abilities.PatternInfo;
|
||||
import me.trouper.trimserver.utils.PlayerUtils;
|
||||
import me.trouper.trimserver.utils.SoundPlayer;
|
||||
import me.trouper.trimserver.utils.TargetingUtils;
|
||||
import me.trouper.trimserver.utils.Text;
|
||||
@@ -41,7 +42,6 @@ public class SentryAbility extends AbstractAbility {
|
||||
Location spawn = loc.clone().subtract(0,2,0);
|
||||
List<Entity> turretParts = new ArrayList<>();
|
||||
|
||||
// 1) Spawn the rotating turret (dispenser model)
|
||||
BlockDisplay turret = w.spawn(spawn.clone().add(0.5, 1.5, 0.5), BlockDisplay.class, display -> {
|
||||
display.setBlock(turretMat.createBlockData());
|
||||
display.setBrightness(new Display.Brightness(15, 15));
|
||||
@@ -49,11 +49,8 @@ public class SentryAbility extends AbstractAbility {
|
||||
display.setInterpolationDuration(2);
|
||||
display.addScoreboardTag("$/TrimServer/ Temp");
|
||||
|
||||
// Get the current transformation
|
||||
Transformation transformation = display.getTransformation();
|
||||
// Set the translation to center the pivot point
|
||||
transformation.getTranslation().set(-0.5f, -0.5f, -0.5f);
|
||||
// Apply the modified transformation back to the display
|
||||
display.setTransformation(transformation);
|
||||
});
|
||||
|
||||
@@ -69,9 +66,8 @@ public class SentryAbility extends AbstractAbility {
|
||||
|
||||
turretParts.add(dummy);
|
||||
|
||||
// 2) Legs: four converging from a 1×1 square around base up to head-pole
|
||||
double legHeight = 1.5;
|
||||
double halfSize = 0.6; // distance from center
|
||||
double halfSize = 0.6;
|
||||
List<BlockDisplay> stand = new ArrayList<>();
|
||||
for (double dx : new double[]{ -halfSize, +halfSize }) {
|
||||
for (double dz : new double[]{ -halfSize, +halfSize }) {
|
||||
@@ -90,7 +86,6 @@ public class SentryAbility extends AbstractAbility {
|
||||
|
||||
turretParts.addAll(stand);
|
||||
|
||||
// 3) Ammo display above head
|
||||
TextDisplay meter = w.spawn(spawn.clone().add(0.5, 2.5, 0.5), TextDisplay.class, t -> {
|
||||
t.setBillboard(Display.Billboard.CENTER);
|
||||
t.setRotation(0, 90);
|
||||
@@ -116,7 +111,11 @@ public class SentryAbility extends AbstractAbility {
|
||||
String bar = Text.generateProgressBar(10, maxAmmo, chamber);
|
||||
meter.text(Text.color("%s's Sentry\n".formatted(owner.getName()) + "Ammo " + bar));
|
||||
|
||||
Optional<Player> target = TargetingUtils.getClosestPlayer(finalLoc,15,p -> !p.isDead() && !p.equals(owner) && !main.man().trustBackend.trusts(owner,p) && !shaper.activeShellTasks.containsKey(p.getUniqueId()));
|
||||
Optional<Player> target = TargetingUtils.getClosestPlayer(finalLoc,15,p -> !p.isDead() &&
|
||||
!p.equals(owner) &&
|
||||
!main.man().trustBackend.trusts(owner,p) &&
|
||||
!shaper.activeShellTasks.containsKey(p.getUniqueId()) &&
|
||||
PlayerUtils.combatAllowed(p,owner));
|
||||
|
||||
if (target.isPresent()) {
|
||||
Player tracked = target.get();
|
||||
|
||||
@@ -53,7 +53,14 @@ public class SilenceAbility extends AbstractAbility {
|
||||
|
||||
CustomDisplayRaytracer.traceWithReflection(chestLocation,direction,30,0.5,4,point->{
|
||||
point.getWorld().spawnParticle(Particle.SONIC_BOOM, point.getLoc(), 1, 0, 0, 0, 0);
|
||||
List<Entity> targets = point.getNearbyEntities(player,5,true,0.5, entity -> entity instanceof LivingEntity && !entity.equals(player) && !entity.isDead() && !main.man().trustBackend.trusts(player.getUniqueId(),entity.getUniqueId()) && !shaper.activeShellTasks.containsKey(entity.getUniqueId()));
|
||||
List<Entity> targets = point.getNearbyEntities(player,5,true,0.5, entity ->
|
||||
entity instanceof LivingEntity &&
|
||||
!entity.equals(player) &&
|
||||
!entity.isDead() &&
|
||||
!main.man().trustBackend.trusts(player.getUniqueId(),entity.getUniqueId()) &&
|
||||
!shaper.activeShellTasks.containsKey(entity.getUniqueId()) &&
|
||||
PlayerUtils.combatAllowed(entity,player)
|
||||
);
|
||||
targets.forEach(target -> {
|
||||
PlayerUtils.dealTrueDamage((LivingEntity) target, DamageSource.builder(DamageType.SONIC_BOOM).withDirectEntity(player).build(), 10);
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.trimserver.server.systems.abilities.trims;
|
||||
import me.trouper.trimserver.server.systems.abilities.MaterialInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.PatternInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.AbstractAbility;
|
||||
import me.trouper.trimserver.utils.PlayerUtils;
|
||||
import me.trouper.trimserver.utils.SoundPlayer;
|
||||
import me.trouper.trimserver.utils.TargetingUtils;
|
||||
import me.trouper.trimserver.utils.visual.DisplayUtils;
|
||||
@@ -30,7 +31,7 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
private final Map<UUID, List<Entity>> activeSpires = new HashMap<>();
|
||||
private final Map<UUID, BukkitTask> activeTasks = new HashMap<>();
|
||||
private final Set<UUID> activeProjectiles = new HashSet<>();
|
||||
private final Map<UUID, UUID> activeProjectiles = new HashMap<>();
|
||||
|
||||
public SpireAbility() {
|
||||
super(TrimPattern.SPIRE);
|
||||
@@ -40,7 +41,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
|
||||
// Cancel any existing spire for this player
|
||||
if (activeTasks.containsKey(player.getUniqueId())) {
|
||||
activeTasks.get(player.getUniqueId()).cancel();
|
||||
activeTasks.remove(player.getUniqueId());
|
||||
@@ -57,18 +57,14 @@ public class SpireAbility extends AbstractAbility {
|
||||
Location baseLoc = player.getLocation().clone();
|
||||
Location topLoc = baseLoc.clone().add(0, spireHeight, 0);
|
||||
|
||||
// Store entities for cleanup
|
||||
List<Entity> entities = new ArrayList<>();
|
||||
activeSpires.put(player.getUniqueId(), entities);
|
||||
|
||||
// Start with initial effects
|
||||
SoundPlayer startSound = new SoundPlayer(baseLoc, Sound.BLOCK_END_PORTAL_SPAWN, 1.0f, 0.8f);
|
||||
startSound.playWithin(10);
|
||||
|
||||
// Create the blocks for the spire (cone shape)
|
||||
createSpireStructure(player, baseLoc, spireHeight, material, entities);
|
||||
|
||||
// Teleport player to the top
|
||||
Bukkit.getScheduler().runTaskLater(main.getPlugin(), () -> {
|
||||
Bat dummy = world.spawn(topLoc.clone().add(0.5,0,0.5),Bat.class,(bat)->{
|
||||
bat.setAI(false);
|
||||
@@ -80,18 +76,14 @@ public class SpireAbility extends AbstractAbility {
|
||||
dummy.addPassenger(player);
|
||||
}, 10L);
|
||||
|
||||
// Create the barrier sphere
|
||||
double sphereRadius = spireHeight * 0.75;
|
||||
double sphereRadius = spireHeight * 1.25;
|
||||
|
||||
// Create initial sphere particle effect
|
||||
Color sphereColor = getColor(material);
|
||||
Consumer<Location> particleAction = DisplayUtils.DUST_PARTICLE_FACTORY.apply(sphereColor, 1.2f);
|
||||
|
||||
// Create expanding sphere effect
|
||||
DisplayUtils.sphereWave(baseLoc.clone().add(0, sphereRadius/2, 0),
|
||||
sphereRadius, 0.5, 0.5, 0.5, particleAction);
|
||||
sphereRadius, 1, 1, particleAction);
|
||||
|
||||
// Start the main task for the ability
|
||||
BukkitTask task = new BukkitRunnable() {
|
||||
final long endTime = System.currentTimeMillis() + (duration * 1000L);
|
||||
int tickCounter = 0;
|
||||
@@ -99,7 +91,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Check if ability should end
|
||||
if (System.currentTimeMillis() > endTime || !player.isOnline()) {
|
||||
endEnderStorm(player);
|
||||
cancel();
|
||||
@@ -108,15 +99,16 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
tickCounter++;
|
||||
|
||||
// Every 10 ticks, refresh the sphere particles
|
||||
if (tickCounter % 10 == 0) {
|
||||
DisplayUtils.sphere(sphereCenter, sphereRadius, 1.0, 1.0, loc ->
|
||||
world.spawnParticle(Particle.PORTAL, loc, 1, 0, 0, 0, 0));
|
||||
DisplayUtils.sphere(sphereCenter, sphereRadius, 2, loc -> {
|
||||
world.spawnParticle(Particle.PORTAL, loc, 1, 0, 0, 0, 0);
|
||||
world.spawnParticle(Particle.BLOCK, loc, 1, 0, 0, 0, 0,Material.NETHER_PORTAL.createBlockData());
|
||||
});
|
||||
}
|
||||
|
||||
// Every 5 ticks, check for players in the sphere and push them inward if they're near the edge
|
||||
// check for players in the sphere and push them inward if they're near the edge
|
||||
if (tickCounter % 5 == 0) {
|
||||
TargetingUtils.areaAffect(sphereCenter,sphereRadius,target -> !main.man().trustBackend.trusts(player,target),target -> {
|
||||
TargetingUtils.areaAffect(sphereCenter,sphereRadius,target -> !main.man().trustBackend.trusts(player,target) && PlayerUtils.combatAllowed(target,player),target -> {
|
||||
double distanceFromCenter = target.getLocation().distance(sphereCenter);
|
||||
if (distanceFromCenter > sphereRadius * 0.8) {
|
||||
Vector pushDirection = sphereCenter.clone()
|
||||
@@ -129,10 +121,13 @@ public class SpireAbility extends AbstractAbility {
|
||||
});
|
||||
}
|
||||
|
||||
// Fire shulker bullets every 15 ticks (0.75 seconds)
|
||||
if (tickCounter % 15 == 0) {
|
||||
List<Player> targets = world.getNearbyEntities(sphereCenter, sphereRadius, sphereRadius, sphereRadius).stream()
|
||||
.filter(entity -> entity instanceof Player && !entity.equals(player) && !main.man().trustBackend.trusts(player.getUniqueId(), entity.getUniqueId()) && !shaper.activeShellTasks.containsKey(entity.getUniqueId()))
|
||||
.filter(entity -> entity instanceof Player &&
|
||||
!entity.equals(player) &&
|
||||
!main.man().trustBackend.trusts(player.getUniqueId(), entity.getUniqueId()) &&
|
||||
!shaper.activeShellTasks.containsKey(entity.getUniqueId()) &&
|
||||
PlayerUtils.combatAllowed(entity,player))
|
||||
.map(entity -> (Player) entity)
|
||||
.toList();
|
||||
|
||||
@@ -144,17 +139,19 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
ShulkerBullet bullet = (ShulkerBullet) world.spawnEntity(fireLocation, EntityType.SHULKER_BULLET);
|
||||
bullet.setTarget(target);
|
||||
activeProjectiles.add(bullet.getUniqueId());
|
||||
bullet.setShooter(player);
|
||||
activeProjectiles.put(bullet.getUniqueId(),player.getUniqueId());
|
||||
|
||||
world.spawnParticle(Particle.END_ROD, fireLocation,
|
||||
10, 0.2, 0.2, 0.2, 0.1);
|
||||
SoundPlayer bulletSound = new SoundPlayer(fireLocation, Sound.ENTITY_SHULKER_SHOOT, 1.0f, 1.0f);
|
||||
SoundPlayer bulletSound2 = new SoundPlayer(fireLocation, Sound.ENTITY_EVOKER_CAST_SPELL, 1.0f, 1.0f);
|
||||
bulletSound.playWithin(20);
|
||||
bulletSound2.playWithin(20);
|
||||
entities.add(bullet);
|
||||
}
|
||||
}
|
||||
|
||||
// Every 20 ticks, create some ambient particles around the spire
|
||||
if (tickCounter % 10 == 0) {
|
||||
for (int y = 0; y < spireHeight; y += 2) {
|
||||
Location particleLoc = baseLoc.clone().add(0, y, 0);
|
||||
@@ -187,12 +184,10 @@ public class SpireAbility extends AbstractAbility {
|
||||
World world = player.getWorld();
|
||||
BlockData blockData = material.createBlockData();
|
||||
|
||||
// Create the main spire body (cone shape)
|
||||
// Spire Cone
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Calculate radius at this height (decreasing as y increases)
|
||||
double radius = 2.0 * (1 - y / (double) height);
|
||||
|
||||
// Create circle of blocks
|
||||
if (radius > 0.1) {
|
||||
int blocks = Math.max(4, (int) (2 * Math.PI * radius / 0.5));
|
||||
double angleStep = 360.0 / blocks;
|
||||
@@ -204,7 +199,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
Location blockLoc = baseLoc.clone().add(x, y, z);
|
||||
|
||||
// Create Block Display
|
||||
BlockDisplay display = world.spawn(blockLoc, BlockDisplay.class, b -> {
|
||||
b.setBlock(blockData);
|
||||
b.setBrightness(new Display.Brightness(15, 15));
|
||||
@@ -222,7 +216,7 @@ public class SpireAbility extends AbstractAbility {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a platform at the top
|
||||
// Platform
|
||||
double topRadius = 1;
|
||||
int platformBlocks = 12;
|
||||
double angleStep = 360.0 / platformBlocks;
|
||||
@@ -234,7 +228,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
Location blockLoc = baseLoc.clone().add(x-1, height, z);
|
||||
|
||||
// Create Block Display for platform
|
||||
BlockDisplay display = world.spawn(blockLoc, BlockDisplay.class, b -> {
|
||||
b.setBlock(blockData);
|
||||
b.setBrightness(new Display.Brightness(15, 15));
|
||||
@@ -245,7 +238,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
entities.add(display);
|
||||
}
|
||||
|
||||
// Create center block for platform
|
||||
BlockDisplay centerDisplay = world.spawn(baseLoc.clone().add(0, height, 0), BlockDisplay.class, b -> {
|
||||
b.setBlock(blockData);
|
||||
b.setBrightness(new Display.Brightness(15, 15));
|
||||
@@ -254,28 +246,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
entities.add(centerDisplay);
|
||||
|
||||
// Add some decorative elements - crystal-like structures on top
|
||||
for (int i = 0; i < 4; i++) {
|
||||
double angle = i * 90;
|
||||
double x = Math.cos(Math.toRadians(angle)) * 1.5;
|
||||
double z = Math.sin(Math.toRadians(angle)) * 1.5;
|
||||
|
||||
Location crystalBase = baseLoc.clone().add(x, height + 0.5, z);
|
||||
|
||||
// Add a crystal spike using tracing
|
||||
BlockDisplay crystal = BlockDisplayRaytracer.trace(
|
||||
Material.WHITE_STAINED_GLASS,
|
||||
crystalBase,
|
||||
new Vector(0, 2, 0),
|
||||
0.2,
|
||||
2.0,
|
||||
20 * 60
|
||||
);
|
||||
|
||||
entities.add(crystal);
|
||||
}
|
||||
|
||||
// Rising animation for the spire
|
||||
AtomicInteger animStep = new AtomicInteger(0);
|
||||
BukkitTask animTask = new BukkitRunnable() {
|
||||
@Override
|
||||
@@ -286,7 +256,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rising particles
|
||||
for (int y = 0; y < step * height / 10; y++) {
|
||||
Location particleLoc = baseLoc.clone().add(0, y, 0);
|
||||
world.spawnParticle(Particle.END_ROD, particleLoc,
|
||||
@@ -299,8 +268,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(main.getPlugin(), 0L, 2L);
|
||||
|
||||
entities.add(null); // Placeholder to ensure the task is recognized as an entity for cleanup
|
||||
}
|
||||
|
||||
private void endEnderStorm(Player player) {
|
||||
@@ -312,12 +279,10 @@ public class SpireAbility extends AbstractAbility {
|
||||
if (activeSpires.containsKey(player.getUniqueId())) {
|
||||
List<Entity> entities = activeSpires.get(player.getUniqueId());
|
||||
|
||||
// Create disintegration effect
|
||||
if (!entities.isEmpty()) {
|
||||
Location baseLoc = entities.get(0).getLocation();
|
||||
World world = baseLoc.getWorld();
|
||||
|
||||
// Disintegration particles
|
||||
for (Entity entity : entities) {
|
||||
if (entity instanceof Display) {
|
||||
Location loc = entity.getLocation();
|
||||
@@ -340,25 +305,29 @@ public class SpireAbility extends AbstractAbility {
|
||||
|
||||
private Color getColor(Material material) {
|
||||
return switch (material) {
|
||||
case AMETHYST_BLOCK -> Color.fromRGB(137, 0, 201);
|
||||
case COPPER_BLOCK -> Color.fromRGB(184, 115, 51);
|
||||
case DIAMOND_BLOCK -> Color.fromRGB(51, 235, 203);
|
||||
case EMERALD_BLOCK -> Color.fromRGB(0, 217, 58);
|
||||
case GOLD_BLOCK -> Color.fromRGB(255, 230, 0);
|
||||
case IRON_BLOCK -> Color.fromRGB(216, 216, 216);
|
||||
case LAPIS_BLOCK -> Color.fromRGB(0, 85, 255);
|
||||
case NETHERITE_BLOCK -> Color.fromRGB(66, 66, 76);
|
||||
case QUARTZ_BLOCK -> Color.fromRGB(225, 225, 225);
|
||||
case REDSTONE_BLOCK -> Color.fromRGB(255, 0, 0);
|
||||
case RESIN_BLOCK -> Color.fromRGB(239, 195, 134);
|
||||
default -> Color.fromRGB(128, 0, 128);
|
||||
case AMETHYST_BLOCK -> Color.fromRGB(0x8900C9);
|
||||
case COPPER_BLOCK -> Color.fromRGB(0xB87333);
|
||||
case DIAMOND_BLOCK -> Color.fromRGB(0x33EBCB);
|
||||
case EMERALD_BLOCK -> Color.fromRGB(0x00D93A);
|
||||
case GOLD_BLOCK -> Color.fromRGB(0xFFE600);
|
||||
case IRON_BLOCK -> Color.fromRGB(0xD8D8D8);
|
||||
case LAPIS_BLOCK -> Color.fromRGB(0x0055FF);
|
||||
case NETHERITE_BLOCK -> Color.fromRGB(0x42424C);
|
||||
case QUARTZ_BLOCK -> Color.fromRGB(0xE1E1E1);
|
||||
case REDSTONE_BLOCK -> Color.fromRGB(0xFF0000);
|
||||
case RESIN_BLOCK -> Color.fromRGB(0xEFC386);
|
||||
default -> Color.fromRGB(0x800080);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onProjectileHit(ProjectileHitEvent event) {
|
||||
if (event.getEntity() instanceof ShulkerBullet bullet) {
|
||||
if (activeProjectiles.contains(bullet.getUniqueId())) {
|
||||
if (!(event.getEntity() instanceof ShulkerBullet bullet)) return;
|
||||
if (!activeProjectiles.containsKey(bullet.getUniqueId())) return;
|
||||
if (event.getHitEntity() != null && PlayerUtils.combatAllowed(event.getHitEntity(),Bukkit.getPlayer(activeProjectiles.get(bullet.getUniqueId())))) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
activeProjectiles.remove(bullet.getUniqueId());
|
||||
|
||||
Location hitLoc = bullet.getLocation();
|
||||
@@ -368,8 +337,6 @@ public class SpireAbility extends AbstractAbility {
|
||||
SoundPlayer impactSound = new SoundPlayer(hitLoc, Sound.ENTITY_SHULKER_BULLET_HIT, 1.0f, 1.2f);
|
||||
impactSound.playWithin(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MaterialInfo(name = "Amethyst Ender Storm", description = "Summon a spire that traps players and fires shulker bullets", cooldownTicks = 20*90)
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.trimserver.server.systems.abilities.trims;
|
||||
import me.trouper.trimserver.server.systems.abilities.MaterialInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.AbstractAbility;
|
||||
import me.trouper.trimserver.server.systems.abilities.PatternInfo;
|
||||
import me.trouper.trimserver.utils.PlayerUtils;
|
||||
import me.trouper.trimserver.utils.SoundPlayer;
|
||||
import me.trouper.trimserver.utils.TargetingUtils;
|
||||
import me.trouper.trimserver.utils.visual.DisplayUtils;
|
||||
@@ -38,7 +39,10 @@ public class TideAbility extends AbstractAbility {
|
||||
DisplayUtils.waveFan(caster.getLocation().add(0, 2,0),10,direction,90,0.1, point->{
|
||||
point.getWorld().spawnParticle(Particle.BLOCK_CRUMBLE,point,8,0.1,0.1,0.1,0.1, Material.BLUE_STAINED_GLASS.createBlockData());
|
||||
|
||||
TargetingUtils.areaAffect(point,0.3,5,0.3,target -> !main.man().trustBackend.trusts(caster,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()),target -> {
|
||||
TargetingUtils.areaAffect(point,0.3,5,0.3,target -> !main.man().trustBackend.trusts(caster,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
PlayerUtils.combatAllowed(target,caster)
|
||||
,target -> {
|
||||
SoundPlayer blockSound = new SoundPlayer(target.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 1, 0.5F);
|
||||
Vector dir = target.getLocation().toVector().subtract(caster.getLocation().toVector()).normalize();
|
||||
double strength = 0.5;
|
||||
|
||||
@@ -62,7 +62,6 @@ public class WardAbility extends AbstractAbility {
|
||||
return;
|
||||
}
|
||||
|
||||
// spawn and configure warden
|
||||
Warden warden = (Warden) player.getWorld().spawnEntity(player.getLocation().subtract(0,3,0), EntityType.WARDEN, CreatureSpawnEvent.SpawnReason.NATURAL);
|
||||
Verbose.send( "Spawned Warden entity %s for %s", warden.getUniqueId(), player.getName());
|
||||
warden.setAI(false);
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.trimserver.server.systems.abilities.trims;
|
||||
import me.trouper.trimserver.server.systems.abilities.MaterialInfo;
|
||||
import me.trouper.trimserver.server.systems.abilities.AbstractAbility;
|
||||
import me.trouper.trimserver.server.systems.abilities.PatternInfo;
|
||||
import me.trouper.trimserver.utils.PlayerUtils;
|
||||
import me.trouper.trimserver.utils.SoundPlayer;
|
||||
import me.trouper.trimserver.utils.TargetingUtils;
|
||||
import me.trouper.trimserver.utils.visual.DisplayUtils;
|
||||
@@ -63,7 +64,11 @@ public class WayfinderAbility extends AbstractAbility {
|
||||
block.setCancelDrop(true);
|
||||
});
|
||||
|
||||
TargetingUtils.areaAffect(caster.getLocation(),10,target -> !target.isDead() && !target.equals(caster) && !main.man().trustBackend.trusts(caster,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()),target->{
|
||||
TargetingUtils.areaAffect(caster.getLocation(),10,target -> !target.isDead() &&
|
||||
!target.equals(caster) &&
|
||||
!main.man().trustBackend.trusts(caster,target) &&
|
||||
!shaper.activeShellTasks.containsKey(target.getUniqueId()) &&
|
||||
PlayerUtils.combatAllowed(target,caster), target->{
|
||||
SoundPlayer hit = new SoundPlayer(target.getLocation(), Sound.ENTITY_EVOKER_FANGS_ATTACK, 1, 2);
|
||||
Vector direction = target.getLocation().toVector().subtract(caster.getEyeLocation().toVector()).normalize();
|
||||
target.setVelocity(direction.multiply(0.5).setY(0.3));
|
||||
|
||||
@@ -32,7 +32,7 @@ public class WildAbility extends AbstractAbility {
|
||||
AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER);
|
||||
ShaperAbility shaper = (ShaperAbility) shaperInstance;
|
||||
|
||||
if (caster == null || !caster.isOnline() || target == null || !target.isOnline() || caster.equals(target)) return;
|
||||
if (caster == null || !caster.isOnline() || target == null || !target.isOnline() || caster.equals(target) || !PlayerUtils.combatAllowed(target,caster)) return;
|
||||
if (shaper.activeShellTasks.containsKey(target.getUniqueId())) return;
|
||||
if (caster.getWorld() != target.getWorld()) return;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.trouper.trimserver.utils;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
@@ -9,6 +10,7 @@ import com.sk89q.worldguard.protection.regions.RegionQuery;
|
||||
import me.trouper.trimserver.server.Main;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
@@ -85,14 +87,31 @@ public class PlayerUtils implements Main {
|
||||
|
||||
public static void dealTrueDamage(LivingEntity target, DamageSource source, double amount) {
|
||||
if (source.getDirectEntity() instanceof Player a && target instanceof Player t && !combatAllowed(t,a)) return;
|
||||
double newHealth = target.getHealth() - amount;
|
||||
|
||||
target.damage(1, source);
|
||||
|
||||
double newHealth = target.getHealth() - amount;
|
||||
if (newHealth <= 0) {
|
||||
target.setHealth(0);
|
||||
} else {
|
||||
target.setHealth(newHealth);
|
||||
}
|
||||
|
||||
Entity attacker = source.getDirectEntity();
|
||||
if (attacker instanceof LivingEntity) {
|
||||
double dx = target.getX() - attacker.getX();
|
||||
double dz = target.getZ() - attacker.getZ();
|
||||
double magnitude = Math.sqrt(dx * dx + dz * dz);
|
||||
|
||||
if (magnitude > 0) {
|
||||
double strength = 0.4;
|
||||
dx /= magnitude;
|
||||
dz /= magnitude;
|
||||
target.setVelocity(target.getVelocity().add(new Vector(dx * strength, 0.1, dz * strength)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean combatAllowed(Player v, Player a) {
|
||||
LocalPlayer victim = WorldGuardPlugin.inst().wrapPlayer(v);
|
||||
@@ -101,10 +120,31 @@ public class PlayerUtils implements Main {
|
||||
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
|
||||
RegionQuery query = container.createQuery();
|
||||
|
||||
if (!query.testState(victim.getLocation(),victim, Flags.PVP) || !query.testState(attacker.getLocation(),attacker, Flags.PVP)) {
|
||||
if (!query.testState(victim.getLocation(),victim, Flags.PVP)) {
|
||||
main.warning(a,"You cannot attack players protected by regions.");
|
||||
return false;
|
||||
}
|
||||
if (!query.testState(attacker.getLocation(),attacker, Flags.PVP)) {
|
||||
main.warning(a,"You cannot attack players while you are protected by a region.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean combatAllowed(Entity v, Player a) {
|
||||
LocalPlayer attacker = WorldGuardPlugin.inst().wrapPlayer(a);
|
||||
|
||||
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
|
||||
RegionQuery query = container.createQuery();
|
||||
|
||||
if (!query.testState(BukkitAdapter.adapt(v.getLocation()),attacker, Flags.PVP)) {
|
||||
main.warning(a,"You cannot attack entities protected by regions.");
|
||||
return false;
|
||||
}
|
||||
if (!query.testState(attacker.getLocation(),attacker, Flags.PVP)) {
|
||||
main.warning(a,"You cannot attack players while you are protected by a region.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,24 +171,6 @@ public class CustomDisplayRaytracer {
|
||||
BiPredicate<Point, Block> blockReflectCondition,
|
||||
BiPredicate<Point, Entity> entityReflectCondition) {
|
||||
|
||||
/*
|
||||
Raytrace exactly like the non-reflecting methods.
|
||||
However, every point (in addition to the hit condition) have a broad check if both a block, or an entity is hit.
|
||||
If a block is hit, test it and its point against the blockReflectCondition
|
||||
if it should hit, step a point back and use the traceBlockFace method to determine the face to reflect off.
|
||||
get the BlockFace's normal with the getFaceNormal function
|
||||
get the reflection ray's vector with calculateReflection
|
||||
|
||||
|
||||
If an entity is hit, test it and its point against the entityReflectCondition.
|
||||
if it should hit, then step a point back and calculate a glancing reflection with the glanceReflect method.
|
||||
|
||||
Apply the new direction to the raytracer, then skip a point to prevent Immediately reflecting off the same point.
|
||||
|
||||
repeat until running out of distance, running out of max reflections, until the hit condition is satisfied.
|
||||
return the final point processed.
|
||||
|
||||
*/
|
||||
|
||||
if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!");
|
||||
if (distance <= 0) throw new IllegalArgumentException("distance cannot be zero or negative!");
|
||||
@@ -200,27 +182,21 @@ public class CustomDisplayRaytracer {
|
||||
int reflections = 0;
|
||||
|
||||
while (remainingDistance > 0 && reflections <= maxReflections) {
|
||||
// Trace along the current ray direction
|
||||
for (double i = 0.0; i < remainingDistance; i += interval) {
|
||||
Point point = blocksInFrontOf(currentLocation, currentDirection, i, false);
|
||||
|
||||
// Check if the hit condition is satisfied
|
||||
if (hitCondition.test(point)) {
|
||||
return point; // Hit condition met, return the hit point
|
||||
return point;
|
||||
}
|
||||
|
||||
// Check for potential reflection
|
||||
boolean shouldReflect = false;
|
||||
Vector newDirection = null;
|
||||
|
||||
// Check if point hits a block for reflection
|
||||
if (HIT_BLOCK.test(point) && point.getBlock() != null) {
|
||||
Block hitBlock = point.getBlock();
|
||||
if (blockReflectCondition.test(point, hitBlock)) {
|
||||
// Step back slightly to get accurate reflection
|
||||
Point previousPoint = blocksInFrontOf(currentLocation, currentDirection, Math.max(0, i - interval), false);
|
||||
|
||||
// Determine which face was hit
|
||||
BlockFace hitFace = traceBlockFace(previousPoint.getLoc(), currentDirection, interval * 2);
|
||||
|
||||
if (hitFace != null) {
|
||||
@@ -231,12 +207,10 @@ public class CustomDisplayRaytracer {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if point hits an entity for reflection
|
||||
List<Entity> nearbyEntities = point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead());
|
||||
if (!nearbyEntities.isEmpty()) {
|
||||
for (Entity entity : nearbyEntities) {
|
||||
if (entityReflectCondition.test(point, entity)) {
|
||||
// Step back slightly for glancing reflection
|
||||
Point previousPoint = blocksInFrontOf(currentLocation, currentDirection, Math.max(0, i - interval), false);
|
||||
|
||||
newDirection = glanceReflect(currentDirection);
|
||||
@@ -247,41 +221,33 @@ public class CustomDisplayRaytracer {
|
||||
}
|
||||
|
||||
if (shouldReflect) {
|
||||
// Update current location to point before collision
|
||||
double backStep = Math.max(0, i - interval);
|
||||
currentLocation = blocksInFrontOf(currentLocation, currentDirection, backStep, false).getLoc();
|
||||
|
||||
// Update direction to reflection direction
|
||||
currentDirection = newDirection;
|
||||
|
||||
// Update remaining distance
|
||||
remainingDistance -= backStep;
|
||||
|
||||
// Increment reflection counter
|
||||
reflections++;
|
||||
|
||||
// Skip a small amount to prevent immediate reflection off the same object
|
||||
currentLocation = currentLocation.add(currentDirection.clone().multiply(interval * 0.1));
|
||||
remainingDistance -= interval * 0.1;
|
||||
|
||||
break; // Exit the inner loop to restart with new direction
|
||||
break;
|
||||
}
|
||||
|
||||
// If we reached the end without reflecting
|
||||
if (i + interval >= remainingDistance) {
|
||||
Point finalPoint = blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true);
|
||||
return finalPoint;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've used all reflections but didn't reach the hit condition
|
||||
if (reflections > maxReflections) {
|
||||
Point finalPoint = blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true);
|
||||
return finalPoint;
|
||||
}
|
||||
}
|
||||
|
||||
// Should only reach here if we've exhausted all distance
|
||||
return blocksInFrontOf(start, normalizedDir, distance, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,38 +19,38 @@ import java.util.function.Function;
|
||||
public class DisplayUtils implements Main {
|
||||
|
||||
|
||||
public static void sphere(Location center, double radius, double verticalStep, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
for (double yOffset = -radius; yOffset <= radius; yOffset += verticalStep) {
|
||||
double horizontalRadius = Math.sqrt(radius * radius - yOffset * yOffset);
|
||||
public static void sphere(Location center, double radius, double pointDistance, Consumer<Location> action) {
|
||||
double dPhi = pointDistance / radius;
|
||||
|
||||
if (horizontalRadius < 0.01) {
|
||||
Location point = center.clone().add(0, yOffset, 0);
|
||||
action.accept(point);
|
||||
continue;
|
||||
for (double phi = 0.0; phi <= Math.PI; phi += dPhi) {
|
||||
double yOffset = radius * Math.cos(phi);
|
||||
double ringRadius = radius * Math.sin(phi);
|
||||
|
||||
if (ringRadius < 1e-6) {
|
||||
Location loc = center.clone().add(0, yOffset, 0);
|
||||
action.accept(loc);
|
||||
} else {
|
||||
double dTheta = pointDistance / ringRadius;
|
||||
|
||||
for (double theta = 0.0; theta < 2 * Math.PI; theta += dTheta) {
|
||||
double xOffset = ringRadius * Math.cos(theta);
|
||||
double zOffset = ringRadius * Math.sin(theta);
|
||||
|
||||
Location loc = center.clone().add(xOffset, yOffset, zOffset);
|
||||
action.accept(loc);
|
||||
}
|
||||
|
||||
double circumference = 2 * Math.PI * horizontalRadius;
|
||||
int points = Math.max(4, (int) (circumference / maxDistanceBetweenPoints));
|
||||
double angleStep = 360.0 / points;
|
||||
|
||||
for (int i = 0; i < points; i++) {
|
||||
double theta = i * angleStep;
|
||||
double x = Math.cos(Math.toRadians(theta)) * horizontalRadius;
|
||||
double z = Math.sin(Math.toRadians(theta)) * horizontalRadius;
|
||||
Location point = center.clone().add(x, yOffset, z);
|
||||
action.accept(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void sphereWave(Location center, double maxRadius, double radialStep, double verticalStep, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
public static void sphereWave(Location center, double maxRadius, double radialStep, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
AtomicReference<Double> currentRadius = new AtomicReference<>(radialStep);
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(main.getPlugin(), () -> {
|
||||
double r = currentRadius.get();
|
||||
if (r > maxRadius) return;
|
||||
|
||||
sphere(center, r, verticalStep, maxDistanceBetweenPoints, action);
|
||||
sphere(center, r, maxDistanceBetweenPoints, action);
|
||||
currentRadius.set(r + radialStep);
|
||||
}, 0L, 1L);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user