Skip to content

Commit

Permalink
Added armorstand pose expression and test
Browse files Browse the repository at this point in the history
  • Loading branch information
TFSMads committed Jun 28, 2024
1 parent e2928ce commit 3ddd639
Show file tree
Hide file tree
Showing 2 changed files with 330 additions and 0 deletions.
237 changes: 237 additions & 0 deletions src/main/java/ch/njol/skript/expressions/ExprArmorstandPose.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.expressions;

import ch.njol.skript.Skript;
import ch.njol.skript.classes.Changer;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.util.Direction;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.util.EulerAngle;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;

@Name("Armor stand pose")
@Description("The pose of a specific part of an armor stand.")
@Examples( {
"spawn an armor stand at player:",
" set the left arm pose of the armor stand to east",
})
@Since("INSERT VERSION")
public class ExprArmorstandPose extends SimpleExpression<Direction> {

static {
Skript.registerExpression(ExprArmorstandPose.class, Direction.class, ExpressionType.PROPERTY,
"(0¦left arm|1¦right arm|2¦left leg|3¦right leg|4¦body|5¦head) pose of %livingentities%");
}

private Expression<Entity> entity;

private ArmorStandPose pose;

@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
entity = (Expression<Entity>) exprs[0];
pose = ArmorStandPose.getById(parseResult.mark);
return true;
}

@Override
protected @Nullable Direction[] get(Event event) {
for (Entity entity : this.entity.getArray(event)) {
if(!(entity instanceof ArmorStand))
continue;
ArmorStand armorStand = (ArmorStand) entity;
EulerAngle eulerAngle = null;
switch (pose) {
case LEFT_ARM:
eulerAngle = armorStand.getLeftArmPose();
break;
case RIGHT_ARM:
eulerAngle = armorStand.getRightArmPose();
break;
case LEFT_LEG:
eulerAngle = armorStand.getLeftLegPose();
break;
case RIGHT_LEG:
eulerAngle = armorStand.getRightLegPose();
break;
case BODY:
eulerAngle = armorStand.getBodyPose();
break;
case HEAD:
eulerAngle = armorStand.getHeadPose();
break;
}

if(eulerAngle == null)
continue;
return CollectionUtils.array(convertEulerAngleToDirection(eulerAngle, getYawOffset(armorStand)));
}
return null;
}

@Override
public @Nullable Class<?>[] acceptChange(Changer.ChangeMode mode) {
if(mode == Changer.ChangeMode.SET) {
return CollectionUtils.array(Direction.class, Vector.class);
}
return null;
}

@Override
public void change(Event event, @Nullable Object[] delta, Changer.ChangeMode mode) {
if(delta[0] == null || (!(delta[0] instanceof Direction) && !(delta[0] instanceof Vector)))
return;
for (Entity entity : entity.getArray(event)) {
if(!(entity instanceof ArmorStand))
continue;
ArmorStand armorStand = (ArmorStand) entity;
if(delta[0] instanceof Direction)
setPose(armorStand, (Direction) delta[0]);
else if(delta[0] instanceof Vector)
setPose(armorStand, (Vector) delta[0]);
}
}

@Override
public boolean isSingle() {
return true;
}

@Override
public Class<? extends Direction> getReturnType() {
return Direction.class;
}

private void setPose(ArmorStand armorStand, Vector vector) {
switch (pose) {
case LEFT_ARM:
armorStand.setLeftArmPose(convertDirectionToEulerAngle(vector, getYawOffset(armorStand)));
break;
case RIGHT_ARM:
armorStand.setRightArmPose(convertDirectionToEulerAngle(vector, getYawOffset(armorStand)));
break;
case LEFT_LEG:
armorStand.setLeftLegPose(convertDirectionToEulerAngle(vector, getYawOffset(armorStand)));
break;
case RIGHT_LEG:
armorStand.setRightLegPose(convertDirectionToEulerAngle(vector, getYawOffset(armorStand)));
break;
case BODY:
armorStand.setBodyPose(convertDirectionToEulerAngle(vector, getYawOffset(armorStand)));
break;
case HEAD:
armorStand.setHeadPose(convertDirectionToEulerAngle(vector, getYawOffset(armorStand)));
break;
}
}

private void setPose(ArmorStand armorStand, Direction direction) {
setPose(armorStand, direction.getDirection());
}

/**
* @param vector Direction to convert to EulerAngle
* @param yawOffset The yaw adjustment (in degrees) to correct for the armor stand rotation.
* @return The euler angle representing the direction
*/
private @NotNull EulerAngle convertDirectionToEulerAngle(Vector vector, double yawOffset) {
double x = vector.getX();
double z = vector.getZ();
double y = vector.getY();

double yaw = Math.atan2(-x, z);
double pitch = Math.atan2(y, Math.sqrt(x * x + z * z));

return new EulerAngle(yaw + Math.toRadians(yawOffset), 0 , pitch + Math.toRadians(90));
}

