11package org .skriptlang .skriptworldguard .elements .expressions ;
22
33import ch .njol .skript .Skript ;
4+ import ch .njol .skript .classes .Changer .ChangeMode ;
45import ch .njol .skript .doc .Description ;
56import ch .njol .skript .doc .Example ;
67import ch .njol .skript .doc .Name ;
78import ch .njol .skript .doc .Since ;
9+ import ch .njol .skript .entity .EntityData ;
810import ch .njol .skript .expressions .base .PropertyExpression ;
911import ch .njol .skript .lang .Expression ;
1012import ch .njol .skript .lang .Literal ;
1113import ch .njol .skript .lang .SkriptParser .ParseResult ;
1214import ch .njol .skript .lang .SyntaxStringBuilder ;
1315import ch .njol .skript .lang .util .SimpleExpression ;
16+ import ch .njol .skript .registrations .Classes ;
17+ import ch .njol .skript .util .Utils ;
1418import ch .njol .util .Kleenean ;
19+ import ch .njol .util .Math2 ;
20+ import com .google .common .collect .Sets ;
21+ import com .sk89q .worldguard .protection .flags .Flag ;
1522import org .bukkit .event .Event ;
1623import org .jetbrains .annotations .Nullable ;
24+ import org .skriptlang .skript .lang .arithmetic .Arithmetics ;
25+ import org .skriptlang .skript .lang .arithmetic .Operator ;
26+ import org .skriptlang .skript .lang .converter .Converter ;
27+ import org .skriptlang .skript .lang .converter .Converters ;
1728import org .skriptlang .skript .registration .SyntaxRegistry ;
1829import org .skriptlang .skriptworldguard .worldguard .WorldGuardFlag ;
1930import org .skriptlang .skriptworldguard .worldguard .WorldGuardRegion ;
2233import java .util .ArrayList ;
2334import java .util .Arrays ;
2435import java .util .List ;
36+ import java .util .Set ;
2537import java .util .function .Function ;
2638
2739@ Name ("Region Flag" )
@@ -78,7 +90,6 @@ public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean is
7890 return new Object [0 ];
7991 }
8092 }
81-
8293 WorldGuardRegion [] regions = this .regions .getArray (event );
8394
8495 List <Object > values = new ArrayList <>();
@@ -105,6 +116,174 @@ public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean is
105116 return values .toArray ((Object []) Array .newInstance (getReturnType (), values .size ()));
106117 }
107118
119+ @ Override
120+ public Class <?> @ Nullable [] acceptChange (ChangeMode mode ) {
121+ return switch (mode ) {
122+ case ADD , SET , REMOVE , DELETE , RESET ->
123+ new Class []{flag == null ? Object [].class : flag .valueConverter ().fromType ()};
124+ default -> null ;
125+ };
126+ }
127+
128+ @ Override
129+ public void change (Event event , Object @ Nullable [] delta , ChangeMode mode ) {
130+ WorldGuardFlag <?, ?> flag = this .flag ;
131+ if (flag == null ) {
132+ String flagName = this .flagName .getSingle (event );
133+ if (flagName == null ) {
134+ return ;
135+ }
136+ WorldGuardFlag .LookupResult result = WorldGuardFlag .fromName (flagName );
137+ flag = result .flag ();
138+ if (flag == null ) {
139+ error (result .error ());
140+ return ;
141+ }
142+ }
143+ WorldGuardRegion [] regions = this .regions .getArray (event );
144+
145+ // verify types
146+ // this will have been done by EffChange if flag is set (due to accurate types)
147+ if (delta != null && this .flag == null ) {
148+ Class <?> expectedType = flag .valueConverter ().fromType ();
149+ if (expectedType .isArray ()) {
150+ expectedType = expectedType .getComponentType ();
151+ }
152+
153+ // special runtime type handling
154+ // need to manually convert to correct number type
155+ if (Number .class .isAssignableFrom (expectedType )) {
156+ Object [] delta2 = new Object [delta .length ];
157+ Converter <Number , ?> converter = Converters .getConverter (Number .class , expectedType );
158+ assert converter != null ;
159+ for (int i = 0 ; i < delta .length ; i ++) {
160+ if (delta [i ] instanceof Number number ) {
161+ delta2 [i ] = converter .convert (number );
162+ }
163+ }
164+ delta = delta2 ;
165+ }
166+
167+ Class <?> foundType = Utils .getSuperType (Arrays .stream (delta )
168+ .map (Object ::getClass )
169+ .toArray (Class []::new ));
170+
171+ if (!expectedType .isAssignableFrom (foundType )) {
172+ String expr = toString (event , Skript .debug ());
173+ String add = switch (mode ) {
174+ case ADD -> "to add to '" + expr + "'" ;
175+ case SET -> "to set '" + expr + "' to" ;
176+ case REMOVE -> "to remove from '" + expr + "'" ;
177+ default -> throw new IllegalArgumentException ("Unexpected mode: " + mode );
178+ };
179+ error ("Expected the value " + add + " to be " +
180+ Utils .a (Classes .toString (Classes .getSuperClassInfo (expectedType ))) +
181+ " but it was " +
182+ Utils .a (Classes .toString (Classes .getSuperClassInfo (foundType ))));
183+ return ;
184+ }
185+ if (delta .length > 1 && !flag .valueConverter ().fromType ().isArray ()) {
186+ error (toString (event , Skript .debug ()) + " can only have one value " +
187+ (mode == ChangeMode .ADD ? "added to" : "removed from" ) + " it, not more" );
188+ return ;
189+ }
190+ }
191+
192+ if (delta != null ) { // handle special cases
193+ for (int i = 0 ; i < delta .length ; i ++) {
194+ if (delta [i ] instanceof EntityData <?> entityData ) { // fix comparison issues, e.g. isPlural
195+ delta [i ] = EntityData .fromClass (entityData .getType ());
196+ }
197+ }
198+ }
199+
200+ switch (mode ) {
201+ case SET -> {
202+ assert delta != null ;
203+ Object value ;
204+ if (flag .valueConverter ().fromType ().isArray ()) {
205+ value = delta ;
206+ } else {
207+ value = delta [0 ];
208+ }
209+ for (WorldGuardRegion region : regions ) {
210+ //noinspection unchecked, rawtypes
211+ region .region ().setFlag ((Flag ) flag .flag (), ((Function ) flag .valueConverter ().toMapper ()).apply (value ));
212+ }
213+ }
214+ case ADD , REMOVE -> {
215+ assert delta != null ;
216+ boolean isRemove = mode == ChangeMode .REMOVE ;
217+ if (Number .class .isAssignableFrom (flag .valueConverter ().fromType ())) {
218+ for (WorldGuardRegion region : regions ) {
219+ Object current = region .region ().getFlag (flag .flag ());
220+ if (current == null ) {
221+ current = flag .flag ().getDefault ();
222+ }
223+ if (current == null ) {
224+ continue ;
225+ }
226+ //noinspection unchecked, rawtypes
227+ Number mappedCurrent = (Number ) ((Function ) flag .valueConverter ().fromMapper ()).apply (current );
228+
229+ // calculate result
230+ Number result = Arithmetics .calculate (isRemove ? Operator .SUBTRACTION : Operator .ADDITION ,
231+ mappedCurrent , delta [0 ], Number .class );
232+ if (result instanceof Long longResult ) { // convert to Integer
233+ result = (int ) Math2 .fit (Integer .MIN_VALUE , longResult , Integer .MAX_VALUE );
234+ }
235+
236+ //noinspection unchecked, rawtypes
237+ region .region ().setFlag ((Flag ) flag .flag (), ((Function ) flag .valueConverter ().toMapper ()).apply (result ));
238+ }
239+ } else if (flag .valueConverter ().fromType ().isArray ()) {
240+ Set <?> deltaSet = Set .of (delta );
241+ for (WorldGuardRegion region : regions ) {
242+ Object current = region .region ().getFlag (flag .flag ());
243+ if (current == null ) {
244+ current = flag .flag ().getDefault ();
245+ }
246+ Set <?> mappedSet ;
247+ if (current == null ) {
248+ if (isRemove ) { // nothing to remove
249+ continue ;
250+ }
251+ mappedSet = Set .of ();
252+ } else {
253+ //noinspection unchecked, rawtypes
254+ Object [] mappedCurrent = (Object []) ((Function ) flag .valueConverter ().fromMapper ()).apply (current );
255+ mappedSet = Set .of (mappedCurrent );
256+ }
257+
258+ // merge sets
259+ Set <?> valueSet ;
260+ if (isRemove ) {
261+ valueSet = Sets .difference (mappedSet , deltaSet );
262+ } else {
263+ valueSet = Sets .union (mappedSet , deltaSet );
264+ }
265+ Object [] values = valueSet .toArray ();
266+
267+ //noinspection unchecked, rawtypes
268+ region .region ().setFlag ((Flag ) flag .flag (), ((Function ) flag .valueConverter ().toMapper ()).apply (values ));
269+ }
270+ } else {
271+ error (toString (event , Skript .debug ()) + " can't have values " +
272+ (mode == ChangeMode .ADD ? "added to" : "removed from" ) + " it" );
273+ }
274+
275+ }
276+ case DELETE , RESET -> {
277+ for (WorldGuardRegion region : regions ) {
278+ region .region ().setFlag (flag .flag (), null );
279+ }
280+ }
281+ default -> {
282+ assert false ;
283+ }
284+ }
285+ }
286+
108287 @ Override
109288 public boolean isSingle () {
110289 if (flag == null ) {
0 commit comments