Skip to content

Commit e15db44

Browse files
committed
Animation fundamentals, fix density bug, update particle utils
1 parent 29270d6 commit e15db44

22 files changed

+510
-244
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ repositories {
2525
}
2626

2727
dependencies {
28-
compileOnly("io.papermc.paper:paper-api:1.17.1-R0.1-SNAPSHOT")
28+
compileOnly("io.papermc.paper:paper-api:1.20.2-R0.1-SNAPSHOT")
2929
compileOnly("com.github.SkriptLang:Skript:2.7.0")
3030
shadow "org.bstats:bstats-bukkit:3.0.2"
3131
implementation "org.joml:joml:${jomlVersion}"

src/main/java/com/sovdee/skriptparticles/elements/effects/EffRotateShape.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import ch.njol.skript.lang.Expression;
1010
import ch.njol.skript.lang.SkriptParser.ParseResult;
1111
import ch.njol.util.Kleenean;
12-
import com.sovdee.skriptparticles.elements.sections.EffSecDrawShape.DrawEvent;
12+
import com.sovdee.skriptparticles.elements.sections.DrawShapeEffectSection.DrawEvent;
1313
import com.sovdee.skriptparticles.shapes.Shape;
1414
import org.bukkit.event.Event;
1515
import org.bukkit.util.Vector;

src/main/java/com/sovdee/skriptparticles/elements/expressions/properties/ExprShapeParticleDensity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
9393
case DELETE:
9494
case RESET:
9595
case SET:
96-
change = isDensity ? MathUtil.clamp(change, 0.001, 1000) : Math.max(1, (int) change);
96+
change = isDensity ? MathUtil.clamp(1 / change, 0.001, 1000) : Math.max(1, (int) change);
9797
for (Shape shape : shapes) {
9898
if (isDensity) {
9999
shape.setParticleDensity(change);
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
package com.sovdee.skriptparticles.elements.sections;
2+
3+
import ch.njol.skript.Skript;
4+
import ch.njol.skript.config.SectionNode;
5+
import ch.njol.skript.effects.Delay;
6+
import ch.njol.skript.lang.EffectSection;
7+
import ch.njol.skript.lang.Expression;
8+
import ch.njol.skript.lang.SkriptParser.ParseResult;
9+
import ch.njol.skript.lang.Trigger;
10+
import ch.njol.skript.lang.TriggerItem;
11+
import ch.njol.skript.lang.parser.ParserInstance;
12+
import ch.njol.skript.registrations.EventValues;
13+
import ch.njol.skript.util.Direction;
14+
import ch.njol.skript.util.Getter;
15+
import ch.njol.skript.util.Timespan;
16+
import ch.njol.skript.variables.Variables;
17+
import ch.njol.util.Kleenean;
18+
import com.sovdee.skriptparticles.shapes.Shape;
19+
import com.sovdee.skriptparticles.util.DynamicLocation;
20+
import org.bukkit.Bukkit;
21+
import org.bukkit.Location;
22+
import org.bukkit.entity.Entity;
23+
import org.bukkit.entity.Player;
24+
import org.bukkit.event.Event;
25+
import org.bukkit.event.HandlerList;
26+
import org.bukkit.scheduler.BukkitRunnable;
27+
import org.checkerframework.checker.nullness.qual.NonNull;
28+
import org.jetbrains.annotations.Nullable;
29+
30+
import java.util.ArrayList;
31+
import java.util.Collection;
32+
import java.util.List;
33+
import java.util.concurrent.atomic.AtomicBoolean;
34+
import java.util.function.Consumer;
35+
36+
public abstract class DrawShapeEffectSection extends EffectSection {
37+
38+
public static final Timespan ONE_TICK = Timespan.fromTicks_i(1);
39+
40+
static {
41+
EventValues.registerEventValue(DrawEvent.class, Shape.class, new Getter<>() {
42+
@Override
43+
public Shape get(DrawEvent event) {
44+
return event.getShape();
45+
}
46+
}, EventValues.TIME_NOW);
47+
}
48+
49+
protected Expression<Shape> shapes;
50+
protected Expression<Direction> directions;
51+
protected Expression<?> locations;
52+
protected Expression<Player> players;
53+
@Nullable
54+
private Trigger trigger;
55+
56+
protected boolean useShapeLocation;
57+
protected boolean sync;
58+
59+
/**
60+
* Called just after the constructor. Handles checking for delays in the section body and setting the sync tag, then
61+
* passes execution to {@link #init(Expression[], int, Kleenean, ParseResult, boolean)}.
62+
*
63+
* @param expressions all %expr%s included in the matching pattern in the order they appear in the pattern. If an optional value was left out it will still be included in this list
64+
* holding the default value of the desired type which usually depends on the event.
65+
* @param matchedPattern The index of the pattern which matched
66+
* @param isDelayed Whether this expression is used after a delay or not (i.e. if the event has already passed when this expression will be called)
67+
* @param parseResult Additional information about the match.
68+
* @param sectionNode The section node that represents this section.
69+
* @param list A list of {@link TriggerItem}s that belong to this section. This list is modifiable.
70+
* @return Whether this expression was initialised successfully. An error should be printed prior to returning false to specify the cause.
71+
* @see ParserInstance#isCurrentEvent(Class...)
72+
*/
73+
@Override
74+
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, @Nullable SectionNode sectionNode, @Nullable List<TriggerItem> list) {
75+
// handle delays in the section body
76+
if (hasSection()) {
77+
AtomicBoolean delayed = new AtomicBoolean(false);
78+
Runnable afterLoading = () -> delayed.set(!getParser().getHasDelayBefore().isFalse());
79+
assert sectionNode != null;
80+
trigger = loadCode(sectionNode, "draw", afterLoading, DrawEvent.class);
81+
if (delayed.get()) {
82+
Skript.error("Delays can't be used within a Draw Shape Effect Section");
83+
return false;
84+
}
85+
}
86+
return init(expressions, matchedPattern, isDelayed, parseResult, hasSection());
87+
}
88+
89+
/**
90+
* Called just after the constructor. By default, this method does sets the following fields:
91+
* - {@link #shapes} to the first expression
92+
* - {@link #directions} to the second expression
93+
* - {@link #locations} to the third expression
94+
* - {@link #players} to the fourth expression
95+
* - {@link #sync} to whether the sync tag was present
96+
*
97+
* @param expressions all %expr%s included in the matching pattern in the order they appear in the pattern. If an optional value was left out it will still be included in this list
98+
* holding the default value of the desired type which usually depends on the event.
99+
* @param matchedPattern The index of the pattern which matched
100+
* @param isDelayed Whether this expression is used after a delay or not (i.e. if the event has already passed when this expression will be called)
101+
* @param parseResult Additional information about the match.
102+
* @param hasSection Whether this section had a valid section body.
103+
* @return Whether this expression was initialised successfully. An error should be printed prior to returning false to specify the cause
104+
*/
105+
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, boolean hasSection) {
106+
shapes = (Expression<Shape>) expressions[0];
107+
108+
if (expressions[2] != null) {
109+
if (expressions[1] != null)
110+
directions = (Expression<Direction>) expressions[1];
111+
locations = expressions[2];
112+
} else {
113+
useShapeLocation = true;
114+
}
115+
players = (Expression<Player>) expressions[3];
116+
117+
sync = parseResult.hasTag("sync");
118+
119+
return true;
120+
}
121+
122+
@Override
123+
@Nullable
124+
protected TriggerItem walk(Event event) {
125+
debug(event, true);
126+
127+
Delay.addDelayedEvent(event); // Mark this event as delayed
128+
129+
Collection<Player> recipients = new ArrayList<>();
130+
if (players != null) {
131+
recipients.addAll(List.of(players.getArray(event)));
132+
} else {
133+
recipients.addAll(Bukkit.getOnlinePlayers());
134+
}
135+
136+
Object localVars = Variables.copyLocalVariables(event);
137+
138+
Consumer<Shape> consumer;
139+
if (trigger != null) {
140+
consumer = shape -> {
141+
DrawEvent drawEvent = new DrawEvent(shape);
142+
Variables.setLocalVariables(drawEvent, localVars);
143+
TriggerItem.walk(trigger, drawEvent);
144+
Variables.setLocalVariables(event, Variables.copyLocalVariables(drawEvent));
145+
Variables.removeLocals(drawEvent);
146+
};
147+
} else {
148+
consumer = null;
149+
}
150+
151+
// Figure out what locations to draw at, or what entities to follow
152+
List<DynamicLocation> locations = new ArrayList<>();
153+
Direction direction = null;
154+
if (!useShapeLocation) {
155+
if (directions != null)
156+
direction = directions.getSingle(event);
157+
for (Object location : this.locations.getArray(event)) {
158+
if (location instanceof Entity) {
159+
locations.add(new DynamicLocation((Entity) location, direction));
160+
} else if (location instanceof Location) {
161+
locations.add(new DynamicLocation((Location) location, direction));
162+
}
163+
}
164+
} else {
165+
// blank value means use shape location
166+
locations.add(new DynamicLocation());
167+
}
168+
169+
if (sync) {
170+
executeSync(event, locations, consumer, recipients);
171+
} else {
172+
// Clone shapes and run Consumer before going async
173+
// We can't guarantee that the consumer will be thread-safe, so we need do this before going async
174+
List<Shape> preppedShapes = new ArrayList<>();
175+
Shape preppedShape;
176+
for (Shape shape : shapes.getArray(event)) {
177+
preppedShape = shape.clone();
178+
if (consumer != null)
179+
consumer.accept(preppedShape);
180+
preppedShapes.add(preppedShape);
181+
}
182+
183+
if (preppedShapes.isEmpty()) return getNext();
184+
185+
setupAsync(event, locations, preppedShapes, recipients);
186+
}
187+
return getNext();
188+
}
189+
190+
/**
191+
* Sets up the async task to draw the shapes at the given locations for the given recipients.
192+
* Should be called from {@link #walk(Event)} if {@link #sync} is false, and will return the next trigger item to run.
193+
* @param locations the locations to draw the shapes at
194+
* @param shapes the shapes to draw
195+
* @param recipients the players to draw the shapes for
196+
*/
197+
protected void setupAsync(Event event, Collection<DynamicLocation> locations, Collection<Shape> shapes, Collection<Player> recipients) {
198+
BukkitRunnable runnable = new BukkitRunnable() {
199+
@Override
200+
public void run() {
201+
executeAsync(locations, shapes, recipients);
202+
}
203+
};
204+
runnable.runTaskAsynchronously(Skript.getInstance());
205+
}
206+
207+
protected void executeSync(Event event, Collection<DynamicLocation> locations, Consumer<Shape> consumer, Collection<Player> recipients) {
208+
Shape shapeCopy;
209+
for (DynamicLocation dynamicLocation : locations) {
210+
for (Shape shape : shapes.getArray(event)) {
211+
if (consumer != null) {
212+
// copy the shape so that it can be modified by the consumer without affecting the original
213+
shapeCopy = shape.clone();
214+
shapeCopy.draw(dynamicLocation, consumer, recipients);
215+
} else {
216+
shape.draw(dynamicLocation, recipients);
217+
}
218+
}
219+
}
220+
}
221+
222+
protected void executeAsync(Collection<DynamicLocation> locations, Collection<Shape> shapes, Collection<Player> recipients) {
223+
for (DynamicLocation dynamicLocation : locations) {
224+
for (Shape shape : shapes) {
225+
shape.draw(dynamicLocation, recipients);
226+
}
227+
}
228+
}
229+
230+
public static class DrawEvent extends Event {
231+
private final Shape shape;
232+
233+
public DrawEvent(Shape shape) {
234+
this.shape = shape;
235+
}
236+
237+
public Shape getShape() {
238+
return shape;
239+
}
240+
241+
@Override
242+
@NonNull
243+
public HandlerList getHandlers() {
244+
throw new IllegalStateException();
245+
}
246+
}
247+
}

0 commit comments

Comments
 (0)