/**
* @param eulerAngle EulerAngle to convert to Direction
* @param yawOffset The yaw adjustment (in degrees) to correct for the armor stand rotation.
* @return The direction (from normalized vector) representing the EulerAngle
*/
private @NotNull Direction convertEulerAngleToDirection(EulerAngle eulerAngle, double yawOffset) {
double yaw = eulerAngle.getX();
double pitch = eulerAngle.getZ();

//Remove offset
yaw -= Math.toRadians(yawOffset);
pitch -= Math.toRadians(90);

double x = -Math.sin(yaw) * Math.cos(pitch);
double y = Math.sin(pitch);
double z = Math.cos(yaw) * Math.cos(pitch);

return new Direction(new Vector(x, y, z));
}

/**
* @param armorStand The armor stand to get the yaw offset from
* @return The yaw adjustment (in degrees) to correct for the armor stand rotation.
*/
private double getYawOffset(ArmorStand armorStand) {
double yawOffset = Math.round(armorStand.getLocation().getYaw() / 45) * 45;
boolean invert = yawOffset < 135;
if(yawOffset > 135)
yawOffset -= 90;
else if(yawOffset < 135)
yawOffset += 90;
return invert ? -yawOffset : yawOffset;
}

private enum ArmorStandPose {
LEFT_ARM(0), RIGHT_ARM(1), LEFT_LEG(2), RIGHT_LEG(3), BODY(4), HEAD(5);

private final int id;

ArmorStandPose(int id) {
this.id = id;
}

public static @NotNull ArmorStandPose getById(int id) {
for (ArmorStandPose pose : values()) {
if (pose.id == id) {
return pose;
}
}
//Default to left arm if no valid id is found
return ArmorStandPose.LEFT_ARM;
}
}

@Override
public String toString(@Nullable Event event, boolean debug) {
return "armor stand pose expression: " + pose + " of " + entity.toString(event, debug);
}

}
93 changes: 93 additions & 0 deletions src/test/skript/tests/syntaxes/expressions/ExprArmorstandPose.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
test "armorstand pose":
summon armor stand at spawn of world "world":
set {_armorstand} to event-armor stand

set {_dir} to east
set left arm pose of {_armorstand} to {_dir}
set {_dir2} to left arm pose of {_armorstand}

assert normalized vector from {_dir} is vector from {_dir2} with "Armor stand left arm pose doesn't match (set with direction))"

set {_dir} to vector 1,0,1
set right arm pose of {_armorstand} to {_dir}
set {_dir2} to right arm pose of {_armorstand}

assert normalized {_dir} is vector from {_dir2} with "Armor stand right arm pose doesn't match (set with vector)"

set {_dir} to west
set left leg pose of {_armorstand} to {_dir}
set {_dir2} to left leg pose of {_armorstand}

assert normalized vector from {_dir} is vector from {_dir2} with "Armor stand left leg pose doesn't match (set with direction))"

set {_dir} to north west
set right leg pose of {_armorstand} to {_dir}
set {_dir2} to right leg pose of {_armorstand}

assert normalized vector from {_dir} is vector from {_dir2} with "Armor stand right leg pose doesn't match (set with direction))"

set {_dir} to south east
set body pose of {_armorstand} to {_dir}
set {_dir2} to body pose of {_armorstand}

assert normalized vector from {_dir} is vector from {_dir2} with "Armor stand body pose doesn't match (set with direction))"

set {_dir} to up
set head pose of {_armorstand} to {_dir}
set {_dir2} to head pose of {_armorstand}

assert normalized vector from {_dir} is vector from {_dir2} with "Armor stand head pose doesn't match (set with direction))"

kill {_armorstand}
































summon armor stand at spawn of world "world":
set {_armorstand} to event-armor stand

set {_dir} to east
set left arm pose of {_armorstand} to {_dir}
set {_dir2} to left arm pose of {_armorstand}

assert normalized vector from {_dir} is vector from {_dir2} with "Armor stand left arm pose doesn't match (set with direction))"

set {_dir} to vector 1,0,1
set right arm pose of {_armorstand} to {_dir}
set {_dir2} to right arm pose of {_armorstand}

# clean up first in case assert fails
kill {_armorstand}

assert normalized {_dir} is vector from {_dir2} with "Armor stand right arm pose doesn't match (set with vector)"



0 comments on commit 3ddd639

Please sign in to comment.