forked from SteamWar/SteamWar
Add glyph flight for enchant particles and blend team links
Entries with the enchant particle get a Glyph flight setting: float down as usual, fly into the player or fly out of them, the latter two unlocked by the WarGear Season placement and participation. Team link lines now start in the drawing player's particle and blend into the partner's along the way.
This commit is contained in:
@@ -142,6 +142,7 @@ PARTICLE_DESIGN_PULSE_SHIMMER = §fShimmer
|
||||
PARTICLE_DESIGN_HEART_BEAT = §fHeartbeat
|
||||
|
||||
PARTICLE_PARAM_PARTICLE = §fParticle
|
||||
PARTICLE_PARAM_GLYPH_FLIGHT = §fGlyph flight
|
||||
PARTICLE_PARAM_BURST_TRIGGER = §fWhen
|
||||
PARTICLE_PARAM_BURST_SHAPE = §fShape
|
||||
PARTICLE_PARAM_BURST_RADIUS = §fRing radius
|
||||
@@ -228,6 +229,9 @@ PARTICLE_CHOICE_TRIGGER_LAND = §fOn landing
|
||||
PARTICLE_CHOICE_TRIGGER_SNEAK = §fOn sneaking
|
||||
PARTICLE_CHOICE_BURSTSHAPE_PUFF = §fPuff
|
||||
PARTICLE_CHOICE_BURSTSHAPE_RING = §fRing
|
||||
PARTICLE_CHOICE_GLYPHFLIGHT_NORMAL = §fFloat down
|
||||
PARTICLE_CHOICE_GLYPHFLIGHT_INTO_PLAYER = §fInto the player
|
||||
PARTICLE_CHOICE_GLYPHFLIGHT_OUT_OF_PLAYER = §fOut of the player
|
||||
|
||||
# Particle types
|
||||
PARTICLE_TYPE_DUST = §fDust
|
||||
|
||||
@@ -142,6 +142,7 @@ PARTICLE_DESIGN_PULSE_SHIMMER = §fSchimmer
|
||||
PARTICLE_DESIGN_HEART_BEAT = §fHerzschlag
|
||||
|
||||
PARTICLE_PARAM_PARTICLE = §fPartikel
|
||||
PARTICLE_PARAM_GLYPH_FLIGHT = §fGlyphenflug
|
||||
PARTICLE_PARAM_BURST_TRIGGER = §fWann
|
||||
PARTICLE_PARAM_BURST_SHAPE = §fForm
|
||||
PARTICLE_PARAM_BURST_RADIUS = §fRingradius
|
||||
@@ -228,6 +229,9 @@ PARTICLE_CHOICE_TRIGGER_LAND = §fBeim Landen
|
||||
PARTICLE_CHOICE_TRIGGER_SNEAK = §fBeim Schleichen
|
||||
PARTICLE_CHOICE_BURSTSHAPE_PUFF = §fWolke
|
||||
PARTICLE_CHOICE_BURSTSHAPE_RING = §fRing
|
||||
PARTICLE_CHOICE_GLYPHFLIGHT_NORMAL = §fHerabschweben
|
||||
PARTICLE_CHOICE_GLYPHFLIGHT_INTO_PLAYER = §fIn den Spieler
|
||||
PARTICLE_CHOICE_GLYPHFLIGHT_OUT_OF_PLAYER = §fAus dem Spieler
|
||||
|
||||
# Particle types
|
||||
PARTICLE_TYPE_DUST = §fStaub
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
package de.steamwar.lobby.particle.design;
|
||||
|
||||
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||
import de.steamwar.lobby.particle.ParticleElement;
|
||||
import de.steamwar.lobby.particle.elements.*;
|
||||
import de.steamwar.lobby.particle.elements.custom.NonMoving;
|
||||
@@ -43,6 +44,13 @@ public class EntryParameters {
|
||||
BODY,
|
||||
}
|
||||
|
||||
// Enchanting glyphs can fly between each point and the player instead of floating down
|
||||
public enum GlyphFlight {
|
||||
NORMAL,
|
||||
INTO_PLAYER,
|
||||
OUT_OF_PLAYER,
|
||||
}
|
||||
|
||||
public enum Movement {
|
||||
ANY(element -> element),
|
||||
MOVING(element -> data -> {
|
||||
@@ -196,6 +204,12 @@ public class EntryParameters {
|
||||
public final DoubleParameter TRAIL_TARGET_X = new DoubleParameter("trailTargetX", "PARTICLE_PARAM_TRAIL_TARGET_X", 0, -5, 5, 0.1);
|
||||
public final DoubleParameter TRAIL_TARGET_Y = new DoubleParameter("trailTargetY", "PARTICLE_PARAM_TRAIL_TARGET_Y", 1, -5, 5, 0.1);
|
||||
public final DoubleParameter TRAIL_TARGET_Z = new DoubleParameter("trailTargetZ", "PARTICLE_PARAM_TRAIL_TARGET_Z", 0, -5, 5, 0.1);
|
||||
public final ChoiceParameter<GlyphFlight> GLYPH_FLIGHT = ChoiceParameter.ofEnum("glyphFlight", "PARTICLE_PARAM_GLYPH_FLIGHT", GlyphFlight.NORMAL, flight -> false,
|
||||
flight -> switch (flight) {
|
||||
case NORMAL -> ParticleRequirement.NO_REQUIRMENT;
|
||||
case INTO_PLAYER -> Unlocks.WARGEAR_SEASON_PLACEMENT.get();
|
||||
case OUT_OF_PLAYER -> Unlocks.WARGEAR_SEASON.get();
|
||||
});
|
||||
public final ChoiceParameter<Placement> PLACEMENT = ChoiceParameter.ofEnum("placement", "PARTICLE_PARAM_PLACEMENT", Placement.WORLD);
|
||||
public final ChoiceParameter<TickMode> TICK_MODE = ChoiceParameter.ofEnum("tickMode", "PARTICLE_PARAM_TICK_MODE", TickMode.MOVE);
|
||||
public final ChoiceParameter<Movement> MOVEMENT = ChoiceParameter.ofEnum("movement", "PARTICLE_PARAM_MOVEMENT", Movement.ANY);
|
||||
@@ -205,7 +219,7 @@ public class EntryParameters {
|
||||
public final ChoiceParameter<Visibility> VISIBILITY = ChoiceParameter.ofEnum("visibility", "PARTICLE_PARAM_VISIBILITY", Visibility.EVERYONE);
|
||||
|
||||
public final List<Parameter<?>> ALL = List.of(
|
||||
PARTICLE, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS, BLOCK,
|
||||
PARTICLE, GLYPH_FLIGHT, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS, BLOCK,
|
||||
TRAIL_DURATION, TRAIL_TARGET_X, TRAIL_TARGET_Y, TRAIL_TARGET_Z,
|
||||
OFFSET_X, OFFSET_Y, OFFSET_Z, PLACEMENT, INTERVAL, TICK_MODE,
|
||||
MOVEMENT, GLIDING, GROUND, SNEAK, VISIBILITY
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package de.steamwar.lobby.particle.elements.touch;
|
||||
|
||||
import de.steamwar.lobby.particle.ParticleElement;
|
||||
import de.steamwar.lobby.particle.ParticleTickData;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
// Enchanting glyphs that fly between each point and the player's chest. Spawned with an amount of 0, the offset becomes
|
||||
// the glyph's path: it appears at location + offset and flies to the location, like glyphs flying into an enchanting
|
||||
// table.
|
||||
public class GlyphPath implements ParticleElement {
|
||||
|
||||
public enum Direction {
|
||||
INTO_PLAYER,
|
||||
OUT_OF_PLAYER,
|
||||
}
|
||||
|
||||
private static final double CHEST = 1.2;
|
||||
|
||||
private final Direction direction;
|
||||
private final double spreadX;
|
||||
private final double spreadY;
|
||||
private final double spreadZ;
|
||||
private final int count;
|
||||
|
||||
public GlyphPath(Direction direction, double spreadX, double spreadY, double spreadZ, int count) {
|
||||
this.direction = direction;
|
||||
this.spreadX = spreadX;
|
||||
this.spreadY = spreadY;
|
||||
this.spreadZ = spreadZ;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(ParticleTickData particleTickData) {
|
||||
Location chest = particleTickData.getPlayer().getLocation().add(0, CHEST, 0);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Spread like the game spreads particles, as the game does not spread those spawned with an amount of 0
|
||||
Location point = particleTickData.getLocation().clone().add(RANDOM.nextGaussian() * spreadX, RANDOM.nextGaussian() * spreadY, RANDOM.nextGaussian() * spreadZ);
|
||||
Vector fromChest = point.toVector().subtract(chest.toVector());
|
||||
Location target = direction == Direction.INTO_PLAYER ? chest : point;
|
||||
Vector path = direction == Direction.INTO_PLAYER ? fromChest : fromChest.multiply(-1);
|
||||
display(particleTickData, player -> player.spawnParticle(Particle.ENCHANT, target, 0, path.getX(), path.getY(), path.getZ(), 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -16,7 +16,6 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.lobby.particle.elements.touch;
|
||||
|
||||
import de.steamwar.lobby.particle.BodyMotion;
|
||||
@@ -31,21 +30,27 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
// A line of particles between nearby players who show the same reward. Each pair is drawn by only one of them.
|
||||
// A line of particles between nearby players who show the same link. It starts in one player's particle and blends
|
||||
// into the other's towards them. Each pair is drawn by only one of them.
|
||||
public class TeamLink extends DelegatingParticleElement {
|
||||
|
||||
public static final double RANGE = 8;
|
||||
private static final double CHEST = 1.2;
|
||||
// Spreads the blend evenly along the line, so it does not flicker from tick to tick
|
||||
private static final double GOLDEN_RATIO = 0.6180339887;
|
||||
|
||||
// Per reward, who showed it in which tick
|
||||
private static final Map<String, Map<Player, Long>> SHOWING = new HashMap<>();
|
||||
private record Showing(long tick, ParticleElement particle) {
|
||||
}
|
||||
|
||||
private final String reward;
|
||||
// Per link, who showed it in which tick with which particle
|
||||
private static final Map<String, Map<Player, Showing>> SHOWING = new HashMap<>();
|
||||
|
||||
private final String link;
|
||||
private final double spacing;
|
||||
|
||||
public TeamLink(String reward, ParticleElement particleElement, double spacing) {
|
||||
public TeamLink(String link, ParticleElement particleElement, double spacing) {
|
||||
super(particleElement);
|
||||
this.reward = reward;
|
||||
this.link = link;
|
||||
this.spacing = spacing;
|
||||
}
|
||||
|
||||
@@ -53,14 +58,14 @@ public class TeamLink extends DelegatingParticleElement {
|
||||
public void tick(ParticleTickData particleTickData) {
|
||||
Player player = particleTickData.getPlayer();
|
||||
long now = BodyMotion.currentTick();
|
||||
Map<Player, Long> showing = SHOWING.computeIfAbsent(reward, key -> new WeakHashMap<>());
|
||||
showing.put(player, now);
|
||||
Map<Player, Showing> showing = SHOWING.computeIfAbsent(link, key -> new WeakHashMap<>());
|
||||
showing.put(player, new Showing(now, particleElement));
|
||||
|
||||
Location from = player.getLocation().add(0, CHEST, 0);
|
||||
for (Map.Entry<Player, Long> other : showing.entrySet()) {
|
||||
for (Map.Entry<Player, Showing> other : showing.entrySet()) {
|
||||
Player partner = other.getKey();
|
||||
// Still showing it, and drawn by the player with the smaller id only
|
||||
if (partner == player || other.getValue() < now - 1 || player.getUniqueId().compareTo(partner.getUniqueId()) > 0) continue;
|
||||
if (partner == player || other.getValue().tick() < now - 1 || player.getUniqueId().compareTo(partner.getUniqueId()) > 0) continue;
|
||||
Location to = partner.getLocation().add(0, CHEST, 0);
|
||||
if (to.getWorld() != from.getWorld() || to.distanceSquared(from) > RANGE * RANGE) continue;
|
||||
|
||||
@@ -68,7 +73,9 @@ public class TeamLink extends DelegatingParticleElement {
|
||||
int points = (int) Math.ceil(step.length() / spacing);
|
||||
step.multiply(1.0 / points);
|
||||
for (int i = 1; i < points; i++) {
|
||||
particleElement.tick(particleTickData.withLocation(from.clone().add(step.clone().multiply(i))));
|
||||
double towardsPartner = (double) i / points;
|
||||
ParticleElement particle = (i * GOLDEN_RATIO) % 1 < towardsPartner ? other.getValue().particle() : particleElement;
|
||||
particle.tick(particleTickData.withLocation(from.clone().add(step.clone().multiply(i))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,8 @@ class EntryEditor {
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, parameterItem(context, COLORS));
|
||||
} else if (type.usesBlock()) {
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, parameterItem(context, BLOCK));
|
||||
} else if (type == ParticleType.ENCHANT) {
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, parameterItem(context, GLYPH_FLIGHT));
|
||||
}
|
||||
// The trail target follows the particle; its duration goes to the end of the particle row
|
||||
List<Parameter<?>> particleRow = new ArrayList<>(design.replacesAmount(entry.getValues()) ? List.of(SPEED, SPREAD_X, SPREAD_Y, SPREAD_Z) : List.of(COUNT, SPEED, SPREAD_X, SPREAD_Y, SPREAD_Z));
|
||||
|
||||
@@ -28,6 +28,7 @@ import de.steamwar.lobby.particle.UnlockContext;
|
||||
import de.steamwar.lobby.particle.design.*;
|
||||
import de.steamwar.lobby.particle.elements.*;
|
||||
import de.steamwar.lobby.particle.elements.touch.AfterJump;
|
||||
import de.steamwar.lobby.particle.elements.touch.GlyphPath;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@@ -149,6 +150,10 @@ public class ParticleEntry {
|
||||
case ZERO_FLOAT -> (tick, location) -> 0f;
|
||||
};
|
||||
|
||||
if (type == ParticleType.ENCHANT && values.get(GLYPH_FLIGHT) != GlyphFlight.NORMAL) {
|
||||
GlyphPath.Direction direction = values.get(GLYPH_FLIGHT) == GlyphFlight.INTO_PLAYER ? GlyphPath.Direction.INTO_PLAYER : GlyphPath.Direction.OUT_OF_PLAYER;
|
||||
return new GlyphPath(direction, spreadX, spreadY, spreadZ, count);
|
||||
}
|
||||
if (type.isDust()) {
|
||||
return new DustParticle(type.getParticle(), spreadX, spreadY, spreadZ, speed, count, gradient);
|
||||
}
|
||||
@@ -183,7 +188,11 @@ public class ParticleEntry {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !design.usesEntryParticle() || PARTICLE.requirement(values.get(PARTICLE)).test(context);
|
||||
if (!design.usesEntryParticle()) {
|
||||
return true;
|
||||
}
|
||||
ParticleType type = values.get(PARTICLE);
|
||||
return type.getRequirement().test(context) && (type != ParticleType.ENCHANT || GLYPH_FLIGHT.requirement(values.get(GLYPH_FLIGHT)).test(context));
|
||||
}
|
||||
|
||||
// Particles each point spawns; designs that set their own point count replace the amount with 1
|
||||
|
||||
Reference in New Issue
Block a user