Skip to content

Commit 5178ac2

Browse files
authored
Introduces toggle effect & expression (SkriptLang#4154)
* feat(changemode): introduces toggle mode * fix(unreachable): remove unecessary statement * fix(unreachable): remove unecessary statement * fix(condition): missing pipe sign Co-authored-by: TPGamesNL <29547183+TPGamesNL@users.noreply.github.com> * revert(ExprWhitelist): reset mode affected * fix(ExprGlowing): missing toggle mode in acceptChange * chore(toggle): replaces ChangeMode.TOGGLE with ChangeMode.SET * docs(toggle): add missing version update * chore(toggle): remove syntax conflict & unwanted changes * chore(toggle): remove unwanted changes * chore(toggle): remove unwanted changes * chore(toggle): remove missing unwanted changes * fix: tests * chore(toggle): move toggle booleans to EffToggle * fix(toggle): check parsing type * fix(toggle): make tests work * fix(typo): description of toggle effect Co-authored-by: TPGamesNL <29547183+TPGamesNL@users.noreply.github.com> * fix(toggle): make init check work Co-authored-by: TPGamesNL <29547183+TPGamesNL@users.noreply.github.com> * fix(toggle): make requiered changes * chore(toggle): remove unwanted stuff * fix: indentation * docs(ExprToggled): precise we toggle a boolean value Co-authored-by: TPGamesNL <29547183+TPGamesNL@users.noreply.github.com> * fix(ExprToggled): wrong annotation indentation Co-authored-by: TPGamesNL <29547183+TPGamesNL@users.noreply.github.com> * fix(EffToggle): remove boilerplate and unecessary code * fix(EffToggle): boolean value check * fix(EffToggle): apply change to lists * fix(ExprToggled): issue with Expression#stream method * fix: toggle condition Co-authored-by: TPGamesNL <29547183+TPGamesNL@users.noreply.github.com> * feat: optimize toggle operation Co-authored-by: TPGamesNL <29547183+TPGamesNL@users.noreply.github.com> * fix: typing * fix: returning only boolean * test: add block test * More readable patterns Co-authored-by: Ayham Al Ali <alali_ayham@yahoo.com> * Remove comment Co-authored-by: Ayham Al Ali <alali_ayham@yahoo.com> * Improve code quality Co-authored-by: Ayham Al Ali <alali_ayham@yahoo.com> * revert: asked * chore: remove empty line * chore: change pattern of ExprToggle Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * revert: add missing new line for test * chore: rename ExprToggled to ExprInverse * fix: toggle test * fix(typo): apply review * revert: unecessary change * ups * chore: remove unused imports * docs: remove comment * docs: update description Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore: short code Co-authored-by: Ayham Al Ali <alali_ayham@yahoo.com> * chore: adds check before toggle * chore: add missing closures Co-authored-by: Ayham Al Ali <alali_ayham@yahoo.com> * chore: remove finals Co-authored-by: Ayham Al Ali <alali_ayham@yahoo.com> * chore: simplify workflow Co-authored-by: Ayham Al Ali <alali_ayham@yahoo.com> * fix: toggle wasn't working correctly * fix: missing value when boolean toggle * fix: incompatibility Java version method * fix: block wasn't added to the list * test: rename test name Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * chore: remove unecessary part * chore: update code with review * chore(formatting): fix formatting Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore(formatting): fix formatting Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore(formatting): fix formatting Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore(formatting): fix formatting Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore: collect as array directly Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore: rename 'toggledExpr' to 'togglables' * fix(toggle): try to fix unexpected behavior * chore(EffToggle): check if accepts more than single value * fix(EffToggle): avoid index reset when possible * fix(test): rephrase error * fix: wrong annotation import * fix: toggle * chore: add plurial to the syntax Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * fix: test wrong message Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * chore: remove license message Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * fix: code styling * chore: update code based on feedback * chore: remove unnecessary empty line Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * chore: remove unnecessary empty line Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * chore: accept precise type instead of Object.class Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore: simplify duplicated code Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * chore(ExprInverse): rename 'booleanExpr' into 'booleans' * chore: use ChangeMode & ChangerUtils imports to simplify usage
1 parent 904640d commit 5178ac2

3 files changed

Lines changed: 239 additions & 44 deletions

File tree

Lines changed: 141 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,181 @@
11
package ch.njol.skript.effects;
22

3-
import java.lang.invoke.MethodHandle;
4-
import java.lang.invoke.MethodHandles;
5-
import java.lang.invoke.MethodType;
3+
import java.util.function.Function;
64

75
import org.bukkit.block.Block;
8-
import org.bukkit.block.BlockFace;
96
import org.bukkit.block.data.BlockData;
107
import org.bukkit.block.data.Openable;
118
import org.bukkit.block.data.Powerable;
129
import org.bukkit.event.Event;
10+
import org.jetbrains.annotations.NotNull;
1311
import org.jetbrains.annotations.Nullable;
1412

1513
import ch.njol.skript.Skript;
14+
import ch.njol.skript.classes.Changer.ChangeMode;
15+
import ch.njol.skript.classes.Changer.ChangerUtils;
1616
import ch.njol.skript.doc.Description;
1717
import ch.njol.skript.doc.Examples;
1818
import ch.njol.skript.doc.Name;
1919
import ch.njol.skript.doc.Since;
2020
import ch.njol.skript.lang.Effect;
2121
import ch.njol.skript.lang.Expression;
2222
import ch.njol.skript.lang.SkriptParser.ParseResult;
23+
import ch.njol.skript.util.Patterns;
2324
import ch.njol.util.Kleenean;
2425

25-
/**
26-
* @author Peter Güttinger
27-
*/
28-
@SuppressWarnings("deprecation")
2926
@Name("Toggle")
30-
@Description("Toggle the state of a block.")
27+
@Description("Toggle the state of a block or boolean.")
3128
@Examples({"# use arrows to toggle switches, doors, etc.",
3229
"on projectile hit:",
3330
"\tprojectile is arrow",
34-
"\ttoggle the block at the arrow"})
35-
@Since("1.4")
31+
"\ttoggle the block at the arrow",
32+
"",
33+
"# With booleans",
34+
"toggle gravity of player"
35+
})
36+
@Since("1.4, INSERT VERSION (booleans)")
3637
public class EffToggle extends Effect {
37-
38+
39+
private enum Action {
40+
ACTIVATE, DEACTIVATE, TOGGLE;
41+
42+
public boolean apply(boolean current) {
43+
return switch(this) {
44+
case ACTIVATE -> true;
45+
case DEACTIVATE -> false;
46+
case TOGGLE -> !current;
47+
};
48+
}
49+
}
50+
51+
private enum Type {
52+
BLOCKS, BOOLEANS, MIXED;
53+
54+
/**
55+
* Determines the appropriate Type based on the return type of an expression.
56+
* @param returnType The class representing the return type
57+
* @return The corresponding Type
58+
*/
59+
public static Type fromClass(Class<?> returnType) {
60+
boolean isBlockType = Block.class.isAssignableFrom(returnType);
61+
boolean isBooleanType = Boolean.class.isAssignableFrom(returnType);
62+
63+
if (isBlockType && !isBooleanType) {
64+
return BLOCKS;
65+
} else if (isBooleanType && !isBlockType) {
66+
return BOOLEANS;
67+
} else {
68+
return MIXED;
69+
}
70+
}
71+
}
72+
73+
private static final Patterns<Action> patterns = new Patterns<>(new Object[][]{
74+
{"(open|turn on|activate) %blocks%", Action.ACTIVATE},
75+
{"(close|turn off|de[-]activate) %blocks%", Action.DEACTIVATE},
76+
{"(toggle|switch) [[the] state of] %blocks/booleans%", Action.TOGGLE}
77+
});
78+
3879
static {
39-
Skript.registerEffect(EffToggle.class, "(close|turn off|de[-]activate) %blocks%", "(toggle|switch) [[the] state of] %blocks%", "(open|turn on|activate) %blocks%");
80+
Skript.registerEffect(EffToggle.class, patterns.getPatterns());
4081
}
4182

42-
@SuppressWarnings("null")
43-
private Expression<Block> blocks;
44-
private int toggle;
45-
46-
@SuppressWarnings({"unchecked", "null"})
83+
private Expression<?> togglables;
84+
private Action action;
85+
private Type type;
86+
4787
@Override
48-
public boolean init(final Expression<?>[] vars, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) {
49-
blocks = (Expression<Block>) vars[0];
50-
toggle = matchedPattern - 1;
88+
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
89+
togglables = expressions[0];
90+
action = patterns.getInfo(matchedPattern);
91+
92+
// Determine expression type using the enum method
93+
type = Type.fromClass(togglables.getReturnType());
94+
95+
// Validate based on type
96+
if (type == Type.BOOLEANS &&
97+
!ChangerUtils.acceptsChange(togglables, ChangeMode.SET, Boolean.class)) {
98+
Skript.error("Cannot toggle '" + togglables + "' as it cannot be set to booleans.");
99+
return false;
100+
} else if (type == Type.MIXED && !ChangerUtils.acceptsChange(togglables, ChangeMode.SET, Block.class, Boolean.class)) {
101+
Skript.error("Cannot toggle '" + togglables + "' as it cannot be set to both blocks and booleans.");
102+
return false;
103+
}
104+
51105
return true;
52106
}
53107

54108
@Override
55-
protected void execute(final Event e) {
56-
for (Block b : blocks.getArray(e)) {
57-
BlockData data = b.getBlockData();
58-
if (toggle == -1) {
59-
if (data instanceof Openable)
60-
((Openable) data).setOpen(false);
61-
else if (data instanceof Powerable)
62-
((Powerable) data).setPowered(false);
63-
} else if (toggle == 1) {
64-
if (data instanceof Openable)
65-
((Openable) data).setOpen(true);
66-
else if (data instanceof Powerable)
67-
((Powerable) data).setPowered(true);
68-
} else {
69-
if (data instanceof Openable) // open = NOT was open
70-
((Openable) data).setOpen(!((Openable) data).isOpen());
71-
else if (data instanceof Powerable) // power = NOT power
72-
((Powerable) data).setPowered(!((Powerable) data).isPowered());
109+
protected void execute(Event event) {
110+
switch (type) {
111+
case BOOLEANS -> toggleBooleans(event);
112+
case BLOCKS -> toggleBlocks(event);
113+
case MIXED -> toggleMixed(event);
114+
}
115+
}
116+
117+
/**
118+
* Toggles blocks by opening/closing or powering/unpowering them.
119+
* @param event the event used for evaluation
120+
*/
121+
private void toggleBlocks(Event event) {
122+
for (Object obj : togglables.getArray(event)) {
123+
if (obj instanceof Block block) {
124+
toggleSingleBlock(block);
73125
}
74-
75-
b.setBlockData(data);
76126
}
77127
}
78128

129+
/**
130+
* Toggles a single block, either by opening/closing or powering/unpowering it.
131+
* @param block The block to toggle
132+
*/
133+
private void toggleSingleBlock(@NotNull Block block) {
134+
BlockData data = block.getBlockData();
135+
if (data instanceof Openable openable) {
136+
openable.setOpen(action.apply(openable.isOpen()));
137+
} else if (data instanceof Powerable powerable) {
138+
powerable.setPowered(action.apply(powerable.isPowered()));
139+
}
140+
block.setBlockData(data);
141+
}
142+
143+
/**
144+
* Uses {@link Expression#changeInPlace(Event, Function)} to toggle booleans.
145+
* @param event the event used for evaluation
146+
*/
147+
private void toggleBooleans(Event event) {
148+
togglables.changeInPlace(event, (obj) -> {
149+
if (!(obj instanceof Boolean bool)) {
150+
return null;
151+
}
152+
return action.apply(bool);
153+
});
154+
}
155+
156+
/**
157+
* Uses {@link Expression#changeInPlace(Event, Function)} to toggle both blocks and booleans.
158+
* @param event the event used for evaluation
159+
*/
160+
private void toggleMixed(Event event) {
161+
togglables.changeInPlace(event, (obj) -> {
162+
if (obj instanceof Block block) {
163+
toggleSingleBlock(block);
164+
return block;
165+
} else if (obj instanceof Boolean bool) {
166+
return action.apply(bool);
167+
}
168+
return obj;
169+
});
170+
}
171+
79172
@Override
80-
public String toString(final @Nullable Event e, final boolean debug) {
81-
return "toggle " + blocks.toString(e, debug);
173+
public String toString(@Nullable Event event, boolean debug) {
174+
String actionText = switch (action) {
175+
case ACTIVATE -> "activate";
176+
case DEACTIVATE -> "deactivate";
177+
case TOGGLE -> "toggle";
178+
};
179+
return actionText + " " + togglables.toString(event, debug);
82180
}
83-
84181
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package ch.njol.skript.expressions;
2+
3+
import org.bukkit.event.Event;
4+
import org.jetbrains.annotations.Nullable;
5+
6+
import ch.njol.skript.Skript;
7+
import ch.njol.skript.doc.Description;
8+
import ch.njol.skript.doc.Examples;
9+
import ch.njol.skript.doc.Name;
10+
import ch.njol.skript.doc.Since;
11+
import ch.njol.skript.lang.Expression;
12+
import ch.njol.skript.lang.ExpressionType;
13+
import ch.njol.skript.lang.SkriptParser.ParseResult;
14+
import ch.njol.skript.lang.util.SimpleExpression;
15+
import ch.njol.util.Kleenean;
16+
17+
@Name("Inverse Boolean")
18+
@Description("An expression to obtain the inverse value of a boolean")
19+
@Examples("set {_gravity} to inverse of player's flight mode")
20+
@Since("INSERT VERSION")
21+
public class ExprInverse extends SimpleExpression<Boolean> {
22+
23+
static {
24+
Skript.registerExpression(ExprInverse.class, Boolean.class, ExpressionType.COMBINED,
25+
"[the] (inverse|opposite)[s] of %booleans%"
26+
);
27+
}
28+
29+
private Expression<Boolean> booleans;
30+
31+
@Override
32+
@SuppressWarnings("unchecked")
33+
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
34+
booleans = (Expression<Boolean>) exprs[0];
35+
return true;
36+
}
37+
38+
@Override
39+
protected Boolean @Nullable [] get(Event event) {
40+
Boolean[] original = booleans.getArray(event);
41+
Boolean[] toggled = new Boolean[original.length];
42+
for (int i = 0; i < original.length; i++) {
43+
toggled[i] = !original[i];
44+
}
45+
return toggled;
46+
}
47+
48+
@Override
49+
public boolean isSingle() {
50+
return booleans.isSingle();
51+
}
52+
53+
@Override
54+
public Class<? extends Boolean> getReturnType() {
55+
return Boolean.class;
56+
}
57+
58+
@Override
59+
public String toString(@Nullable Event event, boolean debug) {
60+
return "inverse of " + booleans.toString(event, debug);
61+
}
62+
63+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
test "toggle effect":
2+
set {_isToggled} to false
3+
assert {_isToggled} is false with "{_isToggled} should be set to false"
4+
toggle {_isToggled}
5+
assert {_isToggled} is true with "{_isToggled} should be toggled to true"
6+
set {_toggle::*} to false, false and false
7+
assert {_toggle::1} is false with "{_toggle::1} should be set to false"
8+
assert {_toggle::2} is false with "{_toggle::2} should be set to false"
9+
assert {_toggle::3} is false with "{_toggle::3} should be set to false"
10+
toggle {_toggle::*}
11+
assert {_toggle::1} is true with "{_toggle::1} should be toggled to true"
12+
assert {_toggle::2} is true with "{_toggle::2} should be toggled to true"
13+
assert {_toggle::3} is true with "{_toggle::3} should be toggled to true"
14+
assert inverse of {_isToggled} is false with "{_isToggled} should be toggled to false"
15+
16+
set {_block} to block at location(10, 10, 10, world "world")
17+
set {_list::*} to true and {_block}
18+
toggle {_list::*}
19+
assert {_list::1} is false with "{_list::1} should be set to false"
20+
assert amount of {_list::*} is 2 with "{_list::*} should be set to false and {_block}"
21+
22+
set block at location(10, 11, 10, world "world") to birch door
23+
set {_door} to block at location(10, 11, 10, world "world")
24+
assert {_door} is birch door with "{_door} should be birch door"
25+
assert "%block data of {_door}%" contains "open=false" with "{_door} should be closed"
26+
toggle {_door}
27+
assert "%block data of {_door}%" contains "open=true" with "{_door} should be open"
28+
29+
set {_list::myFirstIndex::myFirstValue} to "hello"
30+
set {_list::mySecondIndex::mySecondValue} to "world"
31+
set {_list::myThirdIndex::myThirdValue} to false
32+
toggle {_list::*}
33+
assert {_list::myFirstIndex::myFirstValue} is "hello" with "{_list::myFirstIndex::myFirstValue} should be 'hello'"
34+
assert {_list::mySecondIndex::mySecondValue} is "world" with "{_list::mySecondIndex::mySecondValue} should be 'world'"
35+
assert {_list::myThirdIndex::myThirdValue} is false with "{_list::myThirdIndex::myThirdValue} should not be toggled"

0 commit comments

Comments
 (0)