forked from SteamWar/SteamWar
Turn the touches back into reward designs styled like any other
The Touch setting is gone. Its unique mechanics become reward designs that use the entry's particle: Burst (on jumping, landing or sneaking, as a puff or a ring), Footprints, Team link, Shimmer and Heartbeat. Each is unlocked by any of the rewards that had it, and Burst's triggers are locked separately. The wing beat becomes the Beat on jump wing motion, unlocked by WarGear Season 2022. December snow and the glyphs are dropped: the first is a plain particle with a condition, the second only works with enchanting glyphs.
This commit is contained in:
@@ -116,7 +116,6 @@ 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}
|
||||
@@ -136,9 +135,23 @@ PARTICLE_DESIGN_SIMPLE = §fSimple
|
||||
PARTICLE_DESIGN_CIRCLE = §fCircle
|
||||
PARTICLE_DESIGN_WING = §fWings
|
||||
PARTICLE_DESIGN_CLOUD = §fCloud
|
||||
PARTICLE_DESIGN_BURST = §fBurst
|
||||
PARTICLE_DESIGN_FOOTPRINTS = §fFootprints
|
||||
PARTICLE_DESIGN_TEAM_LINK = §fTeam link
|
||||
PARTICLE_DESIGN_PULSE_SHIMMER = §fShimmer
|
||||
PARTICLE_DESIGN_HEART_BEAT = §fHeartbeat
|
||||
|
||||
PARTICLE_PARAM_PARTICLE = §fParticle
|
||||
PARTICLE_PARAM_TOUCH = §fTouch
|
||||
PARTICLE_PARAM_BURST_TRIGGER = §fWhen
|
||||
PARTICLE_PARAM_BURST_SHAPE = §fShape
|
||||
PARTICLE_PARAM_BURST_RADIUS = §fRing radius
|
||||
PARTICLE_PARAM_BURST_FALL = §fMinimum fall height
|
||||
PARTICLE_PARAM_FOOTPRINT_LIFETIME = §fPrint lifetime (ticks)
|
||||
PARTICLE_PARAM_FOOTPRINT_STEP = §fDistance between prints
|
||||
PARTICLE_PARAM_LINK_SPACING = §fDistance between particles
|
||||
PARTICLE_PARAM_PULSE_SPEED = §fPulse duration (ticks)
|
||||
PARTICLE_PARAM_PULSE_ROTATION = §fRotation
|
||||
PARTICLE_PARAM_HEART_BEAT_SPEED = §fBeat duration (ticks)
|
||||
PARTICLE_PARAM_COUNT = §fAmount
|
||||
PARTICLE_PARAM_SPREAD_X = §fSpread X
|
||||
PARTICLE_PARAM_SPREAD_Y = §fSpread Y
|
||||
@@ -204,28 +217,17 @@ 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
|
||||
PARTICLE_CHOICE_MOTION_FLAP_SLOW = §fFlap slowly
|
||||
PARTICLE_CHOICE_MOTION_FLAP_FAST = §fFlap quickly
|
||||
PARTICLE_CHOICE_MOTION_BEAT_ON_JUMP = §fBeat on jumping
|
||||
PARTICLE_CHOICE_TRIGGER_JUMP = §fOn jumping
|
||||
PARTICLE_CHOICE_TRIGGER_LAND = §fOn landing
|
||||
PARTICLE_CHOICE_TRIGGER_SNEAK = §fOn sneaking
|
||||
PARTICLE_CHOICE_BURSTSHAPE_PUFF = §fPuff
|
||||
PARTICLE_CHOICE_BURSTSHAPE_RING = §fRing
|
||||
|
||||
# Particle types
|
||||
PARTICLE_TYPE_DUST = §fDust
|
||||
|
||||
@@ -116,7 +116,6 @@ 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}
|
||||
@@ -136,9 +135,23 @@ PARTICLE_DESIGN_SIMPLE = §fEinfach
|
||||
PARTICLE_DESIGN_CIRCLE = §fKreis
|
||||
PARTICLE_DESIGN_WING = §fFlügel
|
||||
PARTICLE_DESIGN_CLOUD = §fWolke
|
||||
PARTICLE_DESIGN_BURST = §fStoß
|
||||
PARTICLE_DESIGN_FOOTPRINTS = §fFußspuren
|
||||
PARTICLE_DESIGN_TEAM_LINK = §fTeamband
|
||||
PARTICLE_DESIGN_PULSE_SHIMMER = §fSchimmer
|
||||
PARTICLE_DESIGN_HEART_BEAT = §fHerzschlag
|
||||
|
||||
PARTICLE_PARAM_PARTICLE = §fPartikel
|
||||
PARTICLE_PARAM_TOUCH = §fAkzent
|
||||
PARTICLE_PARAM_BURST_TRIGGER = §fWann
|
||||
PARTICLE_PARAM_BURST_SHAPE = §fForm
|
||||
PARTICLE_PARAM_BURST_RADIUS = §fRingradius
|
||||
PARTICLE_PARAM_BURST_FALL = §fMindestfallhöhe
|
||||
PARTICLE_PARAM_FOOTPRINT_LIFETIME = §fSpurdauer (Ticks)
|
||||
PARTICLE_PARAM_FOOTPRINT_STEP = §fAbstand der Spuren
|
||||
PARTICLE_PARAM_LINK_SPACING = §fAbstand der Partikel
|
||||
PARTICLE_PARAM_PULSE_SPEED = §fPulsdauer (Ticks)
|
||||
PARTICLE_PARAM_PULSE_ROTATION = §fDrehung
|
||||
PARTICLE_PARAM_HEART_BEAT_SPEED = §fSchlagdauer (Ticks)
|
||||
PARTICLE_PARAM_COUNT = §fAnzahl
|
||||
PARTICLE_PARAM_SPREAD_X = §fStreuung X
|
||||
PARTICLE_PARAM_SPREAD_Y = §fStreuung Y
|
||||
@@ -204,28 +217,17 @@ 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
|
||||
PARTICLE_CHOICE_MOTION_FLAP_SLOW = §fLangsam schlagen
|
||||
PARTICLE_CHOICE_MOTION_FLAP_FAST = §fSchnell schlagen
|
||||
PARTICLE_CHOICE_MOTION_BEAT_ON_JUMP = §fSchlag beim Springen
|
||||
PARTICLE_CHOICE_TRIGGER_JUMP = §fBeim Springen
|
||||
PARTICLE_CHOICE_TRIGGER_LAND = §fBeim Landen
|
||||
PARTICLE_CHOICE_TRIGGER_SNEAK = §fBeim Schleichen
|
||||
PARTICLE_CHOICE_BURSTSHAPE_PUFF = §fWolke
|
||||
PARTICLE_CHOICE_BURSTSHAPE_RING = §fRing
|
||||
|
||||
# Particle types
|
||||
PARTICLE_TYPE_DUST = §fStaub
|
||||
|
||||
@@ -44,10 +44,6 @@ public class CompiledSet {
|
||||
for (ParticleEntry entry : particleSet.getEntries()) {
|
||||
if (entry.isUnlocked(context)) {
|
||||
compiledSet.add(entry.build());
|
||||
ParticleElement touch = entry.buildTouch();
|
||||
if (touch != null) {
|
||||
compiledSet.add(touch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return compiledSet;
|
||||
|
||||
@@ -244,6 +244,24 @@ public interface ParticleRequirement {
|
||||
}.or(SERVER_TEAM);
|
||||
}
|
||||
|
||||
// Met by meeting any of the requirements; the lore lists them one per line
|
||||
static ParticleRequirement anyOf(ParticleRequirement... requirements) {
|
||||
return new ParticleRequirement() {
|
||||
@Override
|
||||
public String getRequirementName(Player player) {
|
||||
return java.util.Arrays.stream(requirements).map(requirement -> requirement.getRequirementName(player)).filter(Objects::nonNull).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(UnlockContext context) {
|
||||
for (ParticleRequirement requirement : requirements) {
|
||||
if (requirement.test(context)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// The alternative is checked first, as it is usually the cheap server team check
|
||||
default ParticleRequirement or(ParticleRequirement particleRequirement) {
|
||||
return new ParticleRequirement() {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package de.steamwar.lobby.particle.design;
|
||||
|
||||
import de.steamwar.lobby.particle.ParticleElement;
|
||||
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||
import de.steamwar.lobby.particle.DrawnWings;
|
||||
import de.steamwar.lobby.particle.WingDesign;
|
||||
import de.steamwar.lobby.particle.elements.Circle;
|
||||
@@ -102,7 +103,8 @@ public class Designs {
|
||||
public final ChoiceParameter<WingColoring> WING_COLORING = ChoiceParameter.ofEnum("coloring", "PARTICLE_PARAM_WING_COLORING", WingColoring.OVER_TIME);
|
||||
// Percent of the points shown each tick; fewer makes the wing twinkle and costs less
|
||||
public final IntParameter WING_SPARKLE = new IntParameter("sparkle", "PARTICLE_PARAM_WING_SPARKLE", 100, 10, 100);
|
||||
public final ChoiceParameter<Wing.Motion> WING_MOTION = ChoiceParameter.ofEnum("motion", "PARTICLE_PARAM_WING_MOTION", Wing.Motion.NONE, motion -> motion == Wing.Motion.JUMP_BEAT);
|
||||
public final ChoiceParameter<Wing.Motion> WING_MOTION = ChoiceParameter.ofEnum("motion", "PARTICLE_PARAM_WING_MOTION", Wing.Motion.NONE, motion -> false,
|
||||
motion -> motion == Wing.Motion.BEAT_ON_JUMP ? Unlocks.WARGEAR_SEASON_2022.get() : ParticleRequirement.NO_REQUIRMENT);
|
||||
// Moving wings need to be redrawn often to look smooth; still ones are fine once a second
|
||||
private final int MOVING_WING_INTERVAL = 2;
|
||||
private final int STILL_WING_INTERVAL = 20;
|
||||
@@ -112,14 +114,12 @@ public class Designs {
|
||||
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||
WingShape shape = values.get(WING_SHAPE);
|
||||
return new Wing(particle, values.get(WING_SIZE), shape.design(values.get(WING_DETAIL)),
|
||||
values.get(WING_COLORING) == WingColoring.ALONG_WING, values.get(WING_SPARKLE), values.get(WING_MOTION),
|
||||
values.get(EntryParameters.TOUCH) == Touches.JUMP_WINGS);
|
||||
values.get(WING_COLORING) == WingColoring.ALONG_WING, values.get(WING_SPARKLE), values.get(WING_MOTION));
|
||||
}
|
||||
|
||||
// The wing beat touch makes these wings beat instead of drawing wings of its own
|
||||
@Override
|
||||
public boolean absorbsTouch(Touch touch) {
|
||||
return touch == Touches.JUMP_WINGS;
|
||||
public boolean redrawsAfterJump(ParameterValues values) {
|
||||
return values.get(WING_MOTION) == Wing.Motion.BEAT_ON_JUMP;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,7 +141,8 @@ public class Designs {
|
||||
@Override
|
||||
public void adjust(Parameter<?> changed, ParameterValues values) {
|
||||
if (changed != WING_MOTION) return;
|
||||
boolean moving = values.get(WING_MOTION) != Wing.Motion.NONE;
|
||||
// A beat on jumping is redrawn every tick only while it beats
|
||||
boolean moving = values.get(WING_MOTION) == Wing.Motion.FLAP_SLOW || values.get(WING_MOTION) == Wing.Motion.FLAP_FAST;
|
||||
int interval = values.get(EntryParameters.INTERVAL);
|
||||
if (moving && interval > MOVING_WING_INTERVAL) {
|
||||
values.set(EntryParameters.INTERVAL, MOVING_WING_INTERVAL);
|
||||
|
||||
@@ -204,10 +204,8 @@ 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(
|
||||
TOUCH, PARTICLE, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS, BLOCK,
|
||||
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
|
||||
|
||||
@@ -72,9 +72,9 @@ public abstract class ParticleDesign {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Whether the design shows the touch itself instead of the touch drawing its own particles
|
||||
public boolean absorbsTouch(Touch touch) {
|
||||
// Whether the design must be drawn every tick for a moment after a jump, whatever its interval; these extra
|
||||
// redraws do not count towards the budget
|
||||
public boolean redrawsAfterJump(ParameterValues values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ package de.steamwar.lobby.particle.design;
|
||||
import de.steamwar.lobby.particle.ParticleElement;
|
||||
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||
import de.steamwar.lobby.particle.elements.*;
|
||||
import de.steamwar.lobby.particle.elements.custom.HearthBeat;
|
||||
import de.steamwar.lobby.particle.elements.custom.PulseShimmer;
|
||||
import de.steamwar.lobby.particle.elements.touch.EventBurst;
|
||||
import de.steamwar.lobby.particle.elements.touch.Footprints;
|
||||
import de.steamwar.lobby.particle.elements.touch.Ring;
|
||||
import de.steamwar.lobby.particle.elements.touch.TeamLink;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Particle;
|
||||
@@ -33,12 +39,142 @@ import java.util.function.Consumer;
|
||||
|
||||
import static de.steamwar.lobby.particle.design.EntryParameters.*;
|
||||
|
||||
// Reward designs the builder cannot make. Other rewards unlock ingredients instead: wing images, particles and touches.
|
||||
// Designs unlocked by event and egg hunt rewards. Most are styled like the basic designs, with a particle of the
|
||||
// player's choice; the others have a fixed look. Rewards whose look the basic designs can already build unlock
|
||||
// ingredients instead: wing images, a wing motion and particles.
|
||||
@UtilityClass
|
||||
public class RewardDesigns {
|
||||
|
||||
private final Consumer<ParameterValues> ALWAYS = values -> values.set(TICK_MODE, TickMode.ALWAYS);
|
||||
|
||||
public enum BurstShape {
|
||||
PUFF,
|
||||
RING,
|
||||
}
|
||||
|
||||
private final int RING_POINTS = 16;
|
||||
|
||||
public final ChoiceParameter<EventBurst.Trigger> BURST_TRIGGER = ChoiceParameter.ofEnum("trigger", "PARTICLE_PARAM_BURST_TRIGGER", EventBurst.Trigger.JUMP, trigger -> false,
|
||||
trigger -> switch (trigger) {
|
||||
case JUMP -> Unlocks.AIRSHIP_PLACEMENT.get();
|
||||
case LAND -> ParticleRequirement.anyOf(Unlocks.UNDERWATER_PLACEMENT.get(), Unlocks.ABSTURZ_PLACEMENT.get(), Unlocks.ANY_EVENT.get(), Unlocks.EGG_HUNT_EXTREME.get());
|
||||
case SNEAK -> Unlocks.WARGEAR_SEASON.get();
|
||||
});
|
||||
public final ChoiceParameter<BurstShape> BURST_SHAPE = ChoiceParameter.ofEnum("burstShape", "PARTICLE_PARAM_BURST_SHAPE", BurstShape.PUFF);
|
||||
public final DoubleParameter BURST_RADIUS = new DoubleParameter("burstRadius", "PARTICLE_PARAM_BURST_RADIUS", 1, 0.3, 2, 0.1);
|
||||
public final DoubleParameter BURST_FALL = new DoubleParameter("minimumFall", "PARTICLE_PARAM_BURST_FALL", 1, 0, 10, 0.5);
|
||||
|
||||
// Once at the feet when the player jumps, lands or starts sneaking
|
||||
public final ParticleDesign BURST = new ParticleDesign("burst", Material.FIREWORK_ROCKET, "PARTICLE_DESIGN_BURST", DesignVisibility.REWARD,
|
||||
ParticleRequirement.anyOf(Unlocks.AIRSHIP_PLACEMENT.get(), Unlocks.UNDERWATER_PLACEMENT.get(), Unlocks.ABSTURZ_PLACEMENT.get(),
|
||||
Unlocks.ANY_EVENT.get(), Unlocks.EGG_HUNT_EXTREME.get(), Unlocks.WARGEAR_SEASON.get()),
|
||||
BURST_TRIGGER, BURST_SHAPE, BURST_RADIUS, BURST_FALL) {
|
||||
@Override
|
||||
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||
ParticleElement burst = values.get(BURST_SHAPE) == BurstShape.RING ? new Ring(particle, values.get(BURST_RADIUS), RING_POINTS) : particle;
|
||||
double minimumFall = values.get(BURST_TRIGGER) == EventBurst.Trigger.LAND ? values.get(BURST_FALL) : 0;
|
||||
return new EventBurst(values.get(BURST_TRIGGER), minimumFall, burst);
|
||||
}
|
||||
|
||||
// Shows only now and then, so it counts as a single point
|
||||
@Override
|
||||
public int points(ParameterValues values) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showsSetting(Parameter<?> parameter, ParameterValues values) {
|
||||
if (parameter == BURST_RADIUS) return values.get(BURST_SHAPE) == BurstShape.RING;
|
||||
if (parameter == BURST_FALL) return values.get(BURST_TRIGGER) == EventBurst.Trigger.LAND;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDefaults(ParameterValues values) {
|
||||
values.set(TICK_MODE, TickMode.ALWAYS).set(COUNT, 8).set(SPREAD_X, 0.3).set(SPREAD_Y, 0.05).set(SPREAD_Z, 0.3).set(SPEED, 0.02);
|
||||
}
|
||||
};
|
||||
|
||||
public final IntParameter FOOTPRINT_LIFETIME = new IntParameter("printLifetime", "PARTICLE_PARAM_FOOTPRINT_LIFETIME", 40, 10, 100);
|
||||
public final DoubleParameter FOOTPRINT_STEP = new DoubleParameter("printStep", "PARTICLE_PARAM_FOOTPRINT_STEP", 0.6, 0.3, 2, 0.1);
|
||||
// Blocks per tick while walking, to estimate how many prints are shown at once
|
||||
private final double WALKING_SPEED = 0.22;
|
||||
|
||||
public final ParticleDesign FOOTPRINTS = new ParticleDesign("footprints", Material.LEATHER_BOOTS, "PARTICLE_DESIGN_FOOTPRINTS", DesignVisibility.REWARD,
|
||||
ParticleRequirement.anyOf(Unlocks.ADVENT_WAR_SHIP_PLACEMENT.get(), Unlocks.ADVENT_WAR_SHIP.get(), Unlocks.AIRSHIP.get(), Unlocks.EGG_HUNT_EASY.get()),
|
||||
FOOTPRINT_LIFETIME, FOOTPRINT_STEP) {
|
||||
@Override
|
||||
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||
return new Footprints(particle, values.get(FOOTPRINT_LIFETIME), values.get(FOOTPRINT_STEP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int points(ParameterValues values) {
|
||||
return (int) Math.ceil(values.get(FOOTPRINT_LIFETIME) * WALKING_SPEED / values.get(FOOTPRINT_STEP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDefaults(ParameterValues values) {
|
||||
values.set(TICK_MODE, TickMode.ALWAYS).set(COUNT, 1).set(SPREAD_X, 0.05).set(SPREAD_Z, 0.05).set(SPEED, 0.0);
|
||||
}
|
||||
};
|
||||
|
||||
public final DoubleParameter LINK_SPACING = new DoubleParameter("linkSpacing", "PARTICLE_PARAM_LINK_SPACING", 0.5, 0.3, 2, 0.1);
|
||||
|
||||
// A line to every nearby player who shows this design too
|
||||
public final ParticleDesign TEAM_LINK = new ParticleDesign("team_link", Material.LEAD, "PARTICLE_DESIGN_TEAM_LINK", DesignVisibility.REWARD,
|
||||
ParticleRequirement.anyOf(Unlocks.ANY_EVENT.get(), Unlocks.UNDERWATER.get()),
|
||||
LINK_SPACING) {
|
||||
@Override
|
||||
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||
return new TeamLink("team_link", particle, values.get(LINK_SPACING));
|
||||
}
|
||||
|
||||
// A line to one partner half the range away
|
||||
@Override
|
||||
public int points(ParameterValues values) {
|
||||
return (int) Math.ceil(TeamLink.RANGE / 2 / values.get(LINK_SPACING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDefaults(ParameterValues values) {
|
||||
values.set(TICK_MODE, TickMode.ALWAYS).set(COUNT, 1).set(SPEED, 0.0);
|
||||
}
|
||||
};
|
||||
|
||||
public final ParticleDesign SHIMMER = new ParticleDesign("shimmer", Material.AMETHYST_CLUSTER, "PARTICLE_DESIGN_PULSE_SHIMMER", DesignVisibility.REWARD,
|
||||
ParticleRequirement.anyOf(Unlocks.MINI_WARGEAR_LIGA.get(), Unlocks.WARGEAR_SEASON_PLACEMENT.get()),
|
||||
InternalDesigns.PULSE_SPEED, InternalDesigns.PULSE_ROTATION) {
|
||||
@Override
|
||||
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||
return new PulseShimmer(particle, values.get(InternalDesigns.PULSE_SPEED), values.get(InternalDesigns.PULSE_ROTATION));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int points(ParameterValues values) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDefaults(ParameterValues values) {
|
||||
values.set(TICK_MODE, TickMode.ALWAYS).set(MOVEMENT, Movement.STANDING_STILL).set(COUNT, 1).set(SPEED, 0.0);
|
||||
}
|
||||
};
|
||||
|
||||
public final ParticleDesign HEARTBEAT = new ParticleDesign("heartbeat", Material.RED_DYE, "PARTICLE_DESIGN_HEART_BEAT", DesignVisibility.REWARD,
|
||||
Unlocks.EGG_HUNT_HALF.get(),
|
||||
InternalDesigns.HEART_BEAT_SPEED) {
|
||||
@Override
|
||||
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||
return new HearthBeat(particle, values.get(InternalDesigns.HEART_BEAT_SPEED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDefaults(ParameterValues values) {
|
||||
values.set(TICK_MODE, TickMode.ALWAYS).set(MOVEMENT, Movement.STANDING_STILL).set(COUNT, 1).set(SPEED, 0.0);
|
||||
}
|
||||
};
|
||||
|
||||
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(
|
||||
@@ -81,6 +217,6 @@ public class RewardDesigns {
|
||||
}
|
||||
|
||||
public List<ParticleDesign> all() {
|
||||
return List.of(EVENT_PARTICIPATION_UNDERWATER_MWG, EGG_HUNT_MEDIUM, EGG_HUNT_HARD, EGG_HUNT_ADVANCED);
|
||||
return List.of(BURST, FOOTPRINTS, TEAM_LINK, SHIMMER, HEARTBEAT, EVENT_PARTICIPATION_UNDERWATER_MWG, EGG_HUNT_MEDIUM, EGG_HUNT_HARD, EGG_HUNT_ADVANCED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* 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) {
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
// On a Wings entry the entry's own wings beat; on others it draws dragon breath wings for the beat
|
||||
public final Touch JUMP_WINGS = 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));
|
||||
|
||||
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))),
|
||||
JUMP_WINGS,
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,8 @@ public class Wing extends DelegatingParticleElement {
|
||||
NONE,
|
||||
FLAP_SLOW,
|
||||
FLAP_FAST,
|
||||
// One wing beat after each jump and none otherwise; only for a reward
|
||||
JUMP_BEAT,
|
||||
// One wing beat after each jump; unlocked by a reward
|
||||
BEAT_ON_JUMP,
|
||||
}
|
||||
|
||||
// Wings stand slightly open when idle and open further the faster the player moves
|
||||
@@ -51,8 +51,6 @@ public class Wing extends DelegatingParticleElement {
|
||||
// Share of the points shown each tick, in percent; below 100 the wing twinkles
|
||||
private int density = 100;
|
||||
private Motion motion = Motion.NONE;
|
||||
// An extra beat after each jump on top of the motion
|
||||
private boolean beatOnJump;
|
||||
private double widest;
|
||||
|
||||
public Wing(ParticleElement particleElement, double size, WingDesign wingDesign) {
|
||||
@@ -71,16 +69,8 @@ public class Wing extends DelegatingParticleElement {
|
||||
}
|
||||
}
|
||||
|
||||
public Wing(ParticleElement particleElement, double size, WingDesign wingDesign, boolean colorAlongWing, int density, Motion motion, boolean beatOnJump) {
|
||||
this(particleElement, size, wingDesign, colorAlongWing, density, motion);
|
||||
this.beatOnJump = beatOnJump;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(ParticleTickData particleTickData) {
|
||||
if (motion == Motion.JUMP_BEAT && BodyMotion.ticksSinceJump(particleTickData.getPlayer()) > JUMP_BEAT_TICKS) {
|
||||
return;
|
||||
}
|
||||
double ticks = particleTickData.getDeg() * 10;
|
||||
double open = Math.toRadians(openDegrees(particleTickData, ticks));
|
||||
double cos = Math.cos(open), sin = Math.sin(open);
|
||||
@@ -115,13 +105,10 @@ public class Wing extends DelegatingParticleElement {
|
||||
private double openDegrees(ParticleTickData particleTickData, double ticks) {
|
||||
double speed = Math.min(1, BodyMotion.speed(particleTickData.getPlayer()) / FULL_OPEN_SPEED);
|
||||
double open = IDLE_OPEN_DEGREES + (MOVING_OPEN_DEGREES - IDLE_OPEN_DEGREES) * speed;
|
||||
if (beatOnJump) {
|
||||
open += jumpBeat(particleTickData);
|
||||
}
|
||||
return switch (motion) {
|
||||
case FLAP_SLOW -> open + FLAP_DEGREES * Math.sin(2 * Math.PI * ticks / 20);
|
||||
case FLAP_FAST -> open + FLAP_DEGREES * Math.sin(2 * Math.PI * ticks / 10);
|
||||
case JUMP_BEAT -> open + jumpBeat(particleTickData);
|
||||
case BEAT_ON_JUMP -> open + jumpBeat(particleTickData);
|
||||
default -> open;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,21 +30,21 @@ import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
// Leaves a print every half step on the ground and keeps showing it for a while after the player walked on
|
||||
// Leaves a print every given distance on the ground and keeps showing it for a while after the player walked on
|
||||
public class Footprints extends DelegatingParticleElement {
|
||||
|
||||
private static final double STEP = 0.6;
|
||||
|
||||
private record Print(Location location, int age) {
|
||||
}
|
||||
|
||||
private final int lifetime;
|
||||
private final double step;
|
||||
// Weak keys drop players once they quit
|
||||
private final Map<Player, Deque<Print>> prints = new WeakHashMap<>();
|
||||
|
||||
public Footprints(ParticleElement particleElement, int lifetime) {
|
||||
public Footprints(ParticleElement particleElement, int lifetime, double step) {
|
||||
super(particleElement);
|
||||
this.lifetime = lifetime;
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +54,7 @@ public class Footprints extends DelegatingParticleElement {
|
||||
Deque<Print> playerPrints = prints.computeIfAbsent(player, key -> new ArrayDeque<>());
|
||||
Location feet = player.getLocation();
|
||||
Print last = playerPrints.peekLast();
|
||||
if (player.isOnGround() && (last == null || last.location().getWorld() != feet.getWorld() || last.location().distanceSquared(feet) > STEP * STEP)) {
|
||||
if (player.isOnGround() && (last == null || last.location().getWorld() != feet.getWorld() || last.location().distanceSquared(feet) > step * step)) {
|
||||
playerPrints.addLast(new Print(feet, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* 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.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 along a path. Spawned with an amount of 0, the offset becomes the glyph's motion: it
|
||||
// appears at location + offset and flies back to the location, like glyphs flying into an enchanting table.
|
||||
public class Glyphs implements ParticleElement {
|
||||
|
||||
public enum Path {
|
||||
// From random points on the ground around the player into the chest
|
||||
INTO_PLAYER,
|
||||
// From the chest outwards to a ring around the player
|
||||
OUT_OF_PLAYER,
|
||||
}
|
||||
|
||||
private static final double CHEST = 1.2;
|
||||
|
||||
private final Path path;
|
||||
private final int amount;
|
||||
private final double radius;
|
||||
|
||||
public Glyphs(Path path, int amount, double radius) {
|
||||
this.path = path;
|
||||
this.amount = amount;
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(ParticleTickData particleTickData) {
|
||||
Location chest = particleTickData.getPlayer().getLocation().add(0, CHEST, 0);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Location target;
|
||||
Vector from;
|
||||
if (path == Path.INTO_PLAYER) {
|
||||
double angle = RANDOM.nextDouble() * 2 * Math.PI;
|
||||
double distance = radius * (0.5 + RANDOM.nextDouble() / 2);
|
||||
target = chest;
|
||||
from = new Vector(Math.cos(angle) * distance, -CHEST, Math.sin(angle) * distance);
|
||||
} else {
|
||||
double angle = 2 * Math.PI * i / amount;
|
||||
Vector out = new Vector(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);
|
||||
target = chest.clone().add(out);
|
||||
from = out.multiply(-1);
|
||||
}
|
||||
Location spawn = target;
|
||||
display(particleTickData, player -> player.spawnParticle(Particle.ENCHANT, spawn, 0, from.getX(), from.getY(), from.getZ(), 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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.elements.touch;
|
||||
|
||||
import de.steamwar.lobby.particle.ParticleElement;
|
||||
import de.steamwar.lobby.particle.ParticleTickData;
|
||||
import de.steamwar.lobby.particle.elements.DelegatingParticleElement;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
|
||||
// Only shows during the given month
|
||||
public class Seasonal extends DelegatingParticleElement {
|
||||
|
||||
private final Month month;
|
||||
|
||||
public Seasonal(Month month, ParticleElement particleElement) {
|
||||
super(particleElement);
|
||||
this.month = month;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(ParticleTickData particleTickData) {
|
||||
if (LocalDate.now().getMonth() == month) {
|
||||
particleElement.tick(particleTickData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,18 +34,19 @@ 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.
|
||||
public class TeamLink extends DelegatingParticleElement {
|
||||
|
||||
private static final double RANGE = 8;
|
||||
private static final double SPACING = 0.5;
|
||||
public static final double RANGE = 8;
|
||||
private static final double CHEST = 1.2;
|
||||
|
||||
// Per reward, who showed it in which tick
|
||||
private static final Map<String, Map<Player, Long>> SHOWING = new HashMap<>();
|
||||
|
||||
private final String reward;
|
||||
private final double spacing;
|
||||
|
||||
public TeamLink(String reward, ParticleElement particleElement) {
|
||||
public TeamLink(String reward, ParticleElement particleElement, double spacing) {
|
||||
super(particleElement);
|
||||
this.reward = reward;
|
||||
this.spacing = spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,7 +65,7 @@ public class TeamLink extends DelegatingParticleElement {
|
||||
if (to.getWorld() != from.getWorld() || to.distanceSquared(from) > RANGE * RANGE) continue;
|
||||
|
||||
Vector step = to.toVector().subtract(from.toVector());
|
||||
int points = (int) Math.ceil(step.length() / SPACING);
|
||||
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))));
|
||||
|
||||
@@ -119,21 +119,20 @@ 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 + 2, parameterItem(context, PARTICLE));
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 1, parameterItem(context, PARTICLE));
|
||||
ParticleType type = entry.getValues().get(PARTICLE);
|
||||
if (type.usesColors()) {
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 3, parameterItem(context, COLORS));
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, parameterItem(context, COLORS));
|
||||
} else if (type.usesBlock()) {
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 3, parameterItem(context, BLOCK));
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 2, 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<?>> 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)));
|
||||
inventory.setItem(ROW_DESIGN * 9 + FIRST_SETTING + 3 + i, parameterItem(context, target.get(i)));
|
||||
}
|
||||
particleRow.add(TRAIL_DURATION);
|
||||
}
|
||||
@@ -190,7 +189,6 @@ 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) {
|
||||
@@ -258,7 +256,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class ParticleGuiText {
|
||||
String name = requirement.getRequirementName(player);
|
||||
if (name != null) {
|
||||
lore.add(msg("PARTICLE_UNLOCKED_BY", player));
|
||||
lore.add(name);
|
||||
lore.addAll(List.of(name.split("\n")));
|
||||
}
|
||||
return lore;
|
||||
}
|
||||
@@ -120,9 +120,6 @@ 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);
|
||||
|
||||
@@ -99,9 +99,7 @@ public class ParticleEntry {
|
||||
element = values.get(VISIBILITY).wrap(element);
|
||||
|
||||
int interval = values.get(INTERVAL);
|
||||
if (interval > 1 && design.absorbsTouch(values.get(TOUCH))) {
|
||||
// The jump beat needs every tick to look smooth; outside of it the usual interval applies, and the
|
||||
// extra redraws are not counted towards the budget
|
||||
if (interval > 1 && design.redrawsAfterJump(values)) {
|
||||
element = new AfterJump(element, new Delayed(element, interval), Wing.JUMP_BEAT_TICKS);
|
||||
} else if (interval > 1) {
|
||||
element = new Delayed(element, interval);
|
||||
@@ -174,30 +172,12 @@ public class ParticleEntry {
|
||||
return new LocationMutator(element, offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
// The entry's touch with its placement and visibility, or null; touches ignore conditions and timing
|
||||
public ParticleElement buildTouch() {
|
||||
if (design.absorbsTouch(values.get(TOUCH))) {
|
||||
return null;
|
||||
}
|
||||
ParticleElement touch = values.get(TOUCH).element().get();
|
||||
if (touch == null) {
|
||||
return null;
|
||||
}
|
||||
if (values.get(PLACEMENT) == Placement.BODY) {
|
||||
touch = new BodyRelative(touch);
|
||||
}
|
||||
return new Always(values.get(VISIBILITY).wrap(touch));
|
||||
}
|
||||
|
||||
// Shown only when the design, its chosen options, the touch and, for designs that use it, the particle type are unlocked
|
||||
// Shown only when the design, its chosen options 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;
|
||||
@@ -214,8 +194,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);
|
||||
int touchPoints = design.absorbsTouch(values.get(TOUCH)) ? 0 : values.get(TOUCH).points();
|
||||
return perTick / values.get(INTERVAL) + touchPoints;
|
||||
return perTick / values.get(INTERVAL);
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
|
||||
Reference in New Issue
Block a user