Made it follow combat and region restrictions

This commit is contained in:
thetrouper
2025-05-27 19:39:30 -05:00
parent e43b3a3889
commit 913877a22c
19 changed files with 277 additions and 202 deletions

View File

@@ -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);
});
}
}
}

View File

@@ -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);

View File

@@ -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());
@@ -72,7 +74,7 @@ public class BoltAbility extends AbstractAbility implements Main {
SoundPlayer ring = new SoundPlayer(end, Sound.ITEM_TRIDENT_THUNDER,10,1);
SoundPlayer zip = new SoundPlayer(end, Sound.ENTITY_BEE_STING,10,1);
zip.playWithin(30 );
zip.playWithin(30);
bolt.playWithin(50);
ring.playWithin(30);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
});
}
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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);
});

View File

@@ -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;
@@ -233,8 +227,7 @@ public class SpireAbility extends AbstractAbility {
double z = Math.sin(Math.toRadians(angle)) * topRadius;
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));
@@ -244,8 +237,7 @@ 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,35 +305,37 @@ 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())) {
activeProjectiles.remove(bullet.getUniqueId());
Location hitLoc = bullet.getLocation();
hitLoc.getWorld().spawnParticle(Particle.DRAGON_BREATH, hitLoc,
15, 0.2, 0.2, 0.2, 0.1);
SoundPlayer impactSound = new SoundPlayer(hitLoc, Sound.ENTITY_SHULKER_BULLET_HIT, 1.0f, 1.2f);
impactSound.playWithin(5);
}
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();
hitLoc.getWorld().spawnParticle(Particle.DRAGON_BREATH, hitLoc,
15, 0.2, 0.2, 0.2, 0.1);
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)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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));

View File

@@ -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;

View File

@@ -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,15 +87,32 @@ 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);
LocalPlayer attacker = WorldGuardPlugin.inst().wrapPlayer(a);
@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
double circumference = 2 * Math.PI * horizontalRadius;
int points = Math.max(4, (int) (circumference / maxDistanceBetweenPoints));
double angleStep = 360.0 / points;
if (ringRadius < 1e-6) {
Location loc = center.clone().add(0, yOffset, 0);
action.accept(loc);
} else {
double dTheta = pointDistance / ringRadius;
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);
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);
}
}
}
}
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);
}