Skip to content

Commit

Permalink
Merge pull request #881 from TonytheMacaroni/main
Browse files Browse the repository at this point in the history
OrbitSpell additions, bugfixes
  • Loading branch information
Chronoken authored Apr 13, 2024
2 parents a5dea3c + 2a47880 commit 281ecee
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@
import java.util.*;
import java.util.function.Predicate;

import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;

import org.apache.commons.math4.core.jdkmath.AccurateMath;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import org.bukkit.entity.Entity;
import org.bukkit.util.BoundingBox;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.LivingEntity;

import com.nisovin.magicspells.util.*;
import io.papermc.paper.entity.TeleportFlag;

import de.slikey.effectlib.Effect;
import de.slikey.effectlib.effect.ModifiedEffect;

import com.nisovin.magicspells.util.*;
import com.nisovin.magicspells.Subspell;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.spells.TargetedSpell;
Expand All @@ -24,34 +34,38 @@
import com.nisovin.magicspells.spells.TargetedLocationSpell;
import com.nisovin.magicspells.spelleffects.util.EffectlibSpellEffect;

import de.slikey.effectlib.Effect;
import de.slikey.effectlib.effect.ModifiedEffect;

public class OrbitSpell extends TargetedSpell implements TargetedEntitySpell, TargetedLocationSpell {

private static Set<OrbitTracker> trackerSet;

private final ValidTargetList entityTargetList;

private final ConfigData<Double> hitRadius;
private final ConfigData<Double> maxDuration;
private final ConfigData<Double> verticalHitRadius;

private final ConfigData<Integer> immuneTicks;
private final ConfigData<Integer> tickInterval;
private final ConfigData<Integer> vertExpandDelay;
private final ConfigData<Integer> horizExpandDelay;

private final ConfigData<Float> yOffset;
private final ConfigData<Float> hitRadius;
private final ConfigData<Float> yawOffset;
private final ConfigData<Float> angleOffset;
private final ConfigData<Float> orbitRadius;
private final ConfigData<Float> horizOffset;
private final ConfigData<Float> pitchOffset;
private final ConfigData<Float> vertExpandRadius;
private final ConfigData<Float> verticalHitRadius;
private final ConfigData<Float> horizExpandRadius;
private final ConfigData<Float> secondsPerRevolution;

private final ConfigData<Boolean> followYaw;
private final ConfigData<Boolean> followPitch;
private final ConfigData<Boolean> lockStartYaw;
private final ConfigData<Boolean> lockStartPitch;
private final ConfigData<Boolean> stopOnHitEntity;
private final ConfigData<Boolean> stopOnHitGround;
private final ConfigData<Boolean> counterClockwise;
private final ConfigData<Boolean> constantImmuneTicks;
private final ConfigData<Boolean> requireEntityTarget;

private final String orbitSpellName;
Expand All @@ -70,27 +84,37 @@ public OrbitSpell(MagicConfig config, String spellName) {

trackerSet = new HashSet<>();

entityTargetList = new ValidTargetList(this, getConfigStringList("can-hit", null));
if (config.isList(internalKey + "can-hit"))
entityTargetList = new ValidTargetList(this, getConfigStringList("can-hit", null));
else
entityTargetList = new ValidTargetList(this, getConfigString("can-hit", null));

hitRadius = getConfigDataDouble("hit-radius", 1D);
maxDuration = getConfigDataDouble("max-duration", 20);
verticalHitRadius = getConfigDataDouble("vertical-hit-radius", 1D);

immuneTicks = getConfigDataInt("immune-ticks", -1);
tickInterval = getConfigDataInt("tick-interval", 2);
vertExpandDelay = getConfigDataInt("vert-expand-delay", 0);
horizExpandDelay = getConfigDataInt("horiz-expand-delay", 0);

yOffset = getConfigDataFloat("y-offset", 0.6F);
hitRadius = getConfigDataFloat("hit-radius", 1F);
yawOffset = getConfigDataFloat("start-yaw-offset", getConfigDataFloat("start-horiz-offset", 0));
angleOffset = getConfigDataFloat("start-angle-offset", 0);
orbitRadius = getConfigDataFloat("orbit-radius", 1F);
horizOffset = getConfigDataFloat("start-horiz-offset", 0);
pitchOffset = getConfigDataFloat("start-pitch-offset", 0);
vertExpandRadius = getConfigDataFloat("vert-expand-radius", 0);
verticalHitRadius = getConfigDataFloat("vertical-hit-radius", 1F);
horizExpandRadius = getConfigDataFloat("horiz-expand-radius", 0);
secondsPerRevolution = getConfigDataFloat("seconds-per-revolution", 3F);

followYaw = getConfigDataBoolean("follow-yaw", false);
followPitch = getConfigDataBoolean("follow-pitch", false);
lockStartYaw = getConfigDataBoolean("lock-start-yaw", false);
lockStartPitch = getConfigDataBoolean("lock-start-pitch", true);
stopOnHitEntity = getConfigDataBoolean("stop-on-hit-entity", false);
stopOnHitGround = getConfigDataBoolean("stop-on-hit-ground", false);
counterClockwise = getConfigDataBoolean("counter-clockwise", false);
constantImmuneTicks = getConfigDataBoolean("constant-immune-ticks", true);
requireEntityTarget = getConfigDataBoolean("require-entity-target", true);

orbitSpellName = getConfigString("spell", "");
Expand Down Expand Up @@ -187,47 +211,81 @@ private class OrbitTracker implements Runnable {
private boolean stopped = false;

private final BoundingBox box;
private final Vector currentDirection;
private final Location center;
private final Predicate<Location> transparent;

private final Set<LivingEntity> immune;
private final Vector axis;
private final Vector offset;
private final Vector direction;
private final Vector perpendicular;

private final Object2IntMap<UUID> immune;
private final Set<ArmorStand> armorStandSet;
private final Predicate<Location> transparent;
private final Map<SpellEffect, Entity> entityMap;
private final Set<EffectlibSpellEffect> effectSet;

private final boolean followYaw;
private final boolean followPitch;
private final boolean lockStartYaw;
private final boolean lockStartPitch;
private final boolean stopOnHitEntity;
private final boolean stopOnHitGround;
private final boolean counterClockwise;
private final boolean constantImmuneTicks;

private int tickCount;
private final int taskId;
private final int immuneTicks;
private final int ticksPerRevolution;
private final int repeatingVertTaskId;
private final int repeatingHorizTaskId;

private final long startTime;

private float yOffset;
private float previousYaw;
private float orbitRadius;
private final float distancePerTick;

private float previousYaw;
private float previousPitch;
private final float startYaw;
private final float yawOffset;
private final float startPitch;
private final float pitchOffset;

private final double hitRadius;
private final double angleOffset;
private final double maxDuration;
private final double verticalHitRadius;

private OrbitTracker(SpellData data) {
startTime = System.currentTimeMillis();

center = data.location();
previousYaw = center.getYaw();
box = new BoundingBox(center, hitRadius.get(data), verticalHitRadius.get(data));
startYaw = previousYaw = center.getYaw();
startPitch = previousPitch = center.getPitch();

hitRadius = OrbitSpell.this.hitRadius.get(data);
verticalHitRadius = OrbitSpell.this.verticalHitRadius.get(data);
box = BoundingBox.of(center, hitRadius, verticalHitRadius, hitRadius);

yawOffset = OrbitSpell.this.yawOffset.get(data);
angleOffset = AccurateMath.toRadians(OrbitSpell.this.angleOffset.get(data));
pitchOffset = OrbitSpell.this.pitchOffset.get(data);
lockStartYaw = OrbitSpell.this.lockStartYaw.get(data);
lockStartPitch = OrbitSpell.this.lockStartPitch.get(data);
counterClockwise = OrbitSpell.this.counterClockwise.get(data);

double yaw = (lockStartYaw ? 0 : startYaw) + yawOffset;
double pitch = (lockStartPitch ? 0 : startPitch) + pitchOffset;

currentDirection = center.getDirection().setY(0).normalize();
Util.rotateVector(currentDirection, horizOffset.get(data));
axis = Util.getDirection(yaw, pitch + (counterClockwise ? -90 : 90));
offset = new Vector();
direction = Util.getDirection(yaw, pitch);
perpendicular = axis.clone().crossProduct(direction);

followYaw = OrbitSpell.this.followYaw.get(data);
followPitch = OrbitSpell.this.followPitch.get(data);
stopOnHitEntity = OrbitSpell.this.stopOnHitEntity.get(data);
stopOnHitGround = OrbitSpell.this.stopOnHitGround.get(data);
counterClockwise = OrbitSpell.this.counterClockwise.get(data);

int tickInterval = OrbitSpell.this.tickInterval.get(data);
taskId = MagicSpells.scheduleRepeatingTask(this, 0, tickInterval);
Expand All @@ -246,15 +304,19 @@ private OrbitTracker(SpellData data) {
repeatingVertTaskId = MagicSpells.scheduleRepeatingTask(() -> yOffset += vertExpandRadius, vertExpandDelay, vertExpandDelay);
} else repeatingVertTaskId = -1;

distancePerTick = 6.28f * tickInterval / secondsPerRevolution.get(data) / 20;
ticksPerRevolution = (int) (secondsPerRevolution.get(data) * 20 / tickInterval);

maxDuration = OrbitSpell.this.maxDuration.get(data) * TimeUtil.MILLISECONDS_PER_SECOND;

this.data = data;

transparent = isTransparent(data);

immune = new HashSet<>();
immune = new Object2IntArrayMap<>();
immune.defaultReturnValue(-1);

constantImmuneTicks = OrbitSpell.this.constantImmuneTicks.get(data);
immuneTicks = constantImmuneTicks ? OrbitSpell.this.immuneTicks.get(data) : 0;

entityMap = playSpellEntityEffects(EffectPosition.PROJECTILE, center, data);
effectSet = playSpellEffectLibEffects(EffectPosition.PROJECTILE, center, data);
Expand Down Expand Up @@ -321,29 +383,44 @@ public void run() {

if (orbitSpell != null) orbitSpell.subcast(data.noTarget());

box.setCenter(currentLocation);
box.resize(
currentLocation.getX() - hitRadius,
currentLocation.getY() - verticalHitRadius,
currentLocation.getZ() - hitRadius,
currentLocation.getX() + hitRadius,
currentLocation.getY() + verticalHitRadius,
currentLocation.getZ() + hitRadius
);

for (LivingEntity target : currentLocation.getNearbyLivingEntities(hitRadius, verticalHitRadius)) {
if (entityTargetList != null && !entityTargetList.canTarget(data.caster(), target)) continue;

for (LivingEntity e : data.caster().getWorld().getLivingEntities()) {
if (!e.isValid() || immune.contains(e) || !box.contains(e)) continue;
if (entityTargetList != null && !entityTargetList.canTarget(data.caster(), e)) continue;
int immuneTime = immune.getInt(target.getUniqueId());
int currentTick = Bukkit.getCurrentTick();
if (immuneTime >= 0 && immuneTime >= currentTick) continue;

SpellTargetEvent event = new SpellTargetEvent(OrbitSpell.this, data, e);
SpellTargetEvent event = new SpellTargetEvent(OrbitSpell.this, data, target);
if (!event.callEvent()) continue;

SpellData subData = event.getSpellData();
immune.add(event.getTarget());
target = event.getTarget();

int immuneTicks = constantImmuneTicks ? this.immuneTicks : OrbitSpell.this.immuneTicks.get(subData);
immune.put(target.getUniqueId(), immuneTicks >= 0 ? currentTick + immuneTicks : Integer.MAX_VALUE);

if (entitySpell != null) entitySpell.subcast(subData.noLocation());

playSpellEffects(EffectPosition.TARGET, event.getTarget(), subData);
playSpellEffectsTrail(currentLocation, event.getTarget().getLocation(), subData);
playSpellEffects(EffectPosition.TARGET, target, subData);
playSpellEffectsTrail(currentLocation, target.getLocation(), subData);

if (stopOnHitEntity) {
stop(true);
return;
}
}

if (ticksPerRevolution > 0) tickCount = (tickCount + 1) % ticksPerRevolution;

if (interactions == null || interactions.isEmpty()) return;
Set<OrbitTracker> toRemove = new HashSet<>();
Set<OrbitTracker> trackers = new HashSet<>(trackerSet);
Expand Down Expand Up @@ -383,7 +460,7 @@ private boolean canInteractWith(OrbitTracker collisionTracker) {
if (!data.hasCaster() || !collisionTracker.data.hasCaster()) return false;
if (collisionTracker.equals(this)) return false;
if (!collisionTracker.center.getWorld().equals(center.getWorld())) return false;
return collisionTracker.box.contains(center) || box.contains(collisionTracker.center);
return box.overlaps(collisionTracker.box);
}

private OrbitSpell getOrbitSpell() {
Expand All @@ -394,22 +471,33 @@ private Location getCurrentLocation() {
if (data.hasTarget()) {
data.target().getLocation(center);

if (followYaw) {
float currentYaw = center.getYaw();
float currentYaw = followYaw ? center.getYaw() : startYaw;
float currentPitch = followPitch ? center.getPitch() : startPitch;
if ((followYaw && previousYaw != currentYaw) || (followPitch && previousPitch != currentPitch)) {
float yaw = (lockStartYaw ? currentYaw - startYaw : currentYaw) + yawOffset;
float pitch = (lockStartPitch ? currentPitch - startPitch : currentPitch) + pitchOffset;

if (previousYaw != currentYaw) {
Util.rotateVector(currentDirection, currentYaw - previousYaw);
previousYaw = currentYaw;
}
Util.getDirection(axis, yaw, pitch + (counterClockwise ? -90 : 90));
Util.getDirection(direction, yaw, pitch);
perpendicular.copy(axis).crossProduct(direction);

previousYaw = currentYaw;
previousPitch = currentPitch;
}
}

Vector perp;
if (counterClockwise) perp = new Vector(currentDirection.getZ(), 0, -currentDirection.getX());
else perp = new Vector(-currentDirection.getZ(), 0, currentDirection.getX());
double angle = ticksPerRevolution > 0 ? 2 * tickCount * Math.PI / ticksPerRevolution : 0;
angle += angleOffset;

double cos = orbitRadius * Math.cos(angle);
double sin = orbitRadius * Math.sin(angle);

offset
.setX(cos * direction.getX() + sin * perpendicular.getX())
.setY(cos * direction.getY() + sin * perpendicular.getY())
.setZ(cos * direction.getZ() + sin * perpendicular.getZ());

currentDirection.add(perp.multiply(distancePerTick)).normalize();
return center.clone().add(0, yOffset, 0).add(currentDirection.clone().multiply(orbitRadius)).setDirection(perp);
return center.clone().add(offset).add(0, yOffset, 0).setDirection(offset.crossProduct(axis).multiply(-1));
}

private void stop(boolean removeTracker) {
Expand Down
17 changes: 17 additions & 0 deletions core/src/main/java/com/nisovin/magicspells/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,23 @@ public static Vector rotateVector(Vector v, float yawDegrees, float pitchDegrees
return new Vector(x, y, z);
}

public static Vector getDirection(double yaw, double pitch) {
return getDirection(new Vector(), yaw, pitch);
}

public static Vector getDirection(Vector vector, double yaw, double pitch) {
yaw = AccurateMath.toRadians(yaw);
pitch = AccurateMath.toRadians(pitch);

vector.setY(-Math.sin(pitch));

double xz = Math.cos(pitch);
vector.setX(-xz * Math.sin(yaw));
vector.setZ(xz * Math.cos(yaw));

return vector;
}

public static Location applyAbsoluteOffset(Location loc, Vector offset) {
return loc.add(offset);
}
Expand Down

0 comments on commit 281ecee

Please sign in to comment.