diff --git a/LobbySystem/src/de/steamwar/lobby/LobbySystem.properties b/LobbySystem/src/de/steamwar/lobby/LobbySystem.properties
index 5e419fc3..e691bbf9 100644
--- a/LobbySystem/src/de/steamwar/lobby/LobbySystem.properties
+++ b/LobbySystem/src/de/steamwar/lobby/LobbySystem.properties
@@ -142,6 +142,7 @@ PARTICLE_PARAM_OFFSET_X = §fOffset X
PARTICLE_PARAM_OFFSET_Y = §fOffset Y
PARTICLE_PARAM_OFFSET_Z = §fOffset Z
PARTICLE_PARAM_INTERVAL = §fEvery n ticks
+PARTICLE_PARAM_PLACEMENT = §fRelative to
PARTICLE_PARAM_TICK_MODE = §fShown
PARTICLE_PARAM_MOVEMENT = §fMovement
PARTICLE_PARAM_GLIDING = §fElytra
@@ -160,6 +161,8 @@ PARTICLE_PARAM_WING_SIZE = §fWing size
PARTICLE_CHOICE_TICKMODE_MOVE = §fWhile moving
PARTICLE_CHOICE_TICKMODE_ALWAYS = §fAlways
+PARTICLE_CHOICE_PLACEMENT_WORLD = §fWorld (X east, Y up, Z south)
+PARTICLE_CHOICE_PLACEMENT_BODY = §fBody (X right, Y up, Z forward)
PARTICLE_CHOICE_MOVEMENT_ANY = §fAny movement
PARTICLE_CHOICE_MOVEMENT_MOVING = §fOnly while moving
PARTICLE_CHOICE_MOVEMENT_STANDING_STILL = §fOnly while standing still
diff --git a/LobbySystem/src/de/steamwar/lobby/LobbySystem_de.properties b/LobbySystem/src/de/steamwar/lobby/LobbySystem_de.properties
index df313e50..146f66c1 100644
--- a/LobbySystem/src/de/steamwar/lobby/LobbySystem_de.properties
+++ b/LobbySystem/src/de/steamwar/lobby/LobbySystem_de.properties
@@ -142,6 +142,7 @@ PARTICLE_PARAM_OFFSET_X = §fVersatz X
PARTICLE_PARAM_OFFSET_Y = §fVersatz Y
PARTICLE_PARAM_OFFSET_Z = §fVersatz Z
PARTICLE_PARAM_INTERVAL = §fAlle n Ticks
+PARTICLE_PARAM_PLACEMENT = §fAusgerichtet an
PARTICLE_PARAM_TICK_MODE = §fAnzeige
PARTICLE_PARAM_MOVEMENT = §fBewegung
PARTICLE_PARAM_GLIDING = §fElytra
@@ -160,6 +161,8 @@ PARTICLE_PARAM_WING_SIZE = §fFlügelgröße
PARTICLE_CHOICE_TICKMODE_MOVE = §fBeim Bewegen
PARTICLE_CHOICE_TICKMODE_ALWAYS = §fImmer
+PARTICLE_CHOICE_PLACEMENT_WORLD = §fWelt (X Osten, Y oben, Z Süden)
+PARTICLE_CHOICE_PLACEMENT_BODY = §fKörper (X rechts, Y oben, Z vorne)
PARTICLE_CHOICE_MOVEMENT_ANY = §fJede Bewegung
PARTICLE_CHOICE_MOVEMENT_MOVING = §fNur beim Bewegen
PARTICLE_CHOICE_MOVEMENT_STANDING_STILL = §fNur beim Stehen
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/BodyFrame.java b/LobbySystem/src/de/steamwar/lobby/particle/BodyFrame.java
new file mode 100644
index 00000000..68c5de0d
--- /dev/null
+++ b/LobbySystem/src/de/steamwar/lobby/particle/BodyFrame.java
@@ -0,0 +1,95 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 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 .
+ */
+
+package de.steamwar.lobby.particle;
+
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Pose;
+import org.bukkit.util.Vector;
+
+// The player's body as a coordinate system: origin at the feet, up towards the head, forward out of the chest.
+// Standing it turns with the body; gliding or swimming the body lies along the look direction.
+@Getter
+public class BodyFrame {
+
+ private static final Vector WORLD_UP = new Vector(0, 1, 0);
+ private static final double HALF_BODY = 0.9;
+
+ private final Location origin;
+ private final Vector right;
+ private final Vector up;
+ private final Vector forward;
+ // Reference directions of rings around the body; they do not turn with the body
+ private final Vector ringStart;
+ private final Vector ringSide;
+
+ private BodyFrame(Location origin, Vector right, Vector up, Vector forward) {
+ this.origin = origin;
+ this.right = right;
+ this.up = up;
+ this.forward = forward;
+
+ // World east projected onto the plane around the body, so a standing ring matches the world ring
+ Vector start = new Vector(1, 0, 0).subtract(up.clone().multiply(up.getX()));
+ if (start.lengthSquared() < 1e-6) {
+ start = new Vector(0, 0, 1).subtract(up.clone().multiply(up.getZ()));
+ }
+ this.ringStart = start.normalize();
+ this.ringSide = up.clone().crossProduct(ringStart);
+ }
+
+ public static BodyFrame of(Player player) {
+ Location location = player.getLocation();
+ Pose pose = player.getPose();
+ if (pose == Pose.FALL_FLYING || pose == Pose.SWIMMING) {
+ Vector up = location.getDirection().normalize();
+ Vector right = up.clone().crossProduct(WORLD_UP);
+ if (right.lengthSquared() < 1e-6) {
+ right = horizontal(player.getBodyYaw()).crossProduct(WORLD_UP);
+ }
+ right.normalize();
+ Vector forward = up.clone().crossProduct(right).normalize();
+ // The lying body is centred in its short hitbox, its feet lie behind the centre
+ Location origin = location.clone().add(0, player.getHeight() / 2, 0).subtract(up.clone().multiply(HALF_BODY));
+ return new BodyFrame(origin, right, up, forward);
+ }
+
+ Vector forward = horizontal(player.getBodyYaw());
+ Vector right = forward.clone().crossProduct(WORLD_UP).normalize();
+ return new BodyFrame(location, right, WORLD_UP.clone(), forward);
+ }
+
+ private static Vector horizontal(float yaw) {
+ double radians = Math.toRadians(yaw);
+ return new Vector(-Math.sin(radians), 0, Math.cos(radians));
+ }
+
+ public Vector local(double right, double up, double forward) {
+ return this.right.clone().multiply(right)
+ .add(this.up.clone().multiply(up))
+ .add(this.forward.clone().multiply(forward));
+ }
+
+ // A point on a ring around the up axis; angle 0 and the turning direction match the world rings
+ public Vector ring(double distance, double angle) {
+ return ringStart.clone().multiply(Math.cos(angle) * distance).add(ringSide.clone().multiply(Math.sin(angle) * distance));
+ }
+}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/ParticleTickData.java b/LobbySystem/src/de/steamwar/lobby/particle/ParticleTickData.java
index f5f7cb37..569af6fa 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/ParticleTickData.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/ParticleTickData.java
@@ -41,6 +41,8 @@ public class ParticleTickData {
private final List viewers;
private boolean onlySelf = false;
private boolean onlyOthers = false;
+ // Set for entries placed relative to the body, null for world placement
+ private BodyFrame frame;
public ParticleTickData withLocation(Location location) {
ParticleTickData particleTickData = copy();
@@ -48,6 +50,13 @@ public class ParticleTickData {
return particleTickData;
}
+ public ParticleTickData withFrame(BodyFrame frame) {
+ ParticleTickData particleTickData = copy();
+ particleTickData.frame = frame;
+ particleTickData.location = frame.getOrigin().clone();
+ return particleTickData;
+ }
+
public ParticleTickData onlySelf() {
ParticleTickData particleTickData = copy();
particleTickData.onlySelf = true;
@@ -68,6 +77,6 @@ public class ParticleTickData {
}
public ParticleTickData copy() {
- return new ParticleTickData(world, player, location, deg, isMoving, viewers, onlySelf, onlyOthers);
+ return new ParticleTickData(world, player, location, deg, isMoving, viewers, onlySelf, onlyOthers, frame);
}
}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/TODOs.md b/LobbySystem/src/de/steamwar/lobby/particle/TODOs.md
new file mode 100644
index 00000000..823a020f
--- /dev/null
+++ b/LobbySystem/src/de/steamwar/lobby/particle/TODOs.md
@@ -0,0 +1 @@
+Relative zur rotation den spielers und co
\ No newline at end of file
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/design/Designs.java b/LobbySystem/src/de/steamwar/lobby/particle/design/Designs.java
index cfa4a419..4210da24 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/design/Designs.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/design/Designs.java
@@ -94,14 +94,14 @@ public class Designs {
return values.get(WING_IMAGE).getVectors().length;
}
- // Wings spawn a particle on every pixel of the image, so they start sparse
+ // Wings spawn a particle on every pixel of the image, so they start sparse, and sit on the back even while gliding
@Override
public void applyDefaults(ParameterValues values) {
values.set(EntryParameters.COUNT, 1)
.set(EntryParameters.SPEED, 0.0)
.set(EntryParameters.TICK_MODE, EntryParameters.TickMode.ALWAYS)
.set(EntryParameters.INTERVAL, 20)
- .set(EntryParameters.GLIDING, EntryParameters.Gliding.NOT_GLIDING);
+ .set(EntryParameters.PLACEMENT, EntryParameters.Placement.BODY);
}
};
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/design/EntryParameters.java b/LobbySystem/src/de/steamwar/lobby/particle/design/EntryParameters.java
index 8a3f7f65..69e73263 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/design/EntryParameters.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/design/EntryParameters.java
@@ -37,6 +37,12 @@ public class EntryParameters {
ALWAYS,
}
+ // Body places offsets, heights, rings and wings around the player's body, even while gliding or swimming
+ public enum Placement {
+ WORLD,
+ BODY,
+ }
+
public enum Movement {
ANY(element -> element),
MOVING(element -> data -> {
@@ -183,6 +189,7 @@ public class EntryParameters {
public final DoubleParameter OFFSET_Y = new DoubleParameter("offsetY", "PARTICLE_PARAM_OFFSET_Y", 0, -3, 3, 0.1);
public final DoubleParameter OFFSET_Z = new DoubleParameter("offsetZ", "PARTICLE_PARAM_OFFSET_Z", 0, -3, 3, 0.1);
public final IntParameter INTERVAL = new IntParameter("interval", "PARTICLE_PARAM_INTERVAL", 1, 1, 100);
+ public final ChoiceParameter PLACEMENT = ChoiceParameter.ofEnum("placement", "PARTICLE_PARAM_PLACEMENT", Placement.WORLD);
public final ChoiceParameter TICK_MODE = ChoiceParameter.ofEnum("tickMode", "PARTICLE_PARAM_TICK_MODE", TickMode.MOVE);
public final ChoiceParameter MOVEMENT = ChoiceParameter.ofEnum("movement", "PARTICLE_PARAM_MOVEMENT", Movement.ANY);
public final ChoiceParameter GLIDING = ChoiceParameter.ofEnum("gliding", "PARTICLE_PARAM_GLIDING", Gliding.ANY);
@@ -192,7 +199,7 @@ public class EntryParameters {
public final List> ALL = List.of(
PARTICLE, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS, BLOCK,
- OFFSET_X, OFFSET_Y, OFFSET_Z, INTERVAL, TICK_MODE,
+ OFFSET_X, OFFSET_Y, OFFSET_Z, PLACEMENT, INTERVAL, TICK_MODE,
MOVEMENT, GLIDING, GROUND, SNEAK, VISIBILITY
);
}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/elements/BodyRelative.java b/LobbySystem/src/de/steamwar/lobby/particle/elements/BodyRelative.java
new file mode 100644
index 00000000..3b623a54
--- /dev/null
+++ b/LobbySystem/src/de/steamwar/lobby/particle/elements/BodyRelative.java
@@ -0,0 +1,37 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 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 .
+ */
+
+package de.steamwar.lobby.particle.elements;
+
+import de.steamwar.lobby.particle.BodyFrame;
+import de.steamwar.lobby.particle.ParticleElement;
+import de.steamwar.lobby.particle.ParticleTickData;
+
+// Places everything inside relative to the player's body instead of the world
+public class BodyRelative extends DelegatingParticleElement {
+
+ public BodyRelative(ParticleElement particleElement) {
+ super(particleElement);
+ }
+
+ @Override
+ public void tick(ParticleTickData particleTickData) {
+ particleElement.tick(particleTickData.withFrame(BodyFrame.of(particleTickData.getPlayer())));
+ }
+}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/elements/Circle.java b/LobbySystem/src/de/steamwar/lobby/particle/elements/Circle.java
index e95af976..4dfb7d8e 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/elements/Circle.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/elements/Circle.java
@@ -19,6 +19,7 @@
package de.steamwar.lobby.particle.elements;
+import de.steamwar.lobby.particle.BodyFrame;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import org.bukkit.Location;
@@ -50,6 +51,11 @@ public class Circle extends DelegatingParticleElement {
@Override
public void tick(ParticleTickData particleTickData) {
Location location = particleTickData.getLocation();
+ BodyFrame frame = particleTickData.getFrame();
+ if (frame != null) {
+ particleElement.tick(particleTickData.withLocation(location.clone().add(frame.ring(distance, (particleTickData.getDeg() * speed) % 360 + phase))));
+ return;
+ }
Vector vector = new Vector(distance, 0, 0);
vector.rotateAroundY((particleTickData.getDeg() * speed) % 360 + phase);
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/elements/DoubleCircle.java b/LobbySystem/src/de/steamwar/lobby/particle/elements/DoubleCircle.java
index 117fb2a6..b594527e 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/elements/DoubleCircle.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/elements/DoubleCircle.java
@@ -19,6 +19,7 @@
package de.steamwar.lobby.particle.elements;
+import de.steamwar.lobby.particle.BodyFrame;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import de.steamwar.lobby.particle.ParticleTickType;
@@ -57,6 +58,13 @@ public class DoubleCircle extends Circle {
@Override
public void tick(ParticleTickData particleTickData) {
Location location = particleTickData.getLocation();
+ BodyFrame frame = particleTickData.getFrame();
+ if (frame != null) {
+ double angle = (particleTickData.getDeg() * speed) % 360;
+ particleElement.tick(particleTickData.withLocation(location.clone().add(frame.ring(distance, angle))));
+ second.tick(particleTickData.withLocation(location.clone().add(frame.ring(distance, angle + Math.PI))));
+ return;
+ }
Vector vector = new Vector(distance, 0, 0);
vector.rotateAroundY((particleTickData.getDeg() * speed) % 360);
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/elements/LocationMutator.java b/LobbySystem/src/de/steamwar/lobby/particle/elements/LocationMutator.java
index b83ecf0f..1b62071f 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/elements/LocationMutator.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/elements/LocationMutator.java
@@ -19,15 +19,19 @@
package de.steamwar.lobby.particle.elements;
+import de.steamwar.lobby.particle.BodyFrame;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import org.bukkit.Location;
+import org.bukkit.util.Vector;
import java.util.function.UnaryOperator;
public class LocationMutator extends DelegatingParticleElement {
private UnaryOperator mutator;
+ // Set for plain offsets, which turn into right/up/forward when placed relative to the body
+ private Vector offset;
public LocationMutator(ParticleElement particleElement, UnaryOperator mutator) {
super(particleElement);
@@ -36,10 +40,17 @@ public class LocationMutator extends DelegatingParticleElement {
public LocationMutator(ParticleElement particleElement, double vx, double vy, double vz) {
this(particleElement, location -> location.add(vx, vy, vz));
+ this.offset = new Vector(vx, vy, vz);
}
@Override
public void tick(ParticleTickData particleTickData) {
+ BodyFrame frame = particleTickData.getFrame();
+ if (frame != null && offset != null) {
+ Location location = particleTickData.getLocation().clone().add(frame.local(offset.getX(), offset.getY(), offset.getZ()));
+ particleElement.tick(particleTickData.withLocation(location));
+ return;
+ }
ParticleTickData nParticleTickData = particleTickData.withLocation(mutator.apply(particleTickData.getLocation()));
particleElement.tick(nParticleTickData);
}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/elements/TrippleCircle.java b/LobbySystem/src/de/steamwar/lobby/particle/elements/TrippleCircle.java
index 39425c1e..75019032 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/elements/TrippleCircle.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/elements/TrippleCircle.java
@@ -19,6 +19,7 @@
package de.steamwar.lobby.particle.elements;
+import de.steamwar.lobby.particle.BodyFrame;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import de.steamwar.lobby.particle.ParticleTickType;
@@ -59,6 +60,14 @@ public class TrippleCircle extends Circle {
@Override
public void tick(ParticleTickData particleTickData) {
Location location = particleTickData.getLocation();
+ BodyFrame frame = particleTickData.getFrame();
+ if (frame != null) {
+ double angle = (particleTickData.getDeg() * speed) % 360;
+ particleElement.tick(particleTickData.withLocation(location.clone().add(frame.ring(distance, angle))));
+ second.tick(particleTickData.withLocation(location.clone().add(frame.ring(distance, angle + Math.toRadians(120)))));
+ third.tick(particleTickData.withLocation(location.clone().add(frame.ring(distance, angle + Math.toRadians(240)))));
+ return;
+ }
Vector vector = new Vector(distance, 0, 0);
vector.rotateAroundY((particleTickData.getDeg() * speed) % 360);
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/elements/Wing.java b/LobbySystem/src/de/steamwar/lobby/particle/elements/Wing.java
index 1c8f1322..7c9e9d8d 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/elements/Wing.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/elements/Wing.java
@@ -19,6 +19,7 @@
package de.steamwar.lobby.particle.elements;
+import de.steamwar.lobby.particle.BodyFrame;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import de.steamwar.lobby.particle.WingDesign;
@@ -38,6 +39,17 @@ public class Wing extends DelegatingParticleElement {
@Override
public void tick(ParticleTickData particleTickData) {
+ BodyFrame frame = particleTickData.getFrame();
+ if (frame != null) {
+ // Half a block behind the back, so the wings stay on the back even while gliding
+ double sneak = particleTickData.getPlayer().isSneaking() ? 0.5 : 0;
+ Location base = particleTickData.getLocation();
+ for (Vector dVector : wingDesign.getVectors()) {
+ Vector local = frame.local(dVector.getX() * size, 0.6 + dVector.getY() * size - sneak, -0.5);
+ particleElement.tick(particleTickData.withLocation(base.clone().add(local)));
+ }
+ return;
+ }
for (Vector dVector : wingDesign.getVectors()) {
Vector vector = new Vector(dVector.getX() * size, 0.6 + dVector.getY() * size - (particleTickData.getPlayer().isSneaking() ? 0.5 : 0), 0.5);
vector.rotateAroundY(Math.toRadians(particleTickData.getPlayer().getLocation().getYaw() * -1));
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/elements/custom/YOffset.java b/LobbySystem/src/de/steamwar/lobby/particle/elements/custom/YOffset.java
index 15375522..09f36493 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/elements/custom/YOffset.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/elements/custom/YOffset.java
@@ -19,9 +19,11 @@
package de.steamwar.lobby.particle.elements.custom;
+import de.steamwar.lobby.particle.BodyFrame;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import de.steamwar.lobby.particle.elements.DelegatingParticleElement;
+import org.bukkit.Location;
public class YOffset extends DelegatingParticleElement {
@@ -66,7 +68,9 @@ public class YOffset extends DelegatingParticleElement {
y = minY + (maxY - minY) * value;
}
}
- ParticleTickData current = particleTickData.withLocation(particleTickData.getLocation().clone().add(0, y, 0));
+ BodyFrame frame = particleTickData.getFrame();
+ Location location = particleTickData.getLocation().clone();
+ ParticleTickData current = particleTickData.withLocation(frame == null ? location.add(0, y, 0) : location.add(frame.getUp().clone().multiply(y)));
particleElement.tick(current);
}
}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java b/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java
index 5dd3d5ba..9eb99fe2 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java
@@ -67,6 +67,7 @@ class EntryEditor {
Map.entry(OFFSET_Z, Material.SPECTRAL_ARROW),
Map.entry(INTERVAL, Material.CLOCK),
Map.entry(TICK_MODE, Material.REPEATER),
+ Map.entry(PLACEMENT, Material.PLAYER_HEAD),
Map.entry(MOVEMENT, Material.LEATHER_BOOTS),
Map.entry(GLIDING, Material.ELYTRA),
Map.entry(GROUND, Material.GRASS_BLOCK),
@@ -122,7 +123,7 @@ class EntryEditor {
}));
}
row(inventory, context, ROW_SHAPE, design.getParameters());
- row(inventory, context, ROW_TIMING, List.of(TICK_MODE, INTERVAL, OFFSET_X, OFFSET_Y, OFFSET_Z));
+ row(inventory, context, ROW_TIMING, List.of(TICK_MODE, INTERVAL, PLACEMENT, OFFSET_X, OFFSET_Y, OFFSET_Z));
row(inventory, context, ROW_CONDITIONS, List.of(MOVEMENT, GLIDING, GROUND, SNEAK, VISIBILITY));
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> SlotEditor.open(player, index)));
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/slot/ParticleEntry.java b/LobbySystem/src/de/steamwar/lobby/particle/slot/ParticleEntry.java
index d1c3ba04..f41cebd1 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/slot/ParticleEntry.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/slot/ParticleEntry.java
@@ -79,6 +79,9 @@ public class ParticleEntry {
element = withOffset(design.build(values, null));
}
+ if (values.get(PLACEMENT) == Placement.BODY) {
+ element = new BodyRelative(element);
+ }
element = values.get(MOVEMENT).wrap(element);
element = values.get(GLIDING).wrap(element);
element = values.get(GROUND).wrap(element);
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/slot/Presets.java b/LobbySystem/src/de/steamwar/lobby/particle/slot/Presets.java
index 3a025539..413b9103 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/slot/Presets.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/slot/Presets.java
@@ -122,7 +122,8 @@ public class Presets {
.with(Designs.WING_SIZE, size)
.with(TICK_MODE, TickMode.ALWAYS)
.with(INTERVAL, interval)
- .with(GLIDING, Gliding.NOT_GLIDING);
+ .with(GLIDING, Gliding.NOT_GLIDING)
+ .with(PLACEMENT, Placement.WORLD);
}
private ParticleEntry dustWing(Color color, WingDesign image) {