Turn reward touches into an unlockable Touch setting

Every entry gets a Touch setting next to its design, offering the 16 touches
the rewards carried, each unlocked by its reward. The reward designs that the
builder can now make are removed; only the underwater rain cloud and the
medium, hard and advanced egg hunt designs stay. Reward requirements move to
Unlocks, shared by touches, wing images and cosy smoke. The trail duration
moves to the particle row to make room in the design row.
This commit is contained in:
2026-10-26 14:48:29 +01:00
parent baa278a1f5
commit 8e4730bd12
14 changed files with 263 additions and 175 deletions
@@ -116,6 +116,7 @@ PARTICLE_SLOT_COST = §7Particles per tick§8: §e{0}§8/§7{1}
PARTICLE_ENTRY_EDITOR = §0{0}
PARTICLE_ENTRY_DESIGN = §7Design§8: {0}
PARTICLE_ENTRY_PARTICLE = §7Particle§8: {0}
PARTICLE_ENTRY_TOUCH = §7Touch§8: {0}
PARTICLE_ENTRY_LOCKED = §cDesign locked, not shown
PARTICLE_ENTRY_COST = §7Particles per tick§8: §e{0}
PARTICLE_ENTRY_COST_SHARE = §7This entry§8: §e{0}
@@ -137,6 +138,7 @@ PARTICLE_DESIGN_WING = §fWings
PARTICLE_DESIGN_CLOUD = §fCloud
PARTICLE_PARAM_PARTICLE = §fParticle
PARTICLE_PARAM_TOUCH = §fTouch
PARTICLE_PARAM_COUNT = §fAmount
PARTICLE_PARAM_SPREAD_X = §fSpread X
PARTICLE_PARAM_SPREAD_Y = §fSpread Y
@@ -198,6 +200,27 @@ PARTICLE_CHOICE_IMAGE_SWORD = §fSword
PARTICLE_CHOICE_IMAGE_BAT = §fBat
PARTICLE_CHOICE_IMAGE_BUTTERFLY = §fButterfly
PARTICLE_CHOICE_IMAGE_FAIRY = §fFairy
PARTICLE_CHOICE_IMAGE_MWGL = §fMiniWarGear League
PARTICLE_CHOICE_IMAGE_SW = §fSteamWar
PARTICLE_CHOICE_IMAGE_WGS = §fWarGear Season
PARTICLE_CHOICE_IMAGE_SWORD_CROSSED = §fCrossed Swords
PARTICLE_CHOICE_TOUCH_NONE = §7None
PARTICLE_CHOICE_TOUCH_JUMP_CLOUD = §fCloud puff on jumping
PARTICLE_CHOICE_TOUCH_JUMP_WINGS = §5Wing beat on jumping
PARTICLE_CHOICE_TOUCH_LANDING_SPLASH = §bSplash on landing
PARTICLE_CHOICE_TOUCH_CRASH_LANDING = §cCrash landing
PARTICLE_CHOICE_TOUCH_LAVA_LANDING = §6Lava on landing
PARTICLE_CHOICE_TOUCH_OBSIDIAN_LANDING = §5Obsidian tears on landing
PARTICLE_CHOICE_TOUCH_GLYPH_BURST = §dGlyph burst on sneaking
PARTICLE_CHOICE_TOUCH_WET_FOOTPRINTS = §bWet footprints
PARTICLE_CHOICE_TOUCH_CLOUD_FOOTPRINTS = §fCloud footprints
PARTICLE_CHOICE_TOUCH_BLOSSOM_FOOTPRINTS = §aBlossom footprints
PARTICLE_CHOICE_TOUCH_WATER_LINK = §bWater link to others with it
PARTICLE_CHOICE_TOUCH_BUBBLE_LINK = §3Bubble link to others with it
PARTICLE_CHOICE_TOUCH_GLYPH_PULL = §dGlyphs while standing still
PARTICLE_CHOICE_TOUCH_STILL_SHIMMER = §3Shimmer while standing still
PARTICLE_CHOICE_TOUCH_HEARTBEAT = §cHeartbeat while standing still
PARTICLE_CHOICE_TOUCH_DECEMBER_SNOW = §fSnow in December
PARTICLE_CHOICE_COLORING_OVER_TIME = §fChange over time
PARTICLE_CHOICE_COLORING_ALONG_WING = §fFrom the back to the tips
PARTICLE_CHOICE_MOTION_NONE = §fNone
@@ -373,21 +396,11 @@ PARTICLE_TEAM_PULSE_LOGO = §fPulse Logo
PARTICLE_TEAM_PULSE_HEART_BEAT = §cHeart Beat
PARTICLE_EVENT_ENCHANTING = §cEnchantment
PARTICLE_EVENT_CLOUD = §fClouds
PARTICLE_EVENT_SMOKE = §7Smoke
PARTICLE_EVENT_WATER = §bWater
PARTICLE_EVENT_LAVA = §cLava
PARTICLE_EVENT_WINGS = §fWings
PARTICLE_EVENT_RAIN_CLOUD = §fRandom Raincloud
PARTICLE_EVENT_WGS = §fWGS
PARTICLE_EVENT_WARGEARCLASH = §fClash
PARTICLE_EGG_HUNT_EASY = §aGreenery
PARTICLE_EGG_HUNT_MEDIUM = §eHide and Seek
PARTICLE_EGG_HUNT_HARD = §cOrbit
PARTICLE_EGG_HUNT_EXTREME = §5Purple rain
PARTICLE_EGG_HUNT_ADVANCED = §5Lift off
PARTICLE_EGG_HUNT_HALF = §fThe hunt is on
JUMP_AND_RUN_PROGRESS = §e{0}§8/§f{1} §c{2} §7{3}
JUMP_AND_RUN_CANCEL = {0}
@@ -116,6 +116,7 @@ PARTICLE_SLOT_COST = §7Partikel pro Tick§8: §e{0}§8/§7{1}
PARTICLE_ENTRY_EDITOR = §0{0}
PARTICLE_ENTRY_DESIGN = §7Design§8: {0}
PARTICLE_ENTRY_PARTICLE = §7Partikel§8: {0}
PARTICLE_ENTRY_TOUCH = §7Akzent§8: {0}
PARTICLE_ENTRY_LOCKED = §cDesign gesperrt, wird nicht angezeigt
PARTICLE_ENTRY_COST = §7Partikel pro Tick§8: §e{0}
PARTICLE_ENTRY_COST_SHARE = §7Dieser Eintrag§8: §e{0}
@@ -137,6 +138,7 @@ PARTICLE_DESIGN_WING = §fFlügel
PARTICLE_DESIGN_CLOUD = §fWolke
PARTICLE_PARAM_PARTICLE = §fPartikel
PARTICLE_PARAM_TOUCH = §fAkzent
PARTICLE_PARAM_COUNT = §fAnzahl
PARTICLE_PARAM_SPREAD_X = §fStreuung X
PARTICLE_PARAM_SPREAD_Y = §fStreuung Y
@@ -198,6 +200,27 @@ PARTICLE_CHOICE_IMAGE_SWORD = §fSchwert
PARTICLE_CHOICE_IMAGE_BAT = §fFledermaus
PARTICLE_CHOICE_IMAGE_BUTTERFLY = §fSchmetterling
PARTICLE_CHOICE_IMAGE_FAIRY = §fFee
PARTICLE_CHOICE_IMAGE_MWGL = §fMiniWarGear Liga
PARTICLE_CHOICE_IMAGE_SW = §fSteamWar
PARTICLE_CHOICE_IMAGE_WGS = §fWarGear Saison
PARTICLE_CHOICE_IMAGE_SWORD_CROSSED = §fGekreuzte Schwerter
PARTICLE_CHOICE_TOUCH_NONE = §7Keiner
PARTICLE_CHOICE_TOUCH_JUMP_CLOUD = §fWolke beim Springen
PARTICLE_CHOICE_TOUCH_JUMP_WINGS = §5Flügelschlag beim Springen
PARTICLE_CHOICE_TOUCH_LANDING_SPLASH = §bSpritzer beim Landen
PARTICLE_CHOICE_TOUCH_CRASH_LANDING = §cBruchlandung
PARTICLE_CHOICE_TOUCH_LAVA_LANDING = §6Lava beim Landen
PARTICLE_CHOICE_TOUCH_OBSIDIAN_LANDING = §5Obsidiantränen beim Landen
PARTICLE_CHOICE_TOUCH_GLYPH_BURST = §dGlyphen beim Schleichen
PARTICLE_CHOICE_TOUCH_WET_FOOTPRINTS = §bNasse Fußspuren
PARTICLE_CHOICE_TOUCH_CLOUD_FOOTPRINTS = §fWolkenfußspuren
PARTICLE_CHOICE_TOUCH_BLOSSOM_FOOTPRINTS = §aBlütenfußspuren
PARTICLE_CHOICE_TOUCH_WATER_LINK = §bWasserband zu anderen damit
PARTICLE_CHOICE_TOUCH_BUBBLE_LINK = §3Blasenband zu anderen damit
PARTICLE_CHOICE_TOUCH_GLYPH_PULL = §dGlyphen im Stillstand
PARTICLE_CHOICE_TOUCH_STILL_SHIMMER = §3Schimmer im Stillstand
PARTICLE_CHOICE_TOUCH_HEARTBEAT = §cHerzschlag im Stillstand
PARTICLE_CHOICE_TOUCH_DECEMBER_SNOW = §fSchnee im Dezember
PARTICLE_CHOICE_COLORING_OVER_TIME = §fWechseln mit der Zeit
PARTICLE_CHOICE_COLORING_ALONG_WING = §fVom Rücken zu den Spitzen
PARTICLE_CHOICE_MOTION_NONE = §fKeine
@@ -362,21 +385,11 @@ PARTICLE_PLAYER_10697_AURA = §7Rauchgranate
PARTICLE_TEAM_PULSE_HEART_BEAT = §cHerzschlag
PARTICLE_EVENT_ENCHANTING = §cVerzaubert
PARTICLE_EVENT_CLOUD = §fWolken
PARTICLE_EVENT_SMOKE = §7Rauch
PARTICLE_EVENT_WATER = §bWasser
PARTICLE_EVENT_LAVA = §cLava
PARTICLE_EVENT_WINGS = §fFlügel
PARTICLE_EVENT_RAIN_CLOUD = §fRegenwolke
PARTICLE_EVENT_WGS = §fWGS
PARTICLE_EVENT_WARGEARCLASH = §fClash
PARTICLE_EGG_HUNT_EASY = §aGrünplfanzen
PARTICLE_EGG_HUNT_MEDIUM = §eVersteckspiel
PARTICLE_EGG_HUNT_HARD = §cOrbit
PARTICLE_EGG_HUNT_EXTREME = §5Lila regen
PARTICLE_EGG_HUNT_ADVANCED = §5Abheben
PARTICLE_EGG_HUNT_HALF = §fDie Jagd ist eröffnet
JUMP_AND_RUN_FINISHED = §aBeendet in {0} mit {1} Fails
JUMP_AND_RUN_PERSONAL_BEST = §aNice! Du hast deinen Rekord um {0} verbessert!
@@ -75,10 +75,10 @@ public class Designs {
new ChoiceParameter.Choice<>("butterfly", WingShape.drawn(DrawnWings.BUTTERFLY, Material.PINK_PETALS), false),
new ChoiceParameter.Choice<>("fairy", WingShape.drawn(DrawnWings.FAIRY, Material.ALLAY_SPAWN_EGG), false),
// Tournament wing images, unlocked by the placement that earns their reward
new ChoiceParameter.Choice<>("mwgl", WingShape.image(WingDesign.MWGL, Material.IRON_SWORD), false, () -> RewardDesigns.EVENT_PLACEMENT_MINI_WARGEAR_LIGA.getRequirement()),
new ChoiceParameter.Choice<>("sw", WingShape.image(WingDesign.SW, Material.CYAN_CARPET), false, () -> RewardDesigns.EVENT_PLACEMENT_UNDERWATER_MWG.getRequirement()),
new ChoiceParameter.Choice<>("wgs", WingShape.image(WingDesign.WGS, Material.DIAMOND_HELMET), false, () -> RewardDesigns.EVENT_PLACEMENT_WARGEAR_SEASON_2022.getRequirement()),
new ChoiceParameter.Choice<>("sword_crossed", WingShape.image(WingDesign.SWORD_CROSSED, Material.GOLDEN_SWORD), false, () -> RewardDesigns.EVENT_PLACEMENT_WARGEAR_CLASH.getRequirement()),
new ChoiceParameter.Choice<>("mwgl", WingShape.image(WingDesign.MWGL, Material.IRON_SWORD), false, Unlocks.MINI_WARGEAR_LIGA_PLACEMENT),
new ChoiceParameter.Choice<>("sw", WingShape.image(WingDesign.SW, Material.CYAN_CARPET), false, Unlocks.UNDERWATER_MWG_PLACEMENT),
new ChoiceParameter.Choice<>("wgs", WingShape.image(WingDesign.WGS, Material.DIAMOND_HELMET), false, Unlocks.WARGEAR_SEASON_2022_PLACEMENT),
new ChoiceParameter.Choice<>("sword_crossed", WingShape.image(WingDesign.SWORD_CROSSED, Material.GOLDEN_SWORD), false, Unlocks.WARGEAR_CLASH_PLACEMENT),
new ChoiceParameter.Choice<>("eclipse", WingShape.image(WingDesign.ECLIPSE), true),
new ChoiceParameter.Choice<>("pl", WingShape.image(WingDesign.PL), true),
new ChoiceParameter.Choice<>("plompa_easter", WingShape.image(WingDesign.PlompaEasterWings), true),
@@ -204,8 +204,10 @@ public class EntryParameters {
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 ChoiceParameter<Touch> TOUCH = new ChoiceParameter<>("touch", "PARTICLE_PARAM_TOUCH", Touches.NONE, Touches.choices());
public final List<Parameter<?>> ALL = List.of(
PARTICLE, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS, BLOCK,
TOUCH, PARTICLE, 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
@@ -32,8 +32,6 @@ public class FixedDesign extends ParticleDesign {
private final int points;
private final Consumer<ParameterValues> defaults;
private final Supplier<ParticleElement> shape;
private Supplier<ParticleElement> touch;
private int touchPoints;
public FixedDesign(String id, Material icon, String nameKey, ParticleRequirement requirement, int points, Consumer<ParameterValues> defaults, Supplier<ParticleElement> shape) {
super(id, icon, nameKey, DesignVisibility.REWARD, requirement);
@@ -47,21 +45,9 @@ public class FixedDesign extends ParticleDesign {
return shape.get();
}
// Adds the reward's unique touch; its points count towards the slot budget
public FixedDesign withTouch(int touchPoints, Supplier<ParticleElement> touch) {
this.touchPoints = touchPoints;
this.touch = touch;
return this;
}
@Override
public ParticleElement touch() {
return touch == null ? null : touch.get();
}
@Override
public int points(ParameterValues values) {
return points + touchPoints;
return points;
}
@Override
@@ -72,11 +72,6 @@ public abstract class ParticleDesign {
return false;
}
// An extra the builder cannot make, shown next to the design's look and checked every tick so it never misses
// a jump or landing; null for designs without one
public ParticleElement touch() {
return null;
}
// Called after a design setting changed, to adjust others that depend on it
public void adjust(Parameter<?> changed, ParameterValues values) {
@@ -185,7 +185,7 @@ public enum ParticleType {
public ParticleRequirement getRequirement() {
return switch (this) {
case CHERRY_LEAVES, PALE_OAK_LEAVES, TINTED_LEAVES -> ParticleRequirement.SERVER_TEAM;
case CAMPFIRE_COSY_SMOKE -> RewardDesigns.EVENT_PARTICIPATION_HELLS_BELLS.getRequirement();
case CAMPFIRE_COSY_SMOKE -> Unlocks.HELLS_BELLS.get();
default -> ParticleRequirement.NO_REQUIRMENT;
};
}
@@ -19,107 +19,26 @@
package de.steamwar.lobby.particle.design;
import de.steamwar.lobby.particle.Gradient;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleRequirement;
import de.steamwar.lobby.particle.WingDesign;
import de.steamwar.lobby.particle.elements.*;
import de.steamwar.lobby.particle.elements.custom.HearthBeat;
import de.steamwar.lobby.particle.elements.custom.NonMoving;
import de.steamwar.lobby.particle.elements.custom.PulseShimmer;
import de.steamwar.lobby.particle.elements.touch.*;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.time.Month;
import java.util.List;
import java.util.function.Consumer;
import static de.steamwar.lobby.particle.design.EntryParameters.*;
// Designs earned in tournaments, events and the Easter egg hunt. Rewards whose look the basic designs can build get a
// touch the builder cannot make: bursts on jumping, landing or sneaking, footprints, a link to team-mates, a seasonal
// extra, a preset-only shape or aimed glyphs.
// Reward designs the builder cannot make. Other rewards unlock ingredients instead: wing images, particles and touches.
@UtilityClass
public class RewardDesigns {
private final Consumer<ParameterValues> MOVE = values -> {
};
private final Consumer<ParameterValues> ALWAYS = values -> values.set(TICK_MODE, TickMode.ALWAYS);
private final Consumer<ParameterValues> WINGS = values -> values
.set(TICK_MODE, TickMode.ALWAYS)
.set(INTERVAL, 20)
.set(GLIDING, Gliding.NOT_GLIDING);
public final ParticleDesign EVENT_PLACEMENT_WARGEAR_SEASON = new FixedDesign("event_placement_wargear_season", Material.ENCHANTING_TABLE, "PARTICLE_EVENT_ENCHANTING",
ParticleRequirement.eventPlacement(22, 12, 285, 54), 5, MOVE,
() -> new Cloud(new Circle(new LocationMutator(new SimpleParticle(Particle.ENCHANT, 0, 0, 0, 0.01), 0, 1.1, 0))))
.withTouch(3, () -> new NonMoving(new Glyphs(Glyphs.Path.INTO_PLAYER, 3, 1.5)));
public final ParticleDesign EVENT_PLACEMENT_AIRSHIP = new FixedDesign("event_placement_airship", Material.SNOWBALL, "PARTICLE_EVENT_CLOUD",
ParticleRequirement.eventPlacement(26, 205, 9, 54, 120, 292), 5, MOVE,
() -> new Circle(new LocationMutator(new SimpleParticle(Particle.CLOUD, 0, 0, 0, 0.01), 0, 2.2, 0)))
.withTouch(1, () -> new EventBurst(EventBurst.Trigger.JUMP, new SimpleParticle(Particle.CLOUD, 0.3F, 0.05F, 0.3F, 0.02, 12)));
public final ParticleDesign EVENT_PLACEMENT_HELLS_BELLS = new FixedDesign("event_placement_hells_bells", Material.TNT_MINECART, "PARTICLE_EVENT_SMOKE",
ParticleRequirement.eventPlacement(28, 205, 9, 11), 5, MOVE,
() -> new Cloud(new Circle(new LocationMutator(new SimpleParticle(Particle.CAMPFIRE_COSY_SMOKE, 0, 0, 0, 0.01), 0, 2.2, 0))));
public final ParticleDesign EVENT_PLACEMENT_UNDERWATER = new FixedDesign("event_placement_underwater", Material.PRISMARINE_SHARD, "PARTICLE_EVENT_WATER",
ParticleRequirement.eventPlacement(31, 9, 210, 520), 5, MOVE,
() -> new Cloud(new SimpleParticle(Particle.DRIPPING_WATER)))
.withTouch(1, () -> new EventBurst(EventBurst.Trigger.LAND, 1, new Ring(new SimpleParticle(Particle.SPLASH, 0, 0, 0, 0, 3), 1, 16)));
public final ParticleDesign EVENT_PLACEMENT_ADVENT_WAR_SHIP = new FixedDesign("event_placement_advent_war_ship", Material.PRISMARINE_CRYSTALS, "PARTICLE_EVENT_WATER",
ParticleRequirement.eventPlacement(32, 9, 205, 210), 10, ALWAYS,
() -> new LocationMutator(new Circle(new Group(new SimpleParticle(Particle.DRIPPING_WATER), new SimpleParticle(Particle.UNDERWATER, 0.2F, 0.2F, 0.2F, 0.01))), 0, 1.1, 0))
.withTouch(4, () -> new Footprints(new SimpleParticle(Particle.FISHING, 0.05F, 0, 0.05F, 0, 1), 40));
public final ParticleDesign EVENT_PLACEMENT_MINI_WARGEAR_LIGA = wings("event_placement_mini_wargear_liga", Material.IRON_SWORD, "PARTICLE_EVENT_WINGS",
ParticleRequirement.eventPlacement(33, 9, 34, 205), Particle.UNDERWATER, 0.15, WingDesign.MWGL);
public final ParticleDesign EVENT_PLACEMENT_ABSTURZ = wings("event_placement_absturz", Material.FEATHER, "PARTICLE_EVENT_WINGS",
ParticleRequirement.eventPlacement(34, 210, 205, 527, 286), Particle.CRIT, 0.15, WingDesign.SIMPLE)
.withTouch(1, () -> new EventBurst(EventBurst.Trigger.LAND, 3, new Group(new SimpleParticle(Particle.EXPLOSION, 0, 0, 0, 0, 1),
new Ring(new SimpleParticle(Particle.CRIT, 0, 0.1F, 0, 0.1, 2), 1.2, 16))));
public final ParticleDesign EVENT_PLACEMENT_UNDERWATER_MWG = wings("event_placement_underwater_mwg", Material.CYAN_CARPET, "PARTICLE_EVENT_RAIN_CLOUD",
ParticleRequirement.eventPlacement(35, 9, 210), Particle.CRIT, 0.15, WingDesign.SW);
public final ParticleDesign EVENT_PLACEMENT_WARGEAR_SEASON_2022 = wings("event_placement_wargear_season_2022", Material.DIAMOND_HELMET, "PARTICLE_EVENT_WGS",
ParticleRequirement.eventPlacement(37, 285, 210, 122), Particle.FIREWORK, 0.15, WingDesign.WGS);
public final ParticleDesign EVENT_PLACEMENT_WARGEAR_CLASH = wings("event_placement_wargear_clash", Material.GOLDEN_SWORD, "PARTICLE_EVENT_WARGEARCLASH",
ParticleRequirement.eventPlacement(38, 210, 158, 167, 286), Particle.CRIT, 0.1, WingDesign.SWORD_CROSSED);
// For taking part in any event
public final ParticleDesign EVENT_PARTICIPATION_WATER = new FixedDesign("event_participation_water", Material.WATER_BUCKET, "PARTICLE_EVENT_WATER",
ParticleRequirement.EVENT_PARTICIPATION, 5, ALWAYS,
() -> new Circle(new LocationMutator(new SimpleParticle(Particle.DRIPPING_WATER), 0, 1.1, 0)))
.withTouch(5, () -> new TeamLink("event_participation_water", new SimpleParticle(Particle.DRIPPING_WATER, 0, 0, 0, 0, 1)));
public final ParticleDesign EVENT_PARTICIPATION_LAVA = new FixedDesign("event_participation_lava", Material.LAVA_BUCKET, "PARTICLE_EVENT_LAVA",
ParticleRequirement.EVENT_PARTICIPATION, 5, ALWAYS,
() -> new Circle(new LocationMutator(new SimpleParticle(Particle.DRIPPING_LAVA), 0, 1.1, 0)))
.withTouch(1, () -> new EventBurst(EventBurst.Trigger.LAND, 1, new Group(new SimpleParticle(Particle.LAVA, 0.2F, 0, 0.2F, 0, 4),
new SimpleParticle(Particle.SMOKE, 0.2F, 0.1F, 0.2F, 0.02, 6))));
public final ParticleDesign EVENT_PARTICIPATION_WARGEAR_SEASON = new FixedDesign("event_participation_wargear_season", Material.BOOK, "PARTICLE_EVENT_ENCHANTING",
ParticleRequirement.eventParticipation(22), 5, MOVE,
() -> new SimpleParticle(Particle.ENCHANT))
.withTouch(1, () -> new EventBurst(EventBurst.Trigger.SNEAK, new Glyphs(Glyphs.Path.OUT_OF_PLAYER, 16, 1.5)));
public final ParticleDesign EVENT_PARTICIPATION_AIRSHIP = new FixedDesign("event_participation_airship", Material.SNOW_BLOCK, "PARTICLE_EVENT_CLOUD",
ParticleRequirement.eventParticipation(26), 5, MOVE,
() -> new SimpleParticle(Particle.CLOUD))
.withTouch(4, () -> new Footprints(new SimpleParticle(Particle.CLOUD, 0.05F, 0, 0.05F, 0, 1), 40));
public final ParticleDesign EVENT_PARTICIPATION_HELLS_BELLS = new FixedDesign("event_participation_hells_bells", Material.TNT, "PARTICLE_EVENT_SMOKE",
ParticleRequirement.eventParticipation(28), 5, MOVE,
() -> new Circle(new SimpleParticle(Particle.CAMPFIRE_COSY_SMOKE, 0, 0, 0, 0.01)));
public final ParticleDesign EVENT_PARTICIPATION_UNDERWATER = new FixedDesign("event_participation_underwater", Material.PRISMARINE_BRICKS, "PARTICLE_EVENT_WATER",
ParticleRequirement.eventParticipation(31), 5, MOVE,
() -> new SimpleParticle(Particle.DRIPPING_WATER))
.withTouch(5, () -> new TeamLink("event_participation_underwater", new SimpleParticle(Particle.DRIPPING_WATER, 0, 0, 0, 0, 1)));
public final ParticleDesign EVENT_PARTICIPATION_ADVENT_WAR_SHIP = new FixedDesign("event_participation_advent_war_ship", Material.PRISMARINE_WALL, "PARTICLE_EVENT_WATER",
ParticleRequirement.eventParticipation(32), 10, MOVE,
() -> new Group(new SimpleParticle(Particle.DRIPPING_WATER), new SimpleParticle(Particle.UNDERWATER, 0.2F, 0.2F, 0.2F, 0.01)))
.withTouch(2, () -> new Seasonal(Month.DECEMBER, new LocationMutator(new SimpleParticle(Particle.SNOWFLAKE, 1.2F, 0.3F, 1.2F, 0.01, 2), 0, 2.4, 0)));
public final ParticleDesign EVENT_PARTICIPATION_MINI_WARGEAR_LIGA = new FixedDesign("event_participation_mini_wargear_liga", Material.PRISMARINE_SLAB, "PARTICLE_EVENT_WATER",
ParticleRequirement.eventParticipation(33), 5, MOVE,
() -> new Circle(new SimpleParticle(Particle.UNDERWATER, 0, 0, 0, 0)))
.withTouch(3, () -> new NonMoving(new PulseShimmer(new SimpleParticle(Particle.UNDERWATER, 0, 0, 0, 0, 1), 80, 2)));
public final ParticleDesign EVENT_PARTICIPATION_UNDERWATER_MWG = new FixedDesign("event_participation_underwater_mwg", Material.BLUE_CARPET, "PARTICLE_EVENT_RAIN_CLOUD",
ParticleRequirement.eventParticipation(35), 15, ALWAYS,
() -> new RandomParticle(
@@ -136,25 +55,12 @@ public class RewardDesigns {
rainCloud(null, Particle.SNOWFLAKE, 5),
rainCloud(null, Particle.TOTEM_OF_UNDYING, 2)
));
public final ParticleDesign EVENT_PARTICIPATION_WARGEAR_SEASON_2022 = wings("event_participation_wargear_season_2022", Material.DIAMOND_SWORD, "PARTICLE_EVENT_WINGS",
ParticleRequirement.eventParticipation(37), Particle.DRAGON_BREATH, 0.15, WingDesign.COMPLEX)
.withTouch(2, () -> new Delayed(new Wing(new SimpleParticle(Particle.DRAGON_BREATH, 0, 0, 0, 0, 1), 0.15, WingDesign.COMPLEX, false, 100, Wing.Motion.JUMP_BEAT), 2));
public final ParticleDesign EGG_HUNT_EASY = new FixedDesign("egg_hunt_easy", Material.LIME_CONCRETE_POWDER, "PARTICLE_EGG_HUNT_EASY",
ParticleRequirement.EGG_HUNT_EASY, 1, ALWAYS,
() -> new LocationMutator(new SimpleParticle(Particle.FALLING_SPORE_BLOSSOM, 0.5F, 0.1f, 0.5F, 1, 1), 0, 2.2, 0))
.withTouch(4, () -> new Footprints(new SimpleParticle(Particle.SPORE_BLOSSOM_AIR, 0.05F, 0, 0.05F, 0, 1), 40));
public final ParticleDesign EGG_HUNT_MEDIUM = new FixedDesign("egg_hunt_medium", Material.YELLOW_CONCRETE_POWDER, "PARTICLE_EGG_HUNT_MEDIUM",
ParticleRequirement.EGG_HUNT_MEDIUM, 5, ALWAYS.andThen(values -> values.set(GROUND, Ground.ON_GROUND).set(SNEAK, Sneak.SNEAKING)),
() -> new SimpleParticle(Particle.GLOW_SQUID_INK, 0.4F, 0.9F, 0.4F, 0.01));
public final ParticleDesign EGG_HUNT_HARD = new FixedDesign("egg_hunt_hard", Material.RED_CONCRETE_POWDER, "PARTICLE_EGG_HUNT_HARD",
ParticleRequirement.EGG_HUNT_HARD, 2, ALWAYS,
() -> new LocationMutator(new Circle(new DoubleCircle(new DustParticle(Particle.DUST, 0, 0, 0, 0.01, 1, null), new DustParticle(Particle.DUST, 0, 0, 0, 0.01, 1, null)), 0.8, 0.5), 0, 0.5, 0));
public final ParticleDesign EGG_HUNT_EXTREME = new FixedDesign("egg_hunt_extreme", Material.PURPLE_CONCRETE_POWDER, "PARTICLE_EGG_HUNT_EXTREME",
ParticleRequirement.EGG_HUNT_EXTREME, 1, ALWAYS,
() -> new LocationMutator(new SimpleParticle(Particle.FALLING_OBSIDIAN_TEAR, 0.5F, 0.1F, 0.5F, 1, 1), 0, 2.2, 0))
.withTouch(1, () -> new EventBurst(EventBurst.Trigger.LAND, 1, new Group(new Ring(new SimpleParticle(Particle.WITCH, 0, 0.05F, 0, 0, 2), 1, 16),
new SimpleParticle(Particle.LANDING_OBSIDIAN_TEAR, 0.4F, 0, 0.4F, 0, 6))));
public final ParticleDesign EGG_HUNT_ADVANCED = new FixedDesign("egg_hunt_advanced", Material.BLACK_CONCRETE_POWDER, "PARTICLE_EGG_HUNT_ADVANCED",
ParticleRequirement.EGG_HUNT_ADVANCED, 2, ALWAYS.andThen(values -> values.set(SNEAK, Sneak.SNEAKING)),
() -> new LocationMutator(new Group(
@@ -165,19 +71,6 @@ public class RewardDesigns {
player.setVelocity(player.getVelocity().add(new Vector(0, 0.1, 0)));
}
), 0, -0.2, 0));
// Hearts above the head while sneaking on the ground, a heart cloud ring while in the air
public final ParticleDesign EGG_HUNT_HALF = new FixedDesign("egg_hunt_half", Material.EGG, "PARTICLE_EGG_HUNT_HALF",
ParticleRequirement.EGG_HUNT_FOUND_HALF, 5, ALWAYS,
() -> new Group(
new Sneaking(new Floor(new LocationMutator(new SimpleParticle(Particle.HEART, 0, 0, 0, 0.01), 0, 1.7, 0))),
new NonFloor(new NonFlying(new LocationMutator(new Cloud(new Circle(new SimpleParticle(Particle.HEART, 0, 0, 0, 0.01))), 0, 0.5, 0)))
))
.withTouch(1, () -> new NonMoving(new HearthBeat(new DustParticle(Particle.DUST, 0, 0, 0, 0, 1, new Gradient(java.awt.Color.RED)), 80)));
private FixedDesign wings(String id, Material icon, String nameKey, ParticleRequirement requirement, Particle particle, double size, WingDesign wingDesign) {
return new FixedDesign(id, icon, nameKey, requirement, wingDesign.getVectors().length, WINGS,
() -> new Wing(new SimpleParticle(particle, 0, 0, 0, 0, 1), size, wingDesign));
}
// A cloud above the head that drips the given particle; rain is an optional second particle inside the cloud
private ParticleElement rainCloud(Particle rain, Particle drip, int dripCount) {
@@ -188,15 +81,6 @@ public class RewardDesigns {
}
public List<ParticleDesign> all() {
return List.of(
EVENT_PLACEMENT_WARGEAR_SEASON, EVENT_PLACEMENT_AIRSHIP, EVENT_PLACEMENT_HELLS_BELLS, EVENT_PLACEMENT_UNDERWATER,
EVENT_PLACEMENT_ADVENT_WAR_SHIP, EVENT_PLACEMENT_MINI_WARGEAR_LIGA, EVENT_PLACEMENT_ABSTURZ, EVENT_PLACEMENT_UNDERWATER_MWG,
EVENT_PLACEMENT_WARGEAR_SEASON_2022, EVENT_PLACEMENT_WARGEAR_CLASH,
EVENT_PARTICIPATION_WATER, EVENT_PARTICIPATION_LAVA,
EVENT_PARTICIPATION_WARGEAR_SEASON, EVENT_PARTICIPATION_AIRSHIP, EVENT_PARTICIPATION_HELLS_BELLS, EVENT_PARTICIPATION_UNDERWATER,
EVENT_PARTICIPATION_ADVENT_WAR_SHIP, EVENT_PARTICIPATION_MINI_WARGEAR_LIGA, EVENT_PARTICIPATION_UNDERWATER_MWG,
EVENT_PARTICIPATION_WARGEAR_SEASON_2022,
EGG_HUNT_EASY, EGG_HUNT_MEDIUM, EGG_HUNT_HARD, EGG_HUNT_EXTREME, EGG_HUNT_ADVANCED, EGG_HUNT_HALF
);
return List.of(EVENT_PARTICIPATION_UNDERWATER_MWG, EGG_HUNT_MEDIUM, EGG_HUNT_HARD, EGG_HUNT_ADVANCED);
}
}
@@ -0,0 +1,30 @@
/*
* 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.ParticleRequirement;
import org.bukkit.Material;
import java.util.function.Supplier;
// An extra effect an entry can carry next to its design, unlocked by a reward. Touches run every tick, independent
// of the entry's conditions and timing, so their points count fully towards the slot budget.
public record Touch(String id, Material icon, int points, Supplier<ParticleRequirement> requirement, Supplier<ParticleElement> element) {
}
@@ -0,0 +1,89 @@
/*
* 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.Gradient;
import de.steamwar.lobby.particle.ParticleRequirement;
import de.steamwar.lobby.particle.WingDesign;
import de.steamwar.lobby.particle.elements.*;
import de.steamwar.lobby.particle.elements.custom.HearthBeat;
import de.steamwar.lobby.particle.elements.custom.NonMoving;
import de.steamwar.lobby.particle.elements.custom.PulseShimmer;
import de.steamwar.lobby.particle.elements.touch.*;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
import org.bukkit.Particle;
import java.time.Month;
import java.util.List;
// The touches of the entries' touch setting, each unlocked by the event or egg hunt reward that used to carry it
@UtilityClass
public class Touches {
public final Touch NONE = new Touch("none", Material.GRAY_DYE, 0, () -> ParticleRequirement.NO_REQUIRMENT, () -> null);
private final List<Touch> ALL = List.of(
NONE,
// Bursts on jumping, landing or sneaking
new Touch("jump_cloud", Material.SNOWBALL, 1, Unlocks.AIRSHIP_PLACEMENT,
() -> new EventBurst(EventBurst.Trigger.JUMP, new SimpleParticle(Particle.CLOUD, 0.3F, 0.05F, 0.3F, 0.02, 12))),
new Touch("jump_wings", Material.DRAGON_BREATH, 2, Unlocks.WARGEAR_SEASON_2022,
() -> new Delayed(new Wing(new SimpleParticle(Particle.DRAGON_BREATH, 0, 0, 0, 0, 1), 0.15, WingDesign.COMPLEX, false, 100, Wing.Motion.JUMP_BEAT), 2)),
new Touch("landing_splash", Material.PRISMARINE_SHARD, 1, Unlocks.UNDERWATER_PLACEMENT,
() -> new EventBurst(EventBurst.Trigger.LAND, 1, new Ring(new SimpleParticle(Particle.SPLASH, 0, 0, 0, 0, 3), 1, 16))),
new Touch("crash_landing", Material.FEATHER, 1, Unlocks.ABSTURZ_PLACEMENT,
() -> new EventBurst(EventBurst.Trigger.LAND, 3, new Group(new SimpleParticle(Particle.EXPLOSION, 0, 0, 0, 0, 1),
new Ring(new SimpleParticle(Particle.CRIT, 0, 0.1F, 0, 0.1, 2), 1.2, 16)))),
new Touch("lava_landing", Material.LAVA_BUCKET, 1, Unlocks.ANY_EVENT,
() -> new EventBurst(EventBurst.Trigger.LAND, 1, new Group(new SimpleParticle(Particle.LAVA, 0.2F, 0, 0.2F, 0, 4),
new SimpleParticle(Particle.SMOKE, 0.2F, 0.1F, 0.2F, 0.02, 6)))),
new Touch("obsidian_landing", Material.CRYING_OBSIDIAN, 1, Unlocks.EGG_HUNT_EXTREME,
() -> new EventBurst(EventBurst.Trigger.LAND, 1, new Group(new Ring(new SimpleParticle(Particle.WITCH, 0, 0.05F, 0, 0, 2), 1, 16),
new SimpleParticle(Particle.LANDING_OBSIDIAN_TEAR, 0.4F, 0, 0.4F, 0, 6)))),
new Touch("glyph_burst", Material.BOOK, 1, Unlocks.WARGEAR_SEASON,
() -> new EventBurst(EventBurst.Trigger.SNEAK, new Glyphs(Glyphs.Path.OUT_OF_PLAYER, 16, 1.5))),
// Footprints
new Touch("wet_footprints", Material.PRISMARINE_CRYSTALS, 4, Unlocks.ADVENT_WAR_SHIP_PLACEMENT,
() -> new Footprints(new SimpleParticle(Particle.FISHING, 0.05F, 0, 0.05F, 0, 1), 40)),
new Touch("cloud_footprints", Material.SNOW_BLOCK, 4, Unlocks.AIRSHIP,
() -> new Footprints(new SimpleParticle(Particle.CLOUD, 0.05F, 0, 0.05F, 0, 1), 40)),
new Touch("blossom_footprints", Material.SPORE_BLOSSOM, 4, Unlocks.EGG_HUNT_EASY,
() -> new Footprints(new SimpleParticle(Particle.SPORE_BLOSSOM_AIR, 0.05F, 0, 0.05F, 0, 1), 40)),
// Lines to nearby players with the same touch
new Touch("water_link", Material.WATER_BUCKET, 5, Unlocks.ANY_EVENT,
() -> new TeamLink("water_link", new SimpleParticle(Particle.DRIPPING_WATER, 0, 0, 0, 0, 1))),
new Touch("bubble_link", Material.PRISMARINE_BRICKS, 5, Unlocks.UNDERWATER,
() -> new TeamLink("bubble_link", new SimpleParticle(Particle.BUBBLE_POP, 0, 0, 0, 0, 1))),
// While standing still
new Touch("glyph_pull", Material.ENCHANTING_TABLE, 3, Unlocks.WARGEAR_SEASON_PLACEMENT,
() -> new NonMoving(new Glyphs(Glyphs.Path.INTO_PLAYER, 3, 1.5))),
new Touch("still_shimmer", Material.PRISMARINE_SLAB, 3, Unlocks.MINI_WARGEAR_LIGA,
() -> new NonMoving(new PulseShimmer(new SimpleParticle(Particle.UNDERWATER, 0, 0, 0, 0, 1), 80, 2))),
new Touch("heartbeat", Material.POPPY, 1, Unlocks.EGG_HUNT_HALF,
() -> new NonMoving(new HearthBeat(new DustParticle(Particle.DUST, 0, 0, 0, 0, 1, new Gradient(java.awt.Color.RED)), 80))),
// Seasonal
new Touch("december_snow", Material.POWDER_SNOW_BUCKET, 2, Unlocks.ADVENT_WAR_SHIP,
() -> new Seasonal(Month.DECEMBER, new LocationMutator(new SimpleParticle(Particle.SNOWFLAKE, 1.2F, 0.3F, 1.2F, 0.01, 2), 0, 2.4, 0)))
);
public List<ChoiceParameter.Choice<Touch>> choices() {
return ALL.stream().map(touch -> new ChoiceParameter.Choice<>(touch.id(), touch, false, touch.requirement())).toList();
}
}
@@ -0,0 +1,65 @@
/*
* 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.ParticleRequirement;
import lombok.experimental.UtilityClass;
import java.util.function.Supplier;
// Requirements of the reward ingredients: wing images, particles and touches. Each is created on first use, as event
// requirements look up the event's name.
@UtilityClass
public class Unlocks {
public final Supplier<ParticleRequirement> WARGEAR_SEASON_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(22, 12, 285, 54));
public final Supplier<ParticleRequirement> AIRSHIP_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(26, 205, 9, 54, 120, 292));
public final Supplier<ParticleRequirement> UNDERWATER_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(31, 9, 210, 520));
public final Supplier<ParticleRequirement> ADVENT_WAR_SHIP_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(32, 9, 205, 210));
public final Supplier<ParticleRequirement> MINI_WARGEAR_LIGA_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(33, 9, 34, 205));
public final Supplier<ParticleRequirement> ABSTURZ_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(34, 210, 205, 527, 286));
public final Supplier<ParticleRequirement> UNDERWATER_MWG_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(35, 9, 210));
public final Supplier<ParticleRequirement> WARGEAR_SEASON_2022_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(37, 285, 210, 122));
public final Supplier<ParticleRequirement> WARGEAR_CLASH_PLACEMENT = lazy(() -> ParticleRequirement.eventPlacement(38, 210, 158, 167, 286));
public final Supplier<ParticleRequirement> ANY_EVENT = () -> ParticleRequirement.EVENT_PARTICIPATION;
public final Supplier<ParticleRequirement> WARGEAR_SEASON = lazy(() -> ParticleRequirement.eventParticipation(22));
public final Supplier<ParticleRequirement> AIRSHIP = lazy(() -> ParticleRequirement.eventParticipation(26));
public final Supplier<ParticleRequirement> HELLS_BELLS = lazy(() -> ParticleRequirement.eventParticipation(28));
public final Supplier<ParticleRequirement> UNDERWATER = lazy(() -> ParticleRequirement.eventParticipation(31));
public final Supplier<ParticleRequirement> ADVENT_WAR_SHIP = lazy(() -> ParticleRequirement.eventParticipation(32));
public final Supplier<ParticleRequirement> MINI_WARGEAR_LIGA = lazy(() -> ParticleRequirement.eventParticipation(33));
public final Supplier<ParticleRequirement> WARGEAR_SEASON_2022 = lazy(() -> ParticleRequirement.eventParticipation(37));
public final Supplier<ParticleRequirement> EGG_HUNT_EASY = () -> ParticleRequirement.EGG_HUNT_EASY;
public final Supplier<ParticleRequirement> EGG_HUNT_EXTREME = () -> ParticleRequirement.EGG_HUNT_EXTREME;
public final Supplier<ParticleRequirement> EGG_HUNT_HALF = () -> ParticleRequirement.EGG_HUNT_FOUND_HALF;
private Supplier<ParticleRequirement> lazy(Supplier<ParticleRequirement> create) {
return new Supplier<>() {
private ParticleRequirement requirement;
@Override
public ParticleRequirement get() {
if (requirement == null) requirement = create.get();
return requirement;
}
};
}
}
@@ -119,21 +119,24 @@ class EntryEditor {
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING, new SWItem(design.getIcon(), designName(design, player), List.of(msg("PARTICLE_VALUE_CLICK", player)), false,
clickType -> Pickers.design(player, design, picked -> changeDesign(context, picked), context::reopen)));
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 1, parameterItem(context, TOUCH));
if (design.usesEntryParticle()) {
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 1, parameterItem(context, PARTICLE));
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, parameterItem(context, PARTICLE));
ParticleType type = entry.getValues().get(PARTICLE);
if (type.usesColors()) {
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, parameterItem(context, COLORS));
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 3, parameterItem(context, COLORS));
} else if (type.usesBlock()) {
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, parameterItem(context, BLOCK));
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 3, parameterItem(context, BLOCK));
}
// 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));
if (type.isTrail()) {
List<Parameter<?>> trail = List.of(TRAIL_DURATION, TRAIL_TARGET_X, TRAIL_TARGET_Y, TRAIL_TARGET_Z);
for (int i = 0; i < trail.size(); i++) {
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 3 + i, parameterItem(context, trail.get(i)));
List<Parameter<?>> target = List.of(TRAIL_TARGET_X, TRAIL_TARGET_Y, TRAIL_TARGET_Z);
for (int i = 0; i < target.size(); i++) {
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 4 + i, parameterItem(context, target.get(i)));
}
particleRow.add(TRAIL_DURATION);
}
List<Parameter<?>> particleRow = design.replacesAmount(entry.getValues()) ? List.of(SPEED, SPREAD_X, SPREAD_Y, SPREAD_Z) : List.of(COUNT, SPEED, SPREAD_X, SPREAD_Y, SPREAD_Z);
row(inventory, context, ROW_PARTICLE, particleRow);
} else {
inventory.setItem(ROW_PARTICLE * 9 + FIRST_SETTING, new SWItem(Material.STRUCTURE_VOID, msg("PARTICLE_FIXED_LOOK", player), clickType -> {
@@ -187,6 +190,7 @@ class EntryEditor {
if (parameter == COLORS) icon = Material.RED_DYE;
if (parameter == BLOCK) icon = (Material) value;
if (value instanceof WingShape shape) icon = shape.getIcon();
if (value instanceof Touch touch) icon = touch.icon();
return new SWItem(icon, name(parameter.getNameKey(), player), lore, false, clickType -> {
if (parameter instanceof BooleanParameter toggle) {
@@ -254,6 +258,7 @@ class EntryEditor {
private Material choiceIcon(Object value) {
if (value instanceof ParticleType type) return type.getIcon();
if (value instanceof WingShape shape) return shape.getIcon();
if (value instanceof Touch touch) return touch.icon();
if (value instanceof Material material) return material;
return Material.LEVER;
}
@@ -120,6 +120,9 @@ class ParticleGuiText {
if (entry.getDesign().usesEntryParticle()) {
lore.add(msg("PARTICLE_ENTRY_PARTICLE", player, particleName(values.get(PARTICLE), player)));
}
if (values.get(TOUCH) != Touches.NONE) {
lore.add(msg("PARTICLE_ENTRY_TOUCH", player, choiceName(TOUCH, values.get(TOUCH), player)));
}
lore.add("§8- " + choiceName(TICK_MODE, values.get(TICK_MODE), player));
addCondition(lore, MOVEMENT, values.get(MOVEMENT), Movement.ANY, player);
addCondition(lore, GLIDING, values.get(GLIDING), Gliding.ANY, player);
@@ -169,9 +169,9 @@ public class ParticleEntry {
return new LocationMutator(element, offsetX, offsetY, offsetZ);
}
// The design's touch with the entry's placement and visibility, or null; touches ignore conditions and timing
// The entry's touch with its placement and visibility, or null; touches ignore conditions and timing
public ParticleElement buildTouch() {
ParticleElement touch = design.touch();
ParticleElement touch = values.get(TOUCH).element().get();
if (touch == null) {
return null;
}
@@ -181,12 +181,15 @@ public class ParticleEntry {
return new Always(values.get(VISIBILITY).wrap(touch));
}
// Shown only when the design, its chosen options and, for designs that use it, the particle type are unlocked
// Shown only when the design, its chosen options, the touch and, for designs that use it, the particle type are unlocked
@SuppressWarnings("unchecked")
public boolean isUnlocked(UnlockContext context) {
if (!design.getRequirement().test(context)) {
return false;
}
if (!TOUCH.requirement(values.get(TOUCH)).test(context)) {
return false;
}
for (Parameter<?> parameter : design.getParameters()) {
if (parameter instanceof ChoiceParameter<?> choice && !((ChoiceParameter<Object>) choice).requirement(values.get(choice)).test(context)) {
return false;
@@ -203,7 +206,7 @@ public class ParticleEntry {
// Particle positions spawned per tick, used for the slot budget
public double cost() {
double perTick = design.usesEntryParticle() ? (double) design.points(values) * amount() : design.points(values);
return perTick / values.get(INTERVAL);
return perTick / values.get(INTERVAL) + values.get(TOUCH).points();
}
public JsonObject toJson() {