Add configurable particle slot model

Introduce parameters, particle types, designs and entries that build the
existing particle elements, store each of the 7 particle slots in its own
UserConfig key, and let LobbyPlayer select a slot next to the old enum
particles. Slots 4-5 need a team, 6-7 the server team.
This commit is contained in:
2026-10-26 09:47:49 +01:00
parent ef041272bb
commit 1980375e73
22 changed files with 1396 additions and 5 deletions
@@ -0,0 +1,151 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.lobby.particle.design;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.elements.*;
import de.steamwar.lobby.particle.elements.custom.NonMoving;
import lombok.experimental.UtilityClass;
import java.util.List;
import java.util.function.UnaryOperator;
// Parameters every entry has, independent of its design
@UtilityClass
public class EntryParameters {
public enum TickMode {
MOVE,
ALWAYS,
}
public enum Movement {
ANY(element -> element),
MOVING(element -> data -> {
if (data.isMoving()) element.tick(data);
}),
STANDING_STILL(NonMoving::new),
;
private final UnaryOperator<ParticleElement> wrapper;
Movement(UnaryOperator<ParticleElement> wrapper) {
this.wrapper = wrapper;
}
public ParticleElement wrap(ParticleElement element) {
return wrapper.apply(element);
}
}
public enum Gliding {
ANY(element -> element),
GLIDING(Flying::new),
NOT_GLIDING(NonFlying::new),
;
private final UnaryOperator<ParticleElement> wrapper;
Gliding(UnaryOperator<ParticleElement> wrapper) {
this.wrapper = wrapper;
}
public ParticleElement wrap(ParticleElement element) {
return wrapper.apply(element);
}
}
public enum Ground {
ANY(element -> element),
ON_GROUND(Floor::new),
IN_AIR(NonFloor::new),
;
private final UnaryOperator<ParticleElement> wrapper;
Ground(UnaryOperator<ParticleElement> wrapper) {
this.wrapper = wrapper;
}
public ParticleElement wrap(ParticleElement element) {
return wrapper.apply(element);
}
}
public enum Sneak {
ANY(element -> element),
SNEAKING(Sneaking::new),
NOT_SNEAKING(element -> data -> {
if (!data.getPlayer().isSneaking()) element.tick(data);
}),
;
private final UnaryOperator<ParticleElement> wrapper;
Sneak(UnaryOperator<ParticleElement> wrapper) {
this.wrapper = wrapper;
}
public ParticleElement wrap(ParticleElement element) {
return wrapper.apply(element);
}
}
public enum Visibility {
EVERYONE(element -> element),
ONLY_SELF(OnlySelf::new),
ONLY_OTHERS(OnlyOthers::new),
;
private final UnaryOperator<ParticleElement> wrapper;
Visibility(UnaryOperator<ParticleElement> wrapper) {
this.wrapper = wrapper;
}
public ParticleElement wrap(ParticleElement element) {
return wrapper.apply(element);
}
}
public final ChoiceParameter<ParticleType> PARTICLE = ChoiceParameter.ofEnum("particle", "PARTICLE_PARAM_PARTICLE", ParticleType.FLAME, type -> !type.isBuildable());
public final IntParameter COUNT = new IntParameter("count", "PARTICLE_PARAM_COUNT", 5, 1, 20);
public final DoubleParameter SPREAD_X = new DoubleParameter("spreadX", "PARTICLE_PARAM_SPREAD_X", 0, 0, 2, 0.05);
public final DoubleParameter SPREAD_Y = new DoubleParameter("spreadY", "PARTICLE_PARAM_SPREAD_Y", 0, 0, 2, 0.05);
public final DoubleParameter SPREAD_Z = new DoubleParameter("spreadZ", "PARTICLE_PARAM_SPREAD_Z", 0, 0, 2, 0.05);
public final DoubleParameter SPEED = new DoubleParameter("speed", "PARTICLE_PARAM_SPEED", 0.01, 0, 1, 0.01);
public final GradientParameter COLORS = new GradientParameter("colors", "PARTICLE_PARAM_COLORS", 8);
public final DoubleParameter OFFSET_X = new DoubleParameter("offsetX", "PARTICLE_PARAM_OFFSET_X", 0, -3, 3, 0.1);
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<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);
public final ChoiceParameter<Gliding> GLIDING = ChoiceParameter.ofEnum("gliding", "PARTICLE_PARAM_GLIDING", Gliding.ANY);
public final ChoiceParameter<Ground> GROUND = ChoiceParameter.ofEnum("ground", "PARTICLE_PARAM_GROUND", Ground.ANY);
public final ChoiceParameter<Sneak> SNEAK = ChoiceParameter.ofEnum("sneak", "PARTICLE_PARAM_SNEAK", Sneak.ANY);
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,
OFFSET_X, OFFSET_Y, OFFSET_Z, INTERVAL, TICK_MODE,
MOVEMENT, GLIDING, GROUND, SNEAK, VISIBILITY
);
